using Content.Client.Movement.Systems; using Content.Shared.Actions; using Content.Shared.Ghost; using Robust.Client.Console; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Player; namespace Content.Client.Ghost { public sealed class GhostSystem : SharedGhostSystem { [Dependency] private readonly IClientConsoleHost _console = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly ContentEyeSystem _contentEye = default!; public int AvailableGhostRoleCount { get; private set; } private bool _ghostVisibility; private bool GhostVisibility { get => _ghostVisibility; set { if (_ghostVisibility == value) { return; } _ghostVisibility = value; var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var ghost, out var sprite)) { UpdateVisibility((uid, ghost, sprite)); } } } public GhostComponent? Player => CompOrNull(_playerManager.LocalEntity); public bool IsGhost => Player != null; public event Action? PlayerRemoved; public event Action? PlayerUpdated; public event Action? PlayerAttached; public event Action? PlayerDetached; public event Action? GhostWarpsResponse; public event Action? GhostRoleCountUpdated; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnGhostRemove); SubscribeLocalEvent(OnGhostState); SubscribeLocalEvent(OnGhostPlayerAttach); SubscribeLocalEvent(OnGhostPlayerDetach); SubscribeNetworkEvent(OnGhostWarpsResponse); SubscribeNetworkEvent(OnUpdateGhostRoleCount); SubscribeLocalEvent(OnToggleLighting); SubscribeLocalEvent(OnToggleFoV); SubscribeLocalEvent(OnToggleGhosts); } private void OnStartup(EntityUid uid, GhostComponent component, ComponentStartup args) { UpdateVisibility((uid, component)); } private void UpdateVisibility(Entity ghost) { if (!Resolve(ghost.Owner, ref ghost.Comp1, ref ghost.Comp2)) return; ghost.Comp2.Visible = GhostVisibility || ghost.Comp1.Visible || ghost.Owner == _playerManager.LocalEntity; } private void OnToggleLighting(EntityUid uid, EyeComponent component, ToggleLightingActionEvent args) { if (args.Handled) return; Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup"), args.Performer); _contentEye.RequestToggleLight(uid, component); args.Handled = true; } private void OnToggleFoV(EntityUid uid, EyeComponent component, ToggleFoVActionEvent args) { if (args.Handled) return; Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-fov-popup"), args.Performer); _contentEye.RequestToggleFov(uid, component); args.Handled = true; } private void OnToggleGhosts(EntityUid uid, GhostComponent component, ToggleGhostsActionEvent args) { if (args.Handled) return; Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-ghost-visibility-popup"), args.Performer); if (uid == _playerManager.LocalEntity) ToggleGhostVisibility(); args.Handled = true; } private void OnGhostRemove(EntityUid uid, GhostComponent component, ComponentRemove args) { _actions.RemoveAction(uid, component.ToggleLightingActionEntity); _actions.RemoveAction(uid, component.ToggleFoVActionEntity); _actions.RemoveAction(uid, component.ToggleGhostsActionEntity); _actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity); if (uid != _playerManager.LocalEntity) return; GhostVisibility = false; PlayerRemoved?.Invoke(component); } private void OnGhostPlayerAttach(EntityUid uid, GhostComponent component, LocalPlayerAttachedEvent localPlayerAttachedEvent) { GhostVisibility = true; PlayerAttached?.Invoke(component); } private void OnGhostState(EntityUid uid, GhostComponent component, ref AfterAutoHandleStateEvent args) { if (TryComp(uid, out var sprite)) sprite.LayerSetColor(0, component.Color); UpdateVisibility((uid, component, null)); if (uid != _playerManager.LocalEntity) return; PlayerUpdated?.Invoke(component); } private void OnGhostPlayerDetach(EntityUid uid, GhostComponent component, LocalPlayerDetachedEvent args) { GhostVisibility = false; PlayerDetached?.Invoke(); } private void OnGhostWarpsResponse(GhostWarpsResponseEvent msg) { if (!IsGhost) return; GhostWarpsResponse?.Invoke(msg); } private void OnUpdateGhostRoleCount(GhostUpdateGhostRoleCountEvent msg) { AvailableGhostRoleCount = msg.AvailableGhostRoles; GhostRoleCountUpdated?.Invoke(msg); } public void RequestWarps() { RaiseNetworkEvent(new GhostWarpsRequestEvent()); } public void ReturnToBody() { var msg = new GhostReturnToBodyRequest(); RaiseNetworkEvent(msg); } public void OpenGhostRoles() { _console.RemoteExecuteCommand(null, "ghostroles"); } public void ToggleGhostVisibility() { GhostVisibility = !GhostVisibility; } public void ReturnToRound() { var msg = new GhostReturnToRoundRequest(); RaiseNetworkEvent(msg); } } }