[Feat] Система лежания и снаряды теперь проходят через лежащих людей, если не целиться в них (#195)
* add: система лежания и вставания * add: теперь по-умолчанию по лежачим не попадают снаряды * add: только гуманоиды могут ложиться
This commit is contained in:
@@ -76,6 +76,7 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.Arcade1);
|
||||
human.AddFunction(ContentKeyFunctions.Arcade2);
|
||||
human.AddFunction(ContentKeyFunctions.Arcade3);
|
||||
human.AddFunction(ContentKeyFunctions.LieDown); // WD EDIT
|
||||
|
||||
// actions should be common (for ghosts, mobs, etc)
|
||||
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
|
||||
|
||||
@@ -183,6 +183,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(ContentKeyFunctions.SwapHands);
|
||||
AddButton(ContentKeyFunctions.MoveStoredItem);
|
||||
AddButton(ContentKeyFunctions.RotateStoredItem);
|
||||
AddButton(ContentKeyFunctions.LieDown); // WD EDIT
|
||||
|
||||
AddHeader("ui-options-header-interaction-adv");
|
||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Items;
|
||||
using Content.Client.Weapons.Ranged.Components;
|
||||
using Content.Shared.Camera;
|
||||
@@ -12,13 +13,13 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Spawners;
|
||||
using Robust.Shared.Utility;
|
||||
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
|
||||
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
||||
|
||||
namespace Content.Client.Weapons.Ranged.Systems;
|
||||
|
||||
@@ -31,6 +32,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly IStateManager _state = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public const string HitscanProto = "HitscanEffect";
|
||||
@@ -184,9 +186,15 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
// WD EDIT END
|
||||
|
||||
Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}");
|
||||
//Set the target entity that is directly clicked on.
|
||||
EntityUid? shootingTarget = null;
|
||||
|
||||
if (_state.CurrentState is GameplayStateBase screen)
|
||||
shootingTarget = screen.GetClickedEntity(mousePos);
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new RequestShootEvent
|
||||
{
|
||||
Target = GetNetEntity(shootingTarget),
|
||||
Coordinates = GetNetCoordinates(coordinates),
|
||||
Gun = GetNetEntity(gunUid),
|
||||
});
|
||||
|
||||
@@ -42,9 +42,19 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
||||
// This is so entities that shouldn't get a collision are ignored.
|
||||
if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard
|
||||
|| component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var target = args.OtherEntity;
|
||||
|
||||
var collideAttemptEv = new ProjectileCollideAttemptEvent(uid, component, false);
|
||||
RaiseLocalEvent(target, ref collideAttemptEv);
|
||||
if (collideAttemptEv.Cancelled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// it's here so this check is only done once before possible hit
|
||||
var attemptEv = new ProjectileReflectAttemptEvent(uid, component, false);
|
||||
RaiseLocalEvent(target, ref attemptEv);
|
||||
|
||||
@@ -215,14 +215,31 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask);
|
||||
var rayCastResults =
|
||||
Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList();
|
||||
if (!rayCastResults.Any())
|
||||
if (rayCastResults.Count == 0)
|
||||
break;
|
||||
|
||||
var result = rayCastResults[0];
|
||||
var hit = result.HitEntity;
|
||||
lastHit = hit;
|
||||
RayCastResults? result = null;
|
||||
foreach (var castResults in rayCastResults)
|
||||
{
|
||||
var hitscanAttemptEv = new HitscanHitAttemptEvent(castResults.HitEntity, gun.Target);
|
||||
RaiseLocalEvent(castResults.HitEntity, ref hitscanAttemptEv);
|
||||
if (hitscanAttemptEv.Cancelled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FireEffects(fromEffect, result.Distance, dir.Normalized().ToAngle(), hitscan, hit);
|
||||
result = castResults;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hit = result.Value.HitEntity;
|
||||
lastHit = hit;
|
||||
FireEffects(fromEffect, result.Value.Distance, dir.Normalized().ToAngle(), hitscan, hit);
|
||||
|
||||
var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false);
|
||||
RaiseLocalEvent(hit, ref ev);
|
||||
@@ -296,25 +313,30 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user)
|
||||
{
|
||||
// Do a throw
|
||||
if (!HasComp<ProjectileComponent>(uid))
|
||||
if (TryComp(uid, out ProjectileComponent? projectile))
|
||||
{
|
||||
RemoveShootable(uid);
|
||||
// TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack.
|
||||
// WD EDIT START
|
||||
var coefficient = _powered.GetPowerCoefficient(gunUid);
|
||||
if (gun.ForceThrowingAngle)
|
||||
{
|
||||
var angle = EnsureComp<ThrowingAngleComponent>(uid);
|
||||
angle.Angle = gun.Angle;
|
||||
}
|
||||
ThrowingSystem.TryThrow(uid, mapDirection.Normalized() * 7f * coefficient, gun.ProjectileSpeedModified, user);
|
||||
if (gun.ForceThrowingAngle)
|
||||
RemComp<ThrowingAngleComponent>(uid);
|
||||
// WD EDIT END
|
||||
projectile.Target = gun.Target;
|
||||
ShootProjectile(uid, mapDirection, gunVelocity, gunUid, user, gun.ProjectileSpeedModified);
|
||||
return;
|
||||
}
|
||||
|
||||
ShootProjectile(uid, mapDirection, gunVelocity, gunUid, user, gun.ProjectileSpeedModified);
|
||||
RemoveShootable(uid);
|
||||
// TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack.
|
||||
// WD EDIT START
|
||||
var coefficient = _powered.GetPowerCoefficient(gunUid);
|
||||
if (gun.ForceThrowingAngle)
|
||||
{
|
||||
var angle = EnsureComp<ThrowingAngleComponent>(uid);
|
||||
angle.Angle = gun.Angle;
|
||||
}
|
||||
|
||||
ThrowingSystem.TryThrow(uid, mapDirection.Normalized() * 7f * coefficient, gun.ProjectileSpeedModified,
|
||||
user);
|
||||
|
||||
if (gun.ForceThrowingAngle)
|
||||
RemComp<ThrowingAngleComponent>(uid);
|
||||
|
||||
// WD EDIT END
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
|
||||
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
|
||||
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
|
||||
public static readonly BoundKeyFunction LieDown = "LieDown"; // WD EDIT
|
||||
|
||||
public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
|
||||
public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
|
||||
|
||||
@@ -26,6 +26,12 @@ public sealed partial class ProjectileComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? Weapon;
|
||||
|
||||
/// <summary>
|
||||
/// Entity being targeted by crosshair.
|
||||
/// </summary>
|
||||
[AutoNetworkedField]
|
||||
public EntityUid? Target;
|
||||
|
||||
/// <summary>
|
||||
/// The projectile spawns inside the shooter most of the time, this prevents entities from shooting themselves.
|
||||
/// </summary>
|
||||
|
||||
@@ -334,6 +334,12 @@ public sealed class ImpactEffectEvent : EntityEventArgs
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is just about to be hit with a projectile
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct ProjectileCollideAttemptEvent(EntityUid ProjUid, ProjectileComponent Component, bool Cancelled);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is just about to be hit with a projectile but can reflect it
|
||||
/// </summary>
|
||||
@@ -350,4 +356,4 @@ public record struct ProjectileHitEvent(DamageSpecifier Damage, EntityUid Target
|
||||
/// Raised after a projectile has dealt it's damage.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture);
|
||||
public record struct AfterProjectileHitEvent(DamageSpecifier Damage, EntityUid Target, Fixture Fixture);
|
||||
@@ -1,24 +1,47 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Standing
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(StandingStateSystem))]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(StandingStateSystem))]
|
||||
public sealed partial class StandingStateComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||
public SoundSpecifier DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall");
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Standing { get; set; } = true;
|
||||
public StandingState CurrentState { get; set; } = StandingState.Standing; // WD EDIT
|
||||
|
||||
/// <summary>
|
||||
/// Time required to get up.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public TimeSpan StandingUpTime { get; set; } = TimeSpan.FromSeconds(1); // WD EDIT
|
||||
|
||||
// WD EDIT
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanLieDown = false;
|
||||
|
||||
/// <summary>
|
||||
/// List of fixtures that had their collision mask changed when the entity was downed.
|
||||
/// Required for re-adding the collision mask.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<string> ChangedFixtures = new();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ChangeStandingStateEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
// WD EDIT
|
||||
public enum StandingState
|
||||
{
|
||||
Lying,
|
||||
GettingUp,
|
||||
Standing
|
||||
}
|
||||
@@ -1,166 +1,324 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Rotation;
|
||||
using Robust.Shared.Audio;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Standing
|
||||
namespace Content.Shared.Standing;
|
||||
|
||||
public sealed class StandingStateSystem : EntitySystem
|
||||
{
|
||||
public sealed class StandingStateSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!; // WD EDIT
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!; // WD EDIT
|
||||
|
||||
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
|
||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
||||
|
||||
// WD EDIT START
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
base.Initialize();
|
||||
|
||||
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
|
||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
||||
SubscribeLocalEvent<StandingStateComponent, StandingUpDoAfterEvent>(OnStandingUpDoAfter);
|
||||
SubscribeLocalEvent<StandingStateComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeed);
|
||||
SubscribeLocalEvent<StandingStateComponent, ProjectileCollideAttemptEvent>(OnProjectileCollideAttempt);
|
||||
SubscribeLocalEvent<StandingStateComponent, HitscanHitAttemptEvent>(OnHitscanHitAttempt);
|
||||
|
||||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
SubscribeNetworkEvent<ChangeStandingStateEvent>(OnChangeState);
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.LieDown, InputCmdHandler.FromDelegate(ChangeLyingState))
|
||||
.Register<StandingStateSystem>();
|
||||
}
|
||||
|
||||
|
||||
private void OnRefreshMovementSpeed(
|
||||
EntityUid uid,
|
||||
StandingStateComponent component,
|
||||
RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
if (IsDown(uid))
|
||||
args.ModifySpeed(0.4f, 0.4f);
|
||||
else
|
||||
args.ModifySpeed(1f, 1f);
|
||||
}
|
||||
|
||||
private void OnProjectileCollideAttempt(EntityUid uid, StandingStateComponent component, ref ProjectileCollideAttemptEvent args)
|
||||
{
|
||||
if (component.CurrentState is StandingState.Standing)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Component.Target.HasValue || args.Component.Target != uid)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHitscanHitAttempt(EntityUid uid, StandingStateComponent component, ref HitscanHitAttemptEvent args)
|
||||
{
|
||||
if (component.CurrentState is StandingState.Standing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Target.HasValue || args.Target != uid)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !standingState.Standing;
|
||||
private void OnStandingUpDoAfter(EntityUid uid, StandingStateComponent component, StandingUpDoAfterEvent args)
|
||||
{
|
||||
Stand(uid);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
private void OnChangeState(ChangeStandingStateEvent ev, EntitySessionEventArgs args)
|
||||
{
|
||||
if (!args.SenderSession.AttachedEntity.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
HandsComponent? hands = null)
|
||||
var uid = args.SenderSession.AttachedEntity.Value;
|
||||
|
||||
if (IsDown(uid))
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
TryStandUp(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
TryLieDown(uid);
|
||||
}
|
||||
}
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, ref hands, false);
|
||||
/// <summary>
|
||||
/// Send an update event when player pressed keybind.
|
||||
/// </summary>
|
||||
private void ChangeLyingState(ICommonSession? session)
|
||||
{
|
||||
if (session?.AttachedEntity == null ||
|
||||
!TryComp(session.AttachedEntity, out StandingStateComponent? standing) ||
|
||||
!standing.CanLieDown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!standingState.Standing)
|
||||
return true;
|
||||
RaiseNetworkEvent(new ChangeStandingStateEvent());
|
||||
}
|
||||
|
||||
public bool TryStandUp(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// This is just to avoid most callers doing this manually saving boilerplate
|
||||
// 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to.
|
||||
// We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway
|
||||
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
|
||||
if (dropHeldItems && hands != null)
|
||||
if (standingState.CurrentState is not StandingState.Lying)
|
||||
return false;
|
||||
|
||||
standingState.CurrentState = StandingState.GettingUp;
|
||||
var doargs = new DoAfterArgs(EntityManager, uid, standingState.StandingUpTime,
|
||||
new StandingUpDoAfterEvent(), uid)
|
||||
{
|
||||
BreakOnUserMove = false,
|
||||
BreakOnDamage = false,
|
||||
BreakOnHandChange = false
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doargs);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryLieDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
if (standingState.CurrentState is not StandingState.Standing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Down(uid, true, false, standingState);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// WD EDIT END
|
||||
|
||||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
return standingState.CurrentState is StandingState.Lying or StandingState.GettingUp;
|
||||
}
|
||||
|
||||
public bool Down(
|
||||
EntityUid uid,
|
||||
bool playSound = true,
|
||||
bool dropHeldItems = true,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
HandsComponent? hands = null)
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, ref hands, false);
|
||||
|
||||
if (standingState.CurrentState is StandingState.Lying or StandingState.GettingUp)
|
||||
return true;
|
||||
|
||||
// This is just to avoid most callers doing this manually saving boilerplate
|
||||
// 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to.
|
||||
// We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway
|
||||
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
|
||||
if (dropHeldItems && hands != null)
|
||||
{
|
||||
RaiseLocalEvent(uid, new DropHandItemsEvent());
|
||||
}
|
||||
|
||||
var msg = new DownAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
|
||||
standingState.CurrentState = StandingState.Lying;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new DownedEvent());
|
||||
|
||||
// Seemed like the best place to put it
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
|
||||
|
||||
// Change collision masks to allow going under certain entities like flaps and tables
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var (key, fixture) in fixtureComponent.Fixtures)
|
||||
{
|
||||
RaiseLocalEvent(uid, new DropHandItemsEvent(), false);
|
||||
}
|
||||
if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
|
||||
continue;
|
||||
|
||||
var msg = new DownAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg, false);
|
||||
standingState.ChangedFixtures.Add(key);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer,
|
||||
manager: fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// check if component was just added or streamed to client
|
||||
// if true, no need to play sound - mob was down before player could seen that
|
||||
if (standingState.LifeStage <= ComponentLifeStage.Starting)
|
||||
return true;
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
_audio.PlayPredicted(standingState.DownSound, uid, uid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Stand(
|
||||
EntityUid uid,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
bool force = false)
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, false);
|
||||
|
||||
if (standingState.CurrentState is StandingState.Standing)
|
||||
return true;
|
||||
|
||||
if (!force)
|
||||
{
|
||||
var msg = new StandAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
|
||||
standingState.Standing = false;
|
||||
Dirty(standingState);
|
||||
RaiseLocalEvent(uid, new DownedEvent(), false);
|
||||
|
||||
// Seemed like the best place to put it
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
|
||||
|
||||
// Change collision masks to allow going under certain entities like flaps and tables
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var (key, fixture) in fixtureComponent.Fixtures)
|
||||
{
|
||||
if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
|
||||
continue;
|
||||
|
||||
standingState.ChangedFixtures.Add(key);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// check if component was just added or streamed to client
|
||||
// if true, no need to play sound - mob was down before player could seen that
|
||||
if (standingState.LifeStage <= ComponentLifeStage.Starting)
|
||||
return true;
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
_audio.PlayPredicted(standingState.DownSound, uid, uid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Stand(EntityUid uid,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
bool force = false)
|
||||
standingState.CurrentState = StandingState.Standing;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new StoodEvent());
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, false);
|
||||
|
||||
if (standingState.Standing)
|
||||
return true;
|
||||
|
||||
if (!force)
|
||||
foreach (var key in standingState.ChangedFixtures)
|
||||
{
|
||||
var msg = new StandAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg, false);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
standingState.Standing = true;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new StoodEvent(), false);
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var key in standingState.ChangedFixtures)
|
||||
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||
{
|
||||
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer,
|
||||
fixtureComponent);
|
||||
}
|
||||
}
|
||||
standingState.ChangedFixtures.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DropHandItemsEvent : EventArgs
|
||||
{
|
||||
}
|
||||
standingState.ChangedFixtures.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a down attempt.
|
||||
/// </summary>
|
||||
public sealed class DownAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a stand attempt.
|
||||
/// </summary>
|
||||
public sealed class StandAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity becomes standing
|
||||
/// </summary>
|
||||
public sealed class StoodEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is not standing
|
||||
/// </summary>
|
||||
public sealed class DownedEvent : EntityEventArgs
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DropHandItemsEvent : EventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a down attempt.
|
||||
/// </summary>
|
||||
public sealed class DownAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a stand attempt.
|
||||
/// </summary>
|
||||
public sealed class StandAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity becomes standing
|
||||
/// </summary>
|
||||
public sealed class StoodEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is not standing
|
||||
/// </summary>
|
||||
public sealed class DownedEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
// WD EDIT
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class StandingUpDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
@@ -229,6 +229,9 @@ public sealed partial class GunComponent : Component
|
||||
public bool ClumsyProof = false;
|
||||
|
||||
// WD START
|
||||
|
||||
public EntityUid? Target;
|
||||
|
||||
[DataField("forceThrowingAngle")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ForceThrowingAngle;
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Content.Shared.Weapons.Ranged.Events;
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RequestShootEvent : EntityEventArgs
|
||||
{
|
||||
public NetEntity? Target;
|
||||
public NetEntity Gun;
|
||||
public NetCoordinates Coordinates;
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
return;
|
||||
|
||||
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
||||
gun.Target = GetEntity(msg.Target);
|
||||
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
||||
AttemptShoot(user.Value, ent, gun);
|
||||
}
|
||||
@@ -558,6 +559,12 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
[ByRefEvent]
|
||||
public record struct AttemptShootEvent(EntityUid User, string? Message, bool Cancelled = false, bool ThrowItems = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is just about to be hit with a hitscan
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct HitscanHitAttemptEvent(EntityUid HitEntity, EntityUid? Target, bool Cancelled = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on the gun after firing.
|
||||
/// </summary>
|
||||
|
||||
@@ -134,6 +134,7 @@ ui-options-function-examine-entity = Examine
|
||||
ui-options-function-swap-hands = Swap hands
|
||||
ui-options-function-move-stored-item = Move stored item
|
||||
ui-options-function-rotate-stored-item = Rotate stored item
|
||||
ui-options-function-lie-down = Lie down/Get up
|
||||
ui-options-static-storage-ui = Lock storage window to hotbar
|
||||
|
||||
ui-options-function-smart-equip-backpack = Smart-equip to backpack
|
||||
|
||||
@@ -136,6 +136,7 @@ ui-options-function-examine-entity = Изучить
|
||||
ui-options-function-swap-hands = Поменять руки
|
||||
ui-options-function-move-stored-item = Переместить хранящийся объект
|
||||
ui-options-function-rotate-stored-item = Повернуть хранящийся объект
|
||||
ui-options-function-lie-down = Лечь/встать
|
||||
ui-options-static-storage-ui = Закрепить интерфейс хранилища на хотбаре
|
||||
|
||||
ui-options-function-smart-equip-backpack = Умная экипировка в рюкзак
|
||||
|
||||
@@ -177,6 +177,7 @@
|
||||
- type: SleepEmitSound
|
||||
- type: SSDIndicator
|
||||
- type: StandingState
|
||||
canLieDown: true
|
||||
- type: Fingerprint
|
||||
- type: Dna
|
||||
- type: MindContainer
|
||||
|
||||
@@ -527,3 +527,6 @@ binds:
|
||||
- function: Hotbar9
|
||||
type: State
|
||||
key: Num9
|
||||
- function: LieDown
|
||||
type: State
|
||||
key: U
|
||||
Reference in New Issue
Block a user