using System; using Content.Server.DoAfter; using Content.Server.Popups; using Content.Shared.ActionBlocker; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.Climbing; using Content.Shared.DragDrop; using Content.Shared.Interaction.Helpers; using Content.Shared.Popups; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Maths; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; namespace Content.Server.Climbing.Components { [RegisterComponent] [ComponentReference(typeof(IClimbable))] public class ClimbableComponent : SharedClimbableComponent { /// /// The time it takes to climb onto the entity. /// [ViewVariables] [DataField("delay")] private float _climbDelay = 0.8f; protected override void Initialize() { base.Initialize(); if (!Owner.EnsureComponent(out PhysicsComponent _)) { Logger.Warning($"Entity {IoCManager.Resolve().GetComponent(Owner).EntityName} at {IoCManager.Resolve().GetComponent(Owner).MapPosition} didn't have a {nameof(PhysicsComponent)}"); } } public override bool CanDragDropOn(DragDropEvent eventArgs) { if (!base.CanDragDropOn(eventArgs)) return false; string reason; bool canVault; if (eventArgs.User == eventArgs.Dragged) canVault = CanVault(eventArgs.User, eventArgs.Target, out reason); else canVault = CanVault(eventArgs.User, eventArgs.Dragged, eventArgs.Target, out reason); if (!canVault) eventArgs.User.PopupMessage(reason); return canVault; } /// /// Checks if the user can vault the target /// /// The entity that wants to vault /// The object that is being vaulted /// The reason why it cant be dropped /// private bool CanVault(EntityUid user, EntityUid target, out string reason) { if (!EntitySystem.Get().CanInteract(user)) { reason = Loc.GetString("comp-climbable-cant-interact"); return false; } if (!IoCManager.Resolve().HasComponent(user) || !IoCManager.Resolve().TryGetComponent(user, out SharedBodyComponent? body)) { reason = Loc.GetString("comp-climbable-cant-climb"); return false; } if (!body.HasPartOfType(BodyPartType.Leg) || !body.HasPartOfType(BodyPartType.Foot)) { reason = Loc.GetString("comp-climbable-cant-climb"); return false; } if (!user.InRangeUnobstructed(target, Range)) { reason = Loc.GetString("comp-climbable-cant-reach"); return false; } reason = string.Empty; return true; } /// /// Checks if the user can vault the dragged entity onto the the target /// /// The user that wants to vault the entity /// The entity that is being vaulted /// The object that is being vaulted onto /// The reason why it cant be dropped /// private bool CanVault(EntityUid user, EntityUid dragged, EntityUid target, out string reason) { if (!EntitySystem.Get().CanInteract(user)) { reason = Loc.GetString("comp-climbable-cant-interact"); return false; } if (target == null || !IoCManager.Resolve().HasComponent(dragged)) { reason = Loc.GetString("comp-climbable-cant-climb"); return false; } bool Ignored(EntityUid entity) => entity == target || entity == user || entity == dragged; if (!user.InRangeUnobstructed(target, Range, predicate: Ignored) || !user.InRangeUnobstructed(dragged, Range, predicate: Ignored)) { reason = Loc.GetString("comp-climbable-cant-reach"); return false; } reason = string.Empty; return true; } public override bool DragDropOn(DragDropEvent eventArgs) { if (eventArgs.User == eventArgs.Dragged) { TryClimb(eventArgs.User); } else { TryMoveEntity(eventArgs.User, eventArgs.Dragged); } return true; } private async void TryMoveEntity(EntityUid user, EntityUid entityToMove) { var doAfterEventArgs = new DoAfterEventArgs(user, _climbDelay, default, entityToMove) { BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true }; var result = await EntitySystem.Get().WaitDoAfter(doAfterEventArgs); if (result != DoAfterStatus.Cancelled && IoCManager.Resolve().TryGetComponent(entityToMove, out PhysicsComponent? body) && body.Fixtures.Count >= 1) { var entityPos = IoCManager.Resolve().GetComponent(entityToMove).WorldPosition; var direction = (IoCManager.Resolve().GetComponent(Owner).WorldPosition - entityPos).Normalized; var endPoint = IoCManager.Resolve().GetComponent(Owner).WorldPosition; var climbMode = IoCManager.Resolve().GetComponent(entityToMove); climbMode.IsClimbing = true; if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line { endPoint = new Vector2(entityPos.X, endPoint.Y); } else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line { endPoint = new Vector2(endPoint.X, entityPos.Y); } climbMode.TryMoveTo(entityPos, endPoint); // we may potentially need additional logic since we're forcing a player onto a climbable // there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for var othersMessage = Loc.GetString("comp-climbable-user-climbs-force-other", ("user", user), ("moved-user", entityToMove), ("climbable", Owner)); user.PopupMessageOtherClients(othersMessage); var selfMessage = Loc.GetString("comp-climbable-user-climbs-force", ("moved-user", entityToMove), ("climbable", Owner)); user.PopupMessage(selfMessage); } } public async void TryClimb(EntityUid user) { if (!IoCManager.Resolve().TryGetComponent(user, out ClimbingComponent? climbingComponent) || climbingComponent.IsClimbing) return; var doAfterEventArgs = new DoAfterEventArgs(user, _climbDelay, default, Owner) { BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true }; var result = await EntitySystem.Get().WaitDoAfter(doAfterEventArgs); if (result != DoAfterStatus.Cancelled && IoCManager.Resolve().TryGetComponent(user, out PhysicsComponent? body) && body.Fixtures.Count >= 1) { // TODO: Remove the copy-paste code var userPos = IoCManager.Resolve().GetComponent(user).WorldPosition; var direction = (IoCManager.Resolve().GetComponent(Owner).WorldPosition - userPos).Normalized; var endPoint = IoCManager.Resolve().GetComponent(Owner).WorldPosition; var climbMode = IoCManager.Resolve().GetComponent(user); climbMode.IsClimbing = true; if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line { endPoint = new Vector2(IoCManager.Resolve().GetComponent(user).WorldPosition.X, endPoint.Y); } else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line { endPoint = new Vector2(endPoint.X, IoCManager.Resolve().GetComponent(user).WorldPosition.Y); } climbMode.TryMoveTo(userPos, endPoint); var othersMessage = Loc.GetString("comp-climbable-user-climbs-other", ("user", user), ("climbable", Owner)); user.PopupMessageOtherClients(othersMessage); var selfMessage = Loc.GetString("comp-climbable-user-climbs", ("climbable", Owner)); user.PopupMessage(selfMessage); } } } }