| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- using System.Linq;
- using Content.Server.Administration.Logs;
- using Content.Server.Pointing.Components;
- using Content.Shared.CCVar;
- using Content.Shared.Database;
- using Content.Shared.Examine;
- using Content.Shared.Eye;
- using Content.Shared.Ghost;
- using Content.Shared.IdentityManagement;
- using Content.Shared.Input;
- using Content.Shared.Interaction;
- using Content.Shared.Mind;
- using Content.Shared.Pointing;
- using Content.Shared.Popups;
- using JetBrains.Annotations;
- using Robust.Server.GameObjects;
- using Robust.Server.Player;
- using Robust.Shared.Configuration;
- using Robust.Shared.Enums;
- using Robust.Shared.GameStates;
- using Robust.Shared.Input.Binding;
- using Robust.Shared.Map;
- using Robust.Shared.Player;
- using Robust.Shared.Replays;
- using Robust.Shared.Timing;
- namespace Content.Server.Pointing.EntitySystems
- {
- [UsedImplicitly]
- internal sealed class PointingSystem : SharedPointingSystem
- {
- [Dependency] private readonly IConfigurationManager _config = default!;
- [Dependency] private readonly IReplayRecordingManager _replay = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
- [Dependency] private readonly SharedPopupSystem _popup = default!;
- [Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
- [Dependency] private readonly SharedMindSystem _minds = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
- [Dependency] private readonly SharedMapSystem _map = default!;
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly ExamineSystemShared _examine = default!;
- private TimeSpan _pointDelay = TimeSpan.FromSeconds(0.5f);
- /// <summary>
- /// A dictionary of players to the last time that they
- /// pointed at something.
- /// </summary>
- private readonly Dictionary<ICommonSession, TimeSpan> _pointers = new();
- private const float PointingRange = 15f;
- private void GetCompState(Entity<PointingArrowComponent> entity, ref ComponentGetState args)
- {
- args.State = new SharedPointingArrowComponentState
- {
- StartPosition = entity.Comp.StartPosition,
- EndTime = entity.Comp.EndTime
- };
- }
- private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
- {
- if (e.NewStatus != SessionStatus.Disconnected)
- {
- return;
- }
- _pointers.Remove(e.Session);
- }
- // TODO: FOV
- private void SendMessage(
- EntityUid source,
- IEnumerable<ICommonSession> viewers,
- EntityUid pointed,
- string selfMessage,
- string viewerMessage,
- string? viewerPointedAtMessage = null)
- {
- var netSource = GetNetEntity(source);
- foreach (var viewer in viewers)
- {
- if (viewer.AttachedEntity is not {Valid: true} viewerEntity)
- {
- continue;
- }
- var message = viewerEntity == source
- ? selfMessage
- : viewerEntity == pointed && viewerPointedAtMessage != null
- ? viewerPointedAtMessage
- : viewerMessage;
- // Someone pointing at YOU is slightly more important
- var popupType = viewerEntity == pointed ? PopupType.Medium : PopupType.Small;
- RaiseNetworkEvent(new PopupEntityEvent(message, popupType, netSource), viewerEntity);
- }
- _replay.RecordServerMessage(new PopupEntityEvent(viewerMessage, PopupType.Small, netSource));
- }
- public bool InRange(EntityUid pointer, EntityCoordinates coordinates)
- {
- if (HasComp<GhostComponent>(pointer))
- {
- return _transform.InRange(Transform(pointer).Coordinates, coordinates, 15);
- }
- else
- {
- return _examine.InRangeUnOccluded(pointer, coordinates, 15, predicate: e => e == pointer);
- }
- }
- public bool TryPoint(ICommonSession? session, EntityCoordinates coordsPointed, EntityUid pointed)
- {
- if (session?.AttachedEntity is not { } player)
- {
- Log.Warning($"Player {session} attempted to point without any attached entity");
- return false;
- }
- if (!coordsPointed.IsValid(EntityManager))
- {
- Log.Warning($"Player {ToPrettyString(player)} attempted to point at invalid coordinates: {coordsPointed}");
- return false;
- }
- if (_pointers.TryGetValue(session, out var lastTime) &&
- _gameTiming.CurTime < lastTime + _pointDelay)
- {
- return false;
- }
- if (HasComp<PointingArrowComponent>(pointed))
- {
- // this is a pointing arrow. no pointing here...
- return false;
- }
- if (!CanPoint(player))
- {
- return false;
- }
- if (!InRange(player, coordsPointed))
- {
- _popup.PopupEntity(Loc.GetString("pointing-system-try-point-cannot-reach"), player, player);
- return false;
- }
- var mapCoordsPointed = _transform.ToMapCoordinates(coordsPointed);
- _rotateToFaceSystem.TryFaceCoordinates(player, mapCoordsPointed.Position);
- var arrow = EntityManager.SpawnEntity("PointingArrow", coordsPointed);
- if (TryComp<PointingArrowComponent>(arrow, out var pointing))
- {
- pointing.StartPosition = _transform.ToCoordinates((arrow, Transform(arrow)), _transform.ToMapCoordinates(Transform(player).Coordinates)).Position;
- pointing.EndTime = _gameTiming.CurTime + PointDuration;
- Dirty(arrow, pointing);
- }
- if (EntityQuery<PointingArrowAngeringComponent>().FirstOrDefault() != null)
- {
- if (TryComp<PointingArrowComponent>(arrow, out var pointingArrowComponent))
- {
- pointingArrowComponent.Rogue = true;
- }
- }
- var layer = (int) VisibilityFlags.Normal;
- if (TryComp(player, out VisibilityComponent? playerVisibility))
- {
- var arrowVisibility = EntityManager.EnsureComponent<VisibilityComponent>(arrow);
- layer = playerVisibility.Layer;
- _visibilitySystem.SetLayer((arrow, arrowVisibility), (ushort) layer);
- }
- // Get players that are in range and whose visibility layer matches the arrow's.
- bool ViewerPredicate(ICommonSession playerSession)
- {
- if (!_minds.TryGetMind(playerSession, out _, out var mind) ||
- mind.CurrentEntity is not { Valid: true } ent ||
- !TryComp(ent, out EyeComponent? eyeComp) ||
- (eyeComp.VisibilityMask & layer) == 0)
- return false;
- return _transform.GetMapCoordinates(ent).InRange(_transform.GetMapCoordinates(player), PointingRange);
- }
- var viewers = Filter.Empty()
- .AddWhere(session1 => ViewerPredicate(session1))
- .Recipients;
- string selfMessage;
- string viewerMessage;
- string? viewerPointedAtMessage = null;
- var playerName = Identity.Entity(player, EntityManager);
- if (Exists(pointed))
- {
- var pointedName = Identity.Entity(pointed, EntityManager);
- selfMessage = player == pointed
- ? Loc.GetString("pointing-system-point-at-self")
- : Loc.GetString("pointing-system-point-at-other", ("other", pointedName));
- viewerMessage = player == pointed
- ? Loc.GetString("pointing-system-point-at-self-others", ("otherName", playerName), ("other", playerName))
- : Loc.GetString("pointing-system-point-at-other-others", ("otherName", playerName), ("other", pointedName));
- viewerPointedAtMessage = Loc.GetString("pointing-system-point-at-you-other", ("otherName", playerName));
- var ev = new AfterPointedAtEvent(pointed);
- RaiseLocalEvent(player, ref ev);
- var gotev = new AfterGotPointedAtEvent(player);
- RaiseLocalEvent(pointed, ref gotev);
- _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):user} pointed at {ToPrettyString(pointed):target} {Transform(pointed).Coordinates}");
- }
- else
- {
- TileRef? tileRef = null;
- string? position = null;
- if (_mapManager.TryFindGridAt(mapCoordsPointed, out var gridUid, out var grid))
- {
- position = $"EntId={gridUid} {_map.WorldToTile(gridUid, grid, mapCoordsPointed.Position)}";
- tileRef = _map.GetTileRef(gridUid, grid, _map.WorldToTile(gridUid, grid, mapCoordsPointed.Position));
- }
- var tileDef = _tileDefinitionManager[tileRef?.Tile.TypeId ?? 0];
- var name = Loc.GetString(tileDef.Name);
- selfMessage = Loc.GetString("pointing-system-point-at-tile", ("tileName", name));
- viewerMessage = Loc.GetString("pointing-system-other-point-at-tile", ("otherName", playerName), ("tileName", name));
- _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):user} pointed at {name} {(position == null ? mapCoordsPointed : position)}");
- }
- _pointers[session] = _gameTiming.CurTime;
- SendMessage(player, viewers, pointed, selfMessage, viewerMessage, viewerPointedAtMessage);
- return true;
- }
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<PointingArrowComponent, ComponentGetState>(GetCompState);
- SubscribeNetworkEvent<PointingAttemptEvent>(OnPointAttempt);
- _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
- CommandBinds.Builder
- .Bind(ContentKeyFunctions.Point, new PointerInputCmdHandler(TryPoint))
- .Register<PointingSystem>();
- Subs.CVar(_config, CCVars.PointingCooldownSeconds, v => _pointDelay = TimeSpan.FromSeconds(v), true);
- }
- private void OnPointAttempt(PointingAttemptEvent ev, EntitySessionEventArgs args)
- {
- var target = GetEntity(ev.Target);
- if (TryComp(target, out TransformComponent? xformTarget))
- TryPoint(args.SenderSession, xformTarget.Coordinates, target);
- else
- Log.Warning($"User {args.SenderSession} attempted to point at a non-existent entity uid: {ev.Target}");
- }
- public override void Shutdown()
- {
- base.Shutdown();
- _playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
- _pointers.Clear();
- }
- public override void Update(float frameTime)
- {
- var currentTime = _gameTiming.CurTime;
- var query = AllEntityQuery<PointingArrowComponent>();
- while (query.MoveNext(out var uid, out var component))
- {
- Update((uid, component), currentTime);
- }
- }
- private void Update(Entity<PointingArrowComponent> pointing, TimeSpan currentTime)
- {
- // TODO: That pause PR
- var component = pointing.Comp;
- if (component.EndTime > currentTime)
- return;
- if (component.Rogue)
- {
- RemComp<PointingArrowComponent>(pointing);
- EnsureComp<RoguePointingArrowComponent>(pointing);
- return;
- }
- Del(pointing);
- }
- }
- }
|