[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.Arcade1);
|
||||||
human.AddFunction(ContentKeyFunctions.Arcade2);
|
human.AddFunction(ContentKeyFunctions.Arcade2);
|
||||||
human.AddFunction(ContentKeyFunctions.Arcade3);
|
human.AddFunction(ContentKeyFunctions.Arcade3);
|
||||||
|
human.AddFunction(ContentKeyFunctions.LieDown); // WD EDIT
|
||||||
|
|
||||||
// actions should be common (for ghosts, mobs, etc)
|
// actions should be common (for ghosts, mobs, etc)
|
||||||
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
|
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
AddButton(ContentKeyFunctions.SwapHands);
|
AddButton(ContentKeyFunctions.SwapHands);
|
||||||
AddButton(ContentKeyFunctions.MoveStoredItem);
|
AddButton(ContentKeyFunctions.MoveStoredItem);
|
||||||
AddButton(ContentKeyFunctions.RotateStoredItem);
|
AddButton(ContentKeyFunctions.RotateStoredItem);
|
||||||
|
AddButton(ContentKeyFunctions.LieDown); // WD EDIT
|
||||||
|
|
||||||
AddHeader("ui-options-header-interaction-adv");
|
AddHeader("ui-options-header-interaction-adv");
|
||||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Client.Gameplay;
|
||||||
using Content.Client.Items;
|
using Content.Client.Items;
|
||||||
using Content.Client.Weapons.Ranged.Components;
|
using Content.Client.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
@@ -12,13 +13,13 @@ using Robust.Client.GameObjects;
|
|||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Input;
|
using Robust.Client.Input;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.State;
|
||||||
using Robust.Shared.Animations;
|
using Robust.Shared.Animations;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Spawners;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
|
|
||||||
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Weapons.Ranged.Systems;
|
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 InputSystem _inputSystem = default!;
|
||||||
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
||||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||||
|
[Dependency] private readonly IStateManager _state = default!;
|
||||||
|
|
||||||
[ValidatePrototypeId<EntityPrototype>]
|
[ValidatePrototypeId<EntityPrototype>]
|
||||||
public const string HitscanProto = "HitscanEffect";
|
public const string HitscanProto = "HitscanEffect";
|
||||||
@@ -184,9 +186,15 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
// WD EDIT END
|
// WD EDIT END
|
||||||
|
|
||||||
Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}");
|
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
|
EntityManager.RaisePredictiveEvent(new RequestShootEvent
|
||||||
{
|
{
|
||||||
|
Target = GetNetEntity(shootingTarget),
|
||||||
Coordinates = GetNetCoordinates(coordinates),
|
Coordinates = GetNetCoordinates(coordinates),
|
||||||
Gun = GetNetEntity(gunUid),
|
Gun = GetNetEntity(gunUid),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,9 +42,19 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
|||||||
// This is so entities that shouldn't get a collision are ignored.
|
// This is so entities that shouldn't get a collision are ignored.
|
||||||
if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard
|
if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard
|
||||||
|| component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true })
|
|| component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true })
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var target = args.OtherEntity;
|
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
|
// it's here so this check is only done once before possible hit
|
||||||
var attemptEv = new ProjectileReflectAttemptEvent(uid, component, false);
|
var attemptEv = new ProjectileReflectAttemptEvent(uid, component, false);
|
||||||
RaiseLocalEvent(target, ref attemptEv);
|
RaiseLocalEvent(target, ref attemptEv);
|
||||||
|
|||||||
@@ -215,14 +215,31 @@ public sealed partial class GunSystem : SharedGunSystem
|
|||||||
var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask);
|
var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask);
|
||||||
var rayCastResults =
|
var rayCastResults =
|
||||||
Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList();
|
Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList();
|
||||||
if (!rayCastResults.Any())
|
if (rayCastResults.Count == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
var result = rayCastResults[0];
|
RayCastResults? result = null;
|
||||||
var hit = result.HitEntity;
|
foreach (var castResults in rayCastResults)
|
||||||
lastHit = hit;
|
{
|
||||||
|
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);
|
var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false);
|
||||||
RaiseLocalEvent(hit, ref ev);
|
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)
|
private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user)
|
||||||
{
|
{
|
||||||
// Do a throw
|
// Do a throw
|
||||||
if (!HasComp<ProjectileComponent>(uid))
|
if (TryComp(uid, out ProjectileComponent? projectile))
|
||||||
{
|
{
|
||||||
RemoveShootable(uid);
|
projectile.Target = gun.Target;
|
||||||
// TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack.
|
ShootProjectile(uid, mapDirection, gunVelocity, gunUid, user, gun.ProjectileSpeedModified);
|
||||||
// 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
|
|
||||||
return;
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ namespace Content.Shared.Input
|
|||||||
public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
|
public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
|
||||||
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
|
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
|
||||||
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
|
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
|
||||||
|
public static readonly BoundKeyFunction LieDown = "LieDown"; // WD EDIT
|
||||||
|
|
||||||
public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
|
public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
|
||||||
public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
|
public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ public sealed partial class ProjectileComponent : Component
|
|||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? Weapon;
|
public EntityUid? Weapon;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity being targeted by crosshair.
|
||||||
|
/// </summary>
|
||||||
|
[AutoNetworkedField]
|
||||||
|
public EntityUid? Target;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The projectile spawns inside the shooter most of the time, this prevents entities from shooting themselves.
|
/// The projectile spawns inside the shooter most of the time, this prevents entities from shooting themselves.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Raised when an entity is just about to be hit with a projectile but can reflect it
|
/// Raised when an entity is just about to be hit with a projectile but can reflect it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Standing
|
namespace Content.Shared.Standing
|
||||||
{
|
{
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(StandingStateSystem))]
|
||||||
[Access(typeof(StandingStateSystem))]
|
|
||||||
public sealed partial class StandingStateComponent : Component
|
public sealed partial class StandingStateComponent : Component
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||||
[DataField]
|
|
||||||
public SoundSpecifier DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall");
|
public SoundSpecifier DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall");
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
[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>
|
/// <summary>
|
||||||
/// List of fixtures that had their collision mask changed when the entity was downed.
|
/// List of fixtures that had their collision mask changed when the entity was downed.
|
||||||
@@ -20,5 +29,19 @@ namespace Content.Shared.Standing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public List<string> ChangedFixtures = new();
|
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.Hands.Components;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared.Rotation;
|
using Content.Shared.Rotation;
|
||||||
using Robust.Shared.Audio;
|
using Content.Shared.Weapons.Ranged.Systems;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Systems;
|
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!;
|
base.Initialize();
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
|
||||||
|
|
||||||
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
|
SubscribeLocalEvent<StandingStateComponent, StandingUpDoAfterEvent>(OnStandingUpDoAfter);
|
||||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
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;
|
||||||
return false;
|
|
||||||
|
|
||||||
return !standingState.Standing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true,
|
if (!args.Component.Target.HasValue || args.Component.Target != uid)
|
||||||
StandingStateComponent? standingState = null,
|
|
||||||
AppearanceComponent? appearance = null,
|
|
||||||
HandsComponent? hands = null)
|
|
||||||
{
|
{
|
||||||
// TODO: This should actually log missing comps...
|
args.Cancelled = true;
|
||||||
if (!Resolve(uid, ref standingState, false))
|
}
|
||||||
return false;
|
}
|
||||||
|
|
||||||
// Optional component.
|
private void OnHitscanHitAttempt(EntityUid uid, StandingStateComponent component, ref HitscanHitAttemptEvent args)
|
||||||
Resolve(uid, ref appearance, ref hands, false);
|
{
|
||||||
|
if (component.CurrentState is StandingState.Standing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!standingState.Standing)
|
if (!args.Target.HasValue || args.Target != uid)
|
||||||
return true;
|
{
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is just to avoid most callers doing this manually saving boilerplate
|
private void OnStandingUpDoAfter(EntityUid uid, StandingStateComponent component, StandingUpDoAfterEvent args)
|
||||||
// 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
|
Stand(uid);
|
||||||
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
|
_movement.RefreshMovementSpeedModifiers(uid);
|
||||||
if (dropHeldItems && hands != null)
|
}
|
||||||
|
|
||||||
|
private void OnChangeState(ChangeStandingStateEvent ev, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (!args.SenderSession.AttachedEntity.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uid = args.SenderSession.AttachedEntity.Value;
|
||||||
|
|
||||||
|
if (IsDown(uid))
|
||||||
|
{
|
||||||
|
TryStandUp(uid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TryLieDown(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseNetworkEvent(new ChangeStandingStateEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryStandUp(EntityUid uid, StandingStateComponent? standingState = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref standingState, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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();
|
standingState.ChangedFixtures.Add(key);
|
||||||
RaiseLocalEvent(uid, msg, false);
|
_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)
|
if (msg.Cancelled)
|
||||||
return false;
|
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,
|
standingState.CurrentState = StandingState.Standing;
|
||||||
StandingStateComponent? standingState = null,
|
Dirty(uid, standingState);
|
||||||
AppearanceComponent? appearance = null,
|
RaiseLocalEvent(uid, new StoodEvent());
|
||||||
bool force = false)
|
|
||||||
|
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||||
|
|
||||||
|
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||||
{
|
{
|
||||||
// TODO: This should actually log missing comps...
|
foreach (var key in standingState.ChangedFixtures)
|
||||||
if (!Resolve(uid, ref standingState, false))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Optional component.
|
|
||||||
Resolve(uid, ref appearance, false);
|
|
||||||
|
|
||||||
if (standingState.Standing)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!force)
|
|
||||||
{
|
{
|
||||||
var msg = new StandAttemptEvent();
|
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||||
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))
|
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer,
|
||||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent);
|
fixtureComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
standingState.ChangedFixtures.Clear();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DropHandItemsEvent : EventArgs
|
standingState.ChangedFixtures.Clear();
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
return true;
|
||||||
/// 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
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
public bool ClumsyProof = false;
|
||||||
|
|
||||||
// WD START
|
// WD START
|
||||||
|
|
||||||
|
public EntityUid? Target;
|
||||||
|
|
||||||
[DataField("forceThrowingAngle")]
|
[DataField("forceThrowingAngle")]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool ForceThrowingAngle;
|
public bool ForceThrowingAngle;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Content.Shared.Weapons.Ranged.Events;
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class RequestShootEvent : EntityEventArgs
|
public sealed class RequestShootEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
|
public NetEntity? Target;
|
||||||
public NetEntity Gun;
|
public NetEntity Gun;
|
||||||
public NetCoordinates Coordinates;
|
public NetCoordinates Coordinates;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
|
||||||
|
gun.Target = GetEntity(msg.Target);
|
||||||
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
|
||||||
AttemptShoot(user.Value, ent, gun);
|
AttemptShoot(user.Value, ent, gun);
|
||||||
}
|
}
|
||||||
@@ -558,6 +559,12 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public record struct AttemptShootEvent(EntityUid User, string? Message, bool Cancelled = false, bool ThrowItems = false);
|
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>
|
/// <summary>
|
||||||
/// Raised directed on the gun after firing.
|
/// Raised directed on the gun after firing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ ui-options-function-examine-entity = Examine
|
|||||||
ui-options-function-swap-hands = Swap hands
|
ui-options-function-swap-hands = Swap hands
|
||||||
ui-options-function-move-stored-item = Move stored item
|
ui-options-function-move-stored-item = Move stored item
|
||||||
ui-options-function-rotate-stored-item = Rotate 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-static-storage-ui = Lock storage window to hotbar
|
||||||
|
|
||||||
ui-options-function-smart-equip-backpack = Smart-equip to backpack
|
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-swap-hands = Поменять руки
|
||||||
ui-options-function-move-stored-item = Переместить хранящийся объект
|
ui-options-function-move-stored-item = Переместить хранящийся объект
|
||||||
ui-options-function-rotate-stored-item = Повернуть хранящийся объект
|
ui-options-function-rotate-stored-item = Повернуть хранящийся объект
|
||||||
|
ui-options-function-lie-down = Лечь/встать
|
||||||
ui-options-static-storage-ui = Закрепить интерфейс хранилища на хотбаре
|
ui-options-static-storage-ui = Закрепить интерфейс хранилища на хотбаре
|
||||||
|
|
||||||
ui-options-function-smart-equip-backpack = Умная экипировка в рюкзак
|
ui-options-function-smart-equip-backpack = Умная экипировка в рюкзак
|
||||||
|
|||||||
@@ -177,6 +177,7 @@
|
|||||||
- type: SleepEmitSound
|
- type: SleepEmitSound
|
||||||
- type: SSDIndicator
|
- type: SSDIndicator
|
||||||
- type: StandingState
|
- type: StandingState
|
||||||
|
canLieDown: true
|
||||||
- type: Fingerprint
|
- type: Fingerprint
|
||||||
- type: Dna
|
- type: Dna
|
||||||
- type: MindContainer
|
- type: MindContainer
|
||||||
|
|||||||
@@ -527,3 +527,6 @@ binds:
|
|||||||
- function: Hotbar9
|
- function: Hotbar9
|
||||||
type: State
|
type: State
|
||||||
key: Num9
|
key: Num9
|
||||||
|
- function: LieDown
|
||||||
|
type: State
|
||||||
|
key: U
|
||||||
Reference in New Issue
Block a user