diff --git a/Content.Client/Ghost/GhostSystem.cs b/Content.Client/Ghost/GhostSystem.cs index 2a851d5bdce2ee..e3822e5d512995 100644 --- a/Content.Client/Ghost/GhostSystem.cs +++ b/Content.Client/Ghost/GhostSystem.cs @@ -46,8 +46,10 @@ private bool GhostVisibility public bool IsGhostBarPatron => PlayerGhostPatron != null; public event Action? PlayerRemoved; + public event Action? MediumRemoved; public event Action? PlayerUpdated; public event Action? PlayerAttached; + public event Action? MediumAttached; public event Action? PlayerDetached; public event Action? GhostWarpsResponse; public event Action? GhostRoleCountUpdated; @@ -69,6 +71,11 @@ public override void Initialize() SubscribeLocalEvent(OnToggleLighting); SubscribeLocalEvent(OnToggleFoV); SubscribeLocalEvent(OnToggleGhosts); + + SubscribeLocalEvent(OnMediumStartup); + SubscribeLocalEvent(OnGhostMediumPlayerAttach); + SubscribeLocalEvent(OnToggleGhostsMedium); + SubscribeLocalEvent(OnGhostMediumRemove); } private void OnStartup(EntityUid uid, GhostComponent component, ComponentStartup args) @@ -77,6 +84,12 @@ private void OnStartup(EntityUid uid, GhostComponent component, ComponentStartup sprite.Visible = GhostVisibility || uid == _playerManager.LocalEntity; } + private void OnMediumStartup(EntityUid uid, MediumComponent component, ComponentStartup args) + { + if (TryComp(uid, out SpriteComponent? sprite)) + sprite.Visible = GhostVisibility || uid == _playerManager.LocalEntity; + } + private void OnToggleLighting(EntityUid uid, EyeComponent component, ToggleLightingActionEvent args) { if (args.Handled) @@ -110,6 +123,19 @@ private void OnToggleGhosts(EntityUid uid, GhostComponent component, ToggleGhost args.Handled = true; } + private void OnToggleGhostsMedium(EntityUid uid, MediumComponent component, ToggleGhostsMediumActionEvent args) + { + if (args.Handled) + return; + + var locId = GhostVisibility ? "ghost-gui-toggle-ghost-visibility-popup-off" : "ghost-gui-toggle-ghost-visibility-popup-on"; + Popup.PopupEntity(Loc.GetString(locId), args.Performer); + if (uid == _playerManager.LocalEntity) + ToggleGhostVisibility(); + + args.Handled = true; + } + private void OnGhostRemove(EntityUid uid, GhostComponent component, ComponentRemove args) { _actions.RemoveAction(uid, component.ToggleLightingActionEntity); @@ -124,12 +150,29 @@ private void OnGhostRemove(EntityUid uid, GhostComponent component, ComponentRem PlayerRemoved?.Invoke(component); } + private void OnGhostMediumRemove(EntityUid uid, MediumComponent component, ComponentRemove args) + { + _actions.RemoveAction(uid, component.ToggleGhostsMediumActionEntity); + + if (uid != _playerManager.LocalEntity) + return; + + GhostVisibility = false; + MediumRemoved?.Invoke(component); + } + private void OnGhostPlayerAttach(EntityUid uid, GhostComponent component, LocalPlayerAttachedEvent localPlayerAttachedEvent) { GhostVisibility = true; PlayerAttached?.Invoke(component); } + private void OnGhostMediumPlayerAttach(EntityUid uid, MediumComponent component, LocalPlayerAttachedEvent localPlayerAttachedEvent) + { + GhostVisibility = true; + MediumAttached?.Invoke(component); + } + private void OnGhostState(EntityUid uid, GhostComponent component, ref AfterAutoHandleStateEvent args) { if (TryComp(uid, out var sprite)) diff --git a/Content.Client/_Impstation/Construction/ConstructionMediumComponent.cs b/Content.Client/_Impstation/Construction/ConstructionMediumComponent.cs new file mode 100644 index 00000000000000..7ebf106372b59e --- /dev/null +++ b/Content.Client/_Impstation/Construction/ConstructionMediumComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Construction.Prototypes; +using Robust.Shared.GameObjects; +using Robust.Shared.ViewVariables; + +namespace Content.Client.Construction +{ + [RegisterComponent] + public sealed partial class ConstructionMediumComponent : Component + { + [ViewVariables] public ConstructionPrototype? Prototype { get; set; } + } +} diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 77cf813b56b4f7..871e52460cf2b5 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -5,8 +5,10 @@ using Content.Server.GameTicking; using Content.Server.Ghost.Components; using Content.Server.Mind; +using Content.Server.Polymorph.Components; using Content.Server.Roles.Jobs; using Content.Server.Warps; +using Content.Shared._Impstation.Ghost; using Content.Shared.Actions; using Content.Shared.CCVar; using Content.Shared.Damage; @@ -28,6 +30,7 @@ using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Configuration; +using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; @@ -95,6 +98,26 @@ public override void Initialize() SubscribeLocalEvent(_ => MakeVisible(true)); SubscribeLocalEvent(OnToggleGhostVisibilityToAll); + + SubscribeLocalEvent(OnMediumStartup); + SubscribeLocalEvent(OnMapInitMedium); + SubscribeLocalEvent(OnMediumShutdown); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + comp.CurrentMediumTime += frameTime; + + if (comp.CurrentMediumTime > comp.MediumTime) + { + EntityManager.RemoveComponent(uid); + } + } } private void OnGhostHearingAction(EntityUid uid, GhostComponent component, ToggleGhostHearingActionEvent args) @@ -171,8 +194,8 @@ private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentSt if (_gameTicker.RunLevel != GameRunLevel.PostRound) { - _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); - _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); + _visibilitySystem.AddLayer((uid, visibility), (int)VisibilityFlags.Ghost, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int)VisibilityFlags.Normal, false); _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility); } @@ -182,6 +205,23 @@ private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentSt component.TimeOfDeath = time; } + private void OnMediumStartup(EntityUid uid, MediumComponent component, ComponentStartup args) + { + // Allow this entity to be seen by other ghosts. + var visibility = EnsureComp(uid); + + if (_gameTicker.RunLevel != GameRunLevel.PostRound) + { + _visibilitySystem.AddLayer((uid, visibility), (int)VisibilityFlags.Ghost, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int)VisibilityFlags.Normal, false); + _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility); + } + + SetCanSeeGhosts(uid, true); + + var time = _gameTiming.CurTime; + } + private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args) { // Perf: If the entity is deleting itself, no reason to change these back. @@ -191,8 +231,8 @@ private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentS // Entity can't be seen by ghosts anymore. if (TryComp(uid, out VisibilityComponent? visibility)) { - _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); - _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Normal, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int)VisibilityFlags.Ghost, false); + _visibilitySystem.AddLayer((uid, visibility), (int)VisibilityFlags.Normal, false); _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility); } @@ -201,6 +241,24 @@ private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentS _actions.RemoveAction(uid, component.BooActionEntity); } + private void OnMediumShutdown(EntityUid uid, MediumComponent component, ComponentShutdown args) + { + // Perf: If the entity is deleting itself, no reason to change these back. + if (Terminating(uid)) + return; + + // Entity can't be seen by ghosts anymore. + if (TryComp(uid, out VisibilityComponent? visibility)) + { + _visibilitySystem.RemoveLayer((uid, visibility), (int)VisibilityFlags.Ghost, false); + _visibilitySystem.AddLayer((uid, visibility), (int)VisibilityFlags.Normal, false); + _visibilitySystem.RefreshVisibility(uid, visibilityComponent: visibility); + } + + // Entity can't see ghosts anymore. + SetCanSeeGhosts(uid, false); + } + private void SetCanSeeGhosts(EntityUid uid, bool canSee, EyeComponent? eyeComponent = null) { if (!Resolve(uid, ref eyeComponent, false)) @@ -221,6 +279,11 @@ private void OnMapInit(EntityUid uid, GhostComponent component, MapInitEvent arg _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction); } + private void OnMapInitMedium(EntityUid uid, MediumComponent component, MapInitEvent args) + { + _actions.AddAction(uid, ref component.ToggleGhostsMediumActionEntity, component.ToggleGhostsMediumAction); + } + private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args) { var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath); diff --git a/Content.Server/_Impstation/EntityEffects/Effects/Medium.cs b/Content.Server/_Impstation/EntityEffects/Effects/Medium.cs new file mode 100644 index 00000000000000..a72ec8c21ba427 --- /dev/null +++ b/Content.Server/_Impstation/EntityEffects/Effects/Medium.cs @@ -0,0 +1,29 @@ +using Content.Shared._Impstation.Ghost; +using Content.Shared.EntityEffects; +using Content.Shared.Polymorph; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server._Impstation.EntityEffects.Effects; + +public sealed partial class Medium : EntityEffect +{ + /// + /// What Medium prototype is used on effect + /// + //[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer))] + //public string MediumPrototype { get; set; } + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return "Grants whoever drinks this the ability to see ghosts for a while"; + } + + public override void Effect(EntityEffectBaseArgs args) + { + var entityManager = args.EntityManager; + var uid = args.TargetEntity; + + entityManager.EnsureComponent(uid); + } +} diff --git a/Content.Shared/_Impstation/Ghost/MediumComponent.cs b/Content.Shared/_Impstation/Ghost/MediumComponent.cs new file mode 100644 index 00000000000000..70fdd6399c1aed --- /dev/null +++ b/Content.Shared/_Impstation/Ghost/MediumComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared.Actions; +using Content.Shared.Ghost; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Impstation.Ghost; + +[RegisterComponent, NetworkedComponent, Access(typeof(SharedGhostSystem))] +[AutoGenerateComponentState(true)] +public sealed partial class MediumComponent : Component +{ + [DataField] + public EntProtoId ToggleGhostsMediumAction = "ActionToggleGhostsMedium"; + + [DataField, AutoNetworkedField] + public EntityUid? ToggleGhostsMediumActionEntity; + + //Time in seconds passed since medium vision activated + [DataField, AutoNetworkedField] + public float CurrentMediumTime = 0; + + //Time after how many seconds the medium effect stops + //Im just gonna put it here as a constant instead of making a whole prototype to set it from the yaml + //Because Im not expected for other reagents to reuse that effect and even less so with a different time limit + [DataField, AutoNetworkedField] + public float MediumTime = 300; // 5 minutes +} + +public sealed partial class ToggleGhostsMediumActionEvent : InstantActionEvent { } diff --git a/Resources/Locale/en-US/_Impstation/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/_Impstation/flavors/flavor-profiles.ftl index 2feba84bf57fc4..4c8b1a08ca9c9c 100644 --- a/Resources/Locale/en-US/_Impstation/flavors/flavor-profiles.ftl +++ b/Resources/Locale/en-US/_Impstation/flavors/flavor-profiles.ftl @@ -23,3 +23,5 @@ flavor-complex-snotty = like snot flavor-complex-speed = like speed flavor-base-whimsy = whimsical flavor-complex-secticket = like rotten eggs + +flavor-complex-medium = like your vision expanded diff --git a/Resources/Locale/en-US/_Impstation/reagents/meta/fun.ftl b/Resources/Locale/en-US/_Impstation/reagents/meta/fun.ftl index 22978602d29a8c..a584ee7150bd68 100644 --- a/Resources/Locale/en-US/_Impstation/reagents/meta/fun.ftl +++ b/Resources/Locale/en-US/_Impstation/reagents/meta/fun.ftl @@ -12,3 +12,6 @@ reagent-desc-ungh-juice = The glucose and citric acid seem to have neutralized t reagent-name-holium = holium reagent-desc-holium = An impossibly compressed liquid. It feels as though it could tear through any surface. + +reagent-name-medium = medium +reagent-desc-medium = An alchemical medium to the afterlife. diff --git a/Resources/Prototypes/_Impstation/Entities/Mobs/Player/medium.yml b/Resources/Prototypes/_Impstation/Entities/Mobs/Player/medium.yml new file mode 100644 index 00000000000000..e940f1d4028847 --- /dev/null +++ b/Resources/Prototypes/_Impstation/Entities/Mobs/Player/medium.yml @@ -0,0 +1,10 @@ +- type: entity + id: ActionToggleGhostsMedium + name: Toggle Ghosts + description: Toggle the visibility of ghosts. + components: + - type: InstantAction + icon: { sprite: Mobs/Ghosts/ghost_human.rsi, state: icon } + clientExclusive: true + checkCanInteract: false + event: !type:ToggleGhostsMediumActionEvent diff --git a/Resources/Prototypes/_Impstation/Flavors/flavors.yml b/Resources/Prototypes/_Impstation/Flavors/flavors.yml index 66fb901005f407..2a915968299c93 100644 --- a/Resources/Prototypes/_Impstation/Flavors/flavors.yml +++ b/Resources/Prototypes/_Impstation/Flavors/flavors.yml @@ -102,3 +102,8 @@ id: unholy flavorType: Base description: flavor-base-unholy + +- type: flavor + id: medium + flavorType: Complex + description: flavor-complex-medium diff --git a/Resources/Prototypes/_Impstation/Reagents/fun.yml b/Resources/Prototypes/_Impstation/Reagents/fun.yml index b0a700ce4b2648..04f0e5f0f18170 100644 --- a/Resources/Prototypes/_Impstation/Reagents/fun.yml +++ b/Resources/Prototypes/_Impstation/Reagents/fun.yml @@ -123,3 +123,18 @@ entity: FloorChasmEntity maxOnTileWhitelist: tags: [ FloorChasmEntity ] + +- type: reagent + id: Medium + name: reagent-name-medium + desc: reagent-desc-medium + physicalDesc: reagent-physical-desc-reflective + flavor: medium + color: "#DC89E0" + metabolisms: + Poison: + effects: + - !type:Medium + conditions: + - !type:ReagentThreshold + min: 20 diff --git a/Resources/Prototypes/_Impstation/Recipes/Reactions/chemicals.yml b/Resources/Prototypes/_Impstation/Recipes/Reactions/chemicals.yml index 8e0e0b7b51ad4d..2d18023fb83768 100644 --- a/Resources/Prototypes/_Impstation/Recipes/Reactions/chemicals.yml +++ b/Resources/Prototypes/_Impstation/Recipes/Reactions/chemicals.yml @@ -62,3 +62,19 @@ catalyst: true products: Holium: 1 + +- type: reaction + id: Medium + impact: Medium #lol + reactants: + AquamDivinos: + amount: 1 + Oculine: + amount: 1 + THC: + amount: 1 + PhilosophersJuice: + amount: 1 + catalyst: true + products: + Medium: 1