From 14f949c3112e903cd9adc1a8355a3aa3a9c1fcab Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:22:36 +0200 Subject: [PATCH] Jumpability collisions (#39710) --- .../Components/ActiveLeaperComponent.cs | 16 ++++ .../Components/JumpAbilityComponent.cs | 31 ++++++++ .../Systems/SharedJumpAbilitySystem.cs | 78 ++++++++++++++++++- .../en-US/jump-ability/jump-ability.ftl | 1 + .../Prototypes/Entities/Mobs/Player/clone.yml | 1 + 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 Content.Shared/Movement/Components/ActiveLeaperComponent.cs create mode 100644 Resources/Locale/en-US/jump-ability/jump-ability.ftl diff --git a/Content.Shared/Movement/Components/ActiveLeaperComponent.cs b/Content.Shared/Movement/Components/ActiveLeaperComponent.cs new file mode 100644 index 0000000000..cb5a237af4 --- /dev/null +++ b/Content.Shared/Movement/Components/ActiveLeaperComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +/// +/// Marker component given to the users of the if they are meant to collide with environment. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ActiveLeaperComponent : Component +{ + /// + /// The duration to stun the owner on collide with environment. + /// + [DataField, AutoNetworkedField] + public TimeSpan KnockdownDuration; +} diff --git a/Content.Shared/Movement/Components/JumpAbilityComponent.cs b/Content.Shared/Movement/Components/JumpAbilityComponent.cs index 6da9161578..a6e33d5b87 100644 --- a/Content.Shared/Movement/Components/JumpAbilityComponent.cs +++ b/Content.Shared/Movement/Components/JumpAbilityComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.Actions; using Content.Shared.Movement.Systems; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.Movement.Components; @@ -13,6 +14,18 @@ namespace Content.Shared.Movement.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedJumpAbilitySystem))] public sealed partial class JumpAbilityComponent : Component { + /// + /// The action prototype that allows you to jump. + /// + [DataField] + public EntProtoId Action = "ActionGravityJump"; + + /// + /// Entity to hold the action prototype. + /// + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + /// /// How far you will jump (in tiles). /// @@ -25,11 +38,29 @@ public sealed partial class JumpAbilityComponent : Component [DataField, AutoNetworkedField] public float JumpThrowSpeed = 10f; + /// + /// Whether this entity can collide with another entity, leading to it getting knocked down. + /// + [DataField, AutoNetworkedField] + public bool CanCollide = false; + + /// + /// The duration of the knockdown in case of a collision from CanCollide. + /// + [DataField, AutoNetworkedField] + public TimeSpan CollideKnockdown = TimeSpan.FromSeconds(2); + /// /// This gets played whenever the jump action is used. /// [DataField, AutoNetworkedField] public SoundSpecifier? JumpSound; + + /// + /// The popup to show if the entity is unable to perform a jump. + /// + [DataField, AutoNetworkedField] + public LocId? JumpFailedPopup = "jump-ability-failure"; } public sealed partial class GravityJumpEvent : InstantActionEvent; diff --git a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs index ac720cae68..598e4b564a 100644 --- a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs +++ b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs @@ -1,7 +1,14 @@ +using Content.Shared.Actions; +using Content.Shared.Actions.Components; +using Content.Shared.Cloning.Events; using Content.Shared.Gravity; using Content.Shared.Movement.Components; +using Content.Shared.Popups; +using Content.Shared.Standing; +using Content.Shared.Stunnable; using Content.Shared.Throwing; using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics.Events; namespace Content.Shared.Movement.Systems; @@ -10,18 +17,64 @@ public sealed partial class SharedJumpAbilitySystem : EntitySystem [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGravityJump); + + SubscribeLocalEvent(OnLeaperCollide); + SubscribeLocalEvent(OnLeaperLand); + SubscribeLocalEvent(OnLeaperStopThrow); + + SubscribeLocalEvent(OnClone); + } + + private void OnInit(Entity entity, ref MapInitEvent args) + { + if (!TryComp(entity, out ActionsComponent? comp)) + return; + + _actions.AddAction(entity, ref entity.Comp.ActionEntity, entity.Comp.Action, component: comp); + } + + private void OnShutdown(Entity entity, ref ComponentShutdown args) + { + _actions.RemoveAction(entity.Owner, entity.Comp.ActionEntity); + } + + private void OnLeaperCollide(Entity entity, ref StartCollideEvent args) + { + _stun.TryKnockdown(entity.Owner, entity.Comp.KnockdownDuration, force: true); + RemCompDeferred(entity); + } + + private void OnLeaperLand(Entity entity, ref LandEvent args) + { + RemCompDeferred(entity); + } + + private void OnLeaperStopThrow(Entity entity, ref StopThrowEvent args) + { + RemCompDeferred(entity); } private void OnGravityJump(Entity entity, ref GravityJumpEvent args) { - if (_gravity.IsWeightless(args.Performer)) + if (_gravity.IsWeightless(args.Performer) || _standing.IsDown(args.Performer)) + { + if (entity.Comp.JumpFailedPopup != null) + _popup.PopupClient(Loc.GetString(entity.Comp.JumpFailedPopup.Value), args.Performer, args.Performer); return; + } var xform = Transform(args.Performer); var throwing = xform.LocalRotation.ToWorldVec() * entity.Comp.JumpDistance; @@ -30,6 +83,29 @@ public sealed partial class SharedJumpAbilitySystem : EntitySystem _throwing.TryThrow(args.Performer, direction, entity.Comp.JumpThrowSpeed); _audio.PlayPredicted(entity.Comp.JumpSound, args.Performer, args.Performer); + + if (entity.Comp.CanCollide) + { + EnsureComp(entity, out var leaperComp); + leaperComp.KnockdownDuration = entity.Comp.CollideKnockdown; + Dirty(entity.Owner, leaperComp); + } + args.Handled = true; } + + private void OnClone(Entity ent, ref CloningEvent args) + { + if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) + return; + + var targetComp = Factory.GetComponent(); + targetComp.Action = ent.Comp.Action; + targetComp.CanCollide = ent.Comp.CanCollide; + targetComp.JumpSound = ent.Comp.JumpSound; + targetComp.CollideKnockdown = ent.Comp.CollideKnockdown; + targetComp.JumpDistance = ent.Comp.JumpDistance; + targetComp.JumpThrowSpeed = ent.Comp.JumpThrowSpeed; + AddComp(args.CloneUid, targetComp, true); + } } diff --git a/Resources/Locale/en-US/jump-ability/jump-ability.ftl b/Resources/Locale/en-US/jump-ability/jump-ability.ftl new file mode 100644 index 0000000000..a9737455a3 --- /dev/null +++ b/Resources/Locale/en-US/jump-ability/jump-ability.ftl @@ -0,0 +1 @@ +jump-ability-failure = You cannot jump right now. diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 907b694db9..43174cfffe 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -121,6 +121,7 @@ - Rootable # diona - Sericulture # arachnids - MovementSpeedModifier # moths when weightless + - JumpAbility # vulp leaping copyEquipment: null copyInternalStorage: false copyImplants: false