#nullable enable using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Interactable; using Content.Server.GameObjects.Components.Pulling; using Content.Server.Utility; using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Physics; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components { [RegisterComponent] public class AnchorableComponent : Component, IInteractUsing { public override string Name => "Anchorable"; [ComponentDependency] private PhysicsComponent? _physicsComponent = default!; [ViewVariables] [DataField("tool")] public ToolQuality Tool { get; private set; } = ToolQuality.Anchoring; [ViewVariables] int IInteractUsing.Priority => 1; [ViewVariables(VVAccess.ReadWrite)] [DataField("snap")] public bool Snap { get; private set; } = true; public override void Initialize() { base.Initialize(); Owner.EnsureComponent(out _physicsComponent); } /// /// Checks if a tool can change the anchored status. /// /// The user doing the action /// The tool being used, can be null if forcing it /// Whether or not to check if the tool is valid /// true if it is valid, false otherwise private async Task Valid(IEntity? user, IEntity? utilizing, [NotNullWhen(true)] bool force = false) { if (!Owner.HasComponent()) { return false; } if (user != null && !force) { if (utilizing == null || !utilizing.TryGetComponent(out ToolComponent? tool) || !(await tool.UseTool(user, Owner, 0.5f, Tool))) { return false; } } return true; } /// /// Tries to anchor the owner of this component. /// /// The entity doing the anchoring /// The tool being used, if any /// Whether or not to ignore valid tool checks /// true if anchored, false otherwise public async Task TryAnchor(IEntity? user, IEntity? utilizing = null, bool force = false) { if (!(await Valid(user, utilizing, force))) { return false; } if (_physicsComponent == null) return false; var attempt = new AnchorAttemptMessage(); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, attempt, false); if (attempt.Cancelled) return false; // Snap rotation to cardinal (multiple of 90) var rot = Owner.Transform.LocalRotation; Owner.Transform.LocalRotation = Math.Round(rot / (Math.PI / 2)) * (Math.PI / 2); if (Owner.TryGetComponent(out PullableComponent? pullableComponent)) { if (pullableComponent.Puller != null) { pullableComponent.TryStopPull(); } } if (Snap) Owner.SnapToGrid(Owner.EntityManager); _physicsComponent.BodyType = BodyType.Static; Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new AnchoredMessage(), false); return true; } /// /// Tries to unanchor the owner of this component. /// /// The entity doing the unanchoring /// The tool being used, if any /// Whether or not to ignore valid tool checks /// true if unanchored, false otherwise public async Task TryUnAnchor(IEntity? user, IEntity? utilizing = null, bool force = false) { if (!(await Valid(user, utilizing, force))) { return false; } if (_physicsComponent == null) return false; var attempt = new UnanchorAttemptMessage(); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, attempt, false); if (attempt.Cancelled) return false; _physicsComponent.BodyType = BodyType.Dynamic; Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new UnanchoredMessage(), false); return true; } /// /// Tries to toggle the anchored status of this component's owner. /// /// The entity doing the unanchoring /// The tool being used, if any /// Whether or not to ignore valid tool checks /// true if toggled, false otherwise public async Task TryToggleAnchor(IEntity? user, IEntity? utilizing = null, bool force = false) { if (_physicsComponent == null) return false; return _physicsComponent.BodyType == BodyType.Static ? await TryUnAnchor(user, utilizing, force) : await TryAnchor(user, utilizing, force); } async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { return await TryToggleAnchor(eventArgs.User, eventArgs.Using); } } public class AnchorAttemptMessage : CancellableEntityEventArgs { } public class UnanchorAttemptMessage : CancellableEntityEventArgs { } public class AnchoredMessage : EntityEventArgs {} public class UnanchoredMessage : EntityEventArgs {} }