diff --git a/Content.Client/Ghost/GhostSystem.cs b/Content.Client/Ghost/GhostSystem.cs index a5353921fa..3ace889f27 100644 --- a/Content.Client/Ghost/GhostSystem.cs +++ b/Content.Client/Ghost/GhostSystem.cs @@ -15,7 +15,6 @@ namespace Content.Client.Ghost [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly ILightManager _lightManager = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly ContentEyeSystem _contentEye = default!; public int AvailableGhostRoleCount { get; private set; } @@ -83,7 +82,7 @@ namespace Content.Client.Ghost if (args.Handled) return; - _popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup"), args.Performer); + Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-lighting-manager-popup"), args.Performer); _lightManager.Enabled = !_lightManager.Enabled; args.Handled = true; } @@ -93,7 +92,7 @@ namespace Content.Client.Ghost if (args.Handled) return; - _popup.PopupEntity(Loc.GetString("ghost-gui-toggle-fov-popup"), args.Performer); + Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-fov-popup"), args.Performer); _contentEye.RequestToggleFov(uid); args.Handled = true; } @@ -103,7 +102,7 @@ namespace Content.Client.Ghost if (args.Handled) return; - _popup.PopupEntity(Loc.GetString("ghost-gui-toggle-ghost-visibility-popup"), args.Performer); + Popup.PopupEntity(Loc.GetString("ghost-gui-toggle-ghost-visibility-popup"), args.Performer); ToggleGhostVisibility(); args.Handled = true; } @@ -113,6 +112,7 @@ namespace Content.Client.Ghost _actions.RemoveAction(uid, component.ToggleLightingActionEntity); _actions.RemoveAction(uid, component.ToggleFoVActionEntity); _actions.RemoveAction(uid, component.ToggleGhostsActionEntity); + _actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity); if (uid != _playerManager.LocalPlayer?.ControlledEntity) return; diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index acc375b8b1..b8f4e116a4 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -739,7 +739,7 @@ public sealed partial class ChatSystem : SharedChatSystem // TODO proper speech occlusion var recipients = new Dictionary(); - var ghosts = GetEntityQuery(); + var ghostHearing = GetEntityQuery(); var xforms = GetEntityQuery(); var transformSource = xforms.GetComponent(source); @@ -756,9 +756,9 @@ public sealed partial class ChatSystem : SharedChatSystem if (transformEntity.MapID != sourceMapId) continue; - var observer = ghosts.HasComponent(playerEntity); + var observer = ghostHearing.HasComponent(playerEntity); - // even if they are an observer, in some situations we still need the range + // even if they are a ghost hearer, in some situations we still need the range if (sourceCoords.TryDistance(EntityManager, transformEntity.Coordinates, out var distance) && distance < voiceGetRange) { recipients.Add(player, new ICChatRecipientData(distance, observer)); diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index c8a410b91f..7881803901 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Mind.Components; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; +using Content.Shared.Popups; using Content.Shared.Storage.Components; using Robust.Server.GameObjects; using Robust.Server.Player; @@ -64,11 +65,35 @@ namespace Content.Server.Ghost SubscribeNetworkEvent(OnGhostWarpToTargetRequest); SubscribeLocalEvent(OnActionPerform); + SubscribeLocalEvent(OnGhostHearingAction); SubscribeLocalEvent(OnEntityStorageInsertAttempt); SubscribeLocalEvent(_ => MakeVisible(true)); } + private void OnGhostHearingAction(EntityUid uid, GhostComponent component, ToggleGhostHearingActionEvent args) + { + args.Handled = true; + + if (HasComp(uid)) + { + RemComp(uid); + _actions.SetToggled(component.ToggleGhostHearingActionEntity, true); + } + else + { + AddComp(uid); + _actions.SetToggled(component.ToggleGhostHearingActionEntity, false); + } + + var str = HasComp(uid) + ? Loc.GetString("ghost-gui-toggle-hearing-popup-on") + : Loc.GetString("ghost-gui-toggle-hearing-popup-off"); + + Popup.PopupEntity(str, uid, uid); + Dirty(uid, component); + } + private void OnActionPerform(EntityUid uid, GhostComponent component, BooActionEvent args) { if (args.Handled) @@ -164,6 +189,7 @@ namespace Content.Server.Ghost _actions.SetCooldown(component.BooActionEntity.Value, start, end); } + _actions.AddAction(uid, ref component.ToggleGhostHearingActionEntity, component.ToggleGhostHearingAction); _actions.AddAction(uid, ref component.ToggleLightingActionEntity, component.ToggleLightingAction); _actions.AddAction(uid, ref component.ToggleFoVActionEntity, component.ToggleFoVAction); _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction); diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index e58cb3a16f..9090af4dba 100644 --- a/Content.Shared/Ghost/GhostComponent.cs +++ b/Content.Shared/Ghost/GhostComponent.cs @@ -1,8 +1,6 @@ using Content.Shared.Actions; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Ghost; @@ -14,25 +12,40 @@ public sealed partial class GhostComponent : Component [ViewVariables] public bool IsAttached; - [DataField("toggleLightingAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleLightingAction = "ActionToggleLighting"; + // Actions + [DataField] + public EntProtoId ToggleLightingAction = "ActionToggleLighting"; [DataField, AutoNetworkedField] public EntityUid? ToggleLightingActionEntity; - [DataField("toggleFovAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleFoVAction = "ActionToggleFov"; + [DataField] + public EntProtoId ToggleFoVAction = "ActionToggleFov"; [DataField, AutoNetworkedField] public EntityUid? ToggleFoVActionEntity; - [DataField("toggleGhostsAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleGhostsAction = "ActionToggleGhosts"; + [DataField] + public EntProtoId ToggleGhostsAction = "ActionToggleGhosts"; [DataField, AutoNetworkedField] public EntityUid? ToggleGhostsActionEntity; - [ViewVariables(VVAccess.ReadWrite), DataField("timeOfDeath", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField] + public EntProtoId ToggleGhostHearingAction = "ActionToggleGhostHearing"; + + [DataField] + public EntityUid? ToggleGhostHearingActionEntity; + + [DataField] + public EntProtoId BooAction = "ActionGhostBoo"; + + [DataField, AutoNetworkedField] + public EntityUid? BooActionEntity; + + // End actions + + [ViewVariables(VVAccess.ReadWrite), DataField] public TimeSpan TimeOfDeath = TimeSpan.Zero; [DataField("booRadius")] @@ -41,12 +54,6 @@ public sealed partial class GhostComponent : Component [DataField("booMaxTargets")] public int BooMaxTargets = 3; - [DataField] - public EntProtoId BooAction = "ActionGhostBoo"; - - [DataField, AutoNetworkedField] - public EntityUid? BooActionEntity; - // TODO: instead of this funny stuff just give it access and update in system dirtying when needed [ViewVariables(VVAccess.ReadWrite)] public bool CanGhostInteract @@ -90,10 +97,12 @@ public sealed partial class GhostComponent : Component private bool _canReturnToBody; } +public sealed partial class ToggleFoVActionEvent : InstantActionEvent { } + +public sealed partial class ToggleGhostsActionEvent : InstantActionEvent { } + +public sealed partial class ToggleLightingActionEvent : InstantActionEvent { } + +public sealed partial class ToggleGhostHearingActionEvent : InstantActionEvent { } + public sealed partial class BooActionEvent : InstantActionEvent { } - -public sealed partial class ToggleFoVActionEvent : InstantActionEvent { }; - -public sealed partial class ToggleGhostsActionEvent : InstantActionEvent { }; - -public sealed partial class ToggleLightingActionEvent : InstantActionEvent { }; diff --git a/Content.Shared/Ghost/GhostHearingComponent.cs b/Content.Shared/Ghost/GhostHearingComponent.cs new file mode 100644 index 0000000000..c50a1f6c74 --- /dev/null +++ b/Content.Shared/Ghost/GhostHearingComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.Ghost; + +/// +/// This is used for marking entities which should receive all local chat message, even when out of range +/// +[RegisterComponent] +public sealed partial class GhostHearingComponent : Component +{ +} diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 1bd0bbacb9..c1c2c3c71e 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Emoting; using Content.Shared.Hands; using Content.Shared.Interaction.Events; using Content.Shared.Item; +using Content.Shared.Popups; using Robust.Shared.Serialization; namespace Content.Shared.Ghost @@ -12,6 +13,8 @@ namespace Content.Shared.Ghost /// public abstract class SharedGhostSystem : EntitySystem { + [Dependency] protected readonly SharedPopupSystem Popup = default!; + public override void Initialize() { base.Initialize(); diff --git a/Resources/Locale/en-US/ghost/ghost-gui.ftl b/Resources/Locale/en-US/ghost/ghost-gui.ftl index 8275d7e5d6..909513e96c 100644 --- a/Resources/Locale/en-US/ghost/ghost-gui.ftl +++ b/Resources/Locale/en-US/ghost/ghost-gui.ftl @@ -5,6 +5,9 @@ ghost-gui-toggle-ghost-visibility-popup = Toggled visibility of ghosts. ghost-gui-toggle-lighting-manager-popup = Toggled all lighting. ghost-gui-toggle-fov-popup = Toggled field-of-view. +ghost-gui-toggle-hearing-popup-on = You can now hear all messages. +ghost-gui-toggle-hearing-popup-off = You can now only hear radio and nearby messages. + ghost-target-window-title = Ghost Warp ghost-target-window-current-button = Warp: {$name} diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 851ddacdda..c07d62946e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -15,6 +15,7 @@ context: "aghost" - type: Ghost canInteract: true + - type: GhostHearing - type: Hands - type: Puller - type: CombatMode diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 66e0e42978..c7cac426a4 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -32,6 +32,7 @@ - type: Examiner skipChecks: true - type: Ghost + - type: GhostHearing - type: MovementSpeedModifier baseSprintSpeed: 12 baseWalkSpeed: 8 @@ -106,3 +107,17 @@ clientExclusive: true checkCanInteract: false event: !type:ToggleGhostsActionEvent + +- type: entity + id: ActionToggleGhostHearing + name: Toggle Ghost Hearing + description: Toggle between hearing all messages and hearing only radio & nearby messages. + noSpawn: true + components: + - type: InstantAction + checkCanInteract: false + icon: + sprite: Clothing/Ears/Headsets/base.rsi + state: icon + iconOn: Interface/Actions/ghostHearingToggled.png + event: !type:ToggleGhostHearingActionEvent diff --git a/Resources/Textures/Interface/Actions/ghostHearingToggled.png b/Resources/Textures/Interface/Actions/ghostHearingToggled.png new file mode 100644 index 0000000000..2c619ee496 Binary files /dev/null and b/Resources/Textures/Interface/Actions/ghostHearingToggled.png differ diff --git a/Resources/Textures/Interface/Actions/meta.json b/Resources/Textures/Interface/Actions/meta.json index 6a6cb73219..97d48fa9fc 100644 --- a/Resources/Textures/Interface/Actions/meta.json +++ b/Resources/Textures/Interface/Actions/meta.json @@ -48,6 +48,9 @@ }, { "name": "web" + }, + { + "name": "ghostHearingToggled" } ] }