#nullable enable using System.Linq; using Content.Server.Explosions; using Content.Shared.GameObjects.Components.Pointing; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Server.Interfaces.Player; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Random; using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; using DrawDepth = Content.Shared.GameObjects.DrawDepth; namespace Content.Server.GameObjects.Components.Pointing { [RegisterComponent] public class RoguePointingArrowComponent : SharedRoguePointingArrowComponent { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [ViewVariables] private IEntity? _chasing; [ViewVariables(VVAccess.ReadWrite)] private float _turningDelay; [ViewVariables(VVAccess.ReadWrite)] private float _chasingDelay; [ViewVariables(VVAccess.ReadWrite)] private float _chasingSpeed; [ViewVariables(VVAccess.ReadWrite)] private float _chasingTime; private IEntity? RandomNearbyPlayer() { var players = _playerManager .GetPlayersInRange(Owner.Transform.Coordinates, 15) .Where(player => player.AttachedEntity != null) .ToArray(); if (players.Length == 0) { return null; } return _random.Pick(players).AttachedEntity; } private void UpdateAppearance() { if (_chasing == null || !Owner.TryGetComponent(out AppearanceComponent? appearance)) { return; } appearance.SetData(RoguePointingArrowVisuals.Rotation, Owner.Transform.LocalRotation.Degrees); } protected override void Startup() { base.Startup(); if (Owner.TryGetComponent(out SpriteComponent? sprite)) { sprite.DrawDepth = (int) DrawDepth.Overlays; } } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref _turningDelay, "turningDelay", 2); serializer.DataField(ref _chasingDelay, "chasingDelay", 1); serializer.DataField(ref _chasingSpeed, "chasingSpeed", 5); serializer.DataField(ref _chasingTime, "chasingTime", 1f); } public void Update(float frameTime) { _chasing ??= RandomNearbyPlayer(); if (_chasing == null) { Owner.Delete(); return; } _turningDelay -= frameTime; if (_turningDelay > 0) { var difference = _chasing.Transform.WorldPosition - Owner.Transform.WorldPosition; var angle = difference.ToAngle(); var adjusted = angle.Degrees + 90; var newAngle = Angle.FromDegrees(adjusted); Owner.Transform.LocalRotation = newAngle; UpdateAppearance(); return; } _chasingDelay -= frameTime; Owner.Transform.WorldRotation += Angle.FromDegrees(20); UpdateAppearance(); var toChased = _chasing.Transform.WorldPosition - Owner.Transform.WorldPosition; Owner.Transform.WorldPosition += toChased * frameTime * _chasingSpeed; _chasingTime -= frameTime; if (_chasingTime > 0) { return; } Owner.SpawnExplosion(0, 2, 1, 1); EntitySystem.Get().PlayFromEntity("/Audio/Effects/explosion.ogg", Owner); Owner.Delete(); } } }