From 0fdc56c796d5002b0a7ae392fc07f08938e1b31e Mon Sep 17 00:00:00 2001 From: kin98 <51699101+kin98@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:11:15 +0100 Subject: [PATCH] Make magical vision drain Mana (#1832) * added generic Entity Effects status effect component * added Magic Vision Statuseffect component * renamed visionmask to Visibilitymask and added StatusEffectComponent notice comment * added two event listeners on MagicVisonStatusEffect applied and removed * moved changes to server side * moved Component serverside * removed Overlay Property * Added magic vison status effect prototype and applying * cleaned upnew lines * added new prototype * moved Magic vision status effect component to shared again * fixed applying mask * cleaned new lines * Moved to components folder * marked MagicVisionComponent obsolete and changed protoid name * Added parent MobStatusEffectBase * added mana cost to Magical vision spell * removed unneeded constructor * Added the system * added statuseffect eventlistners for applied and removed also removed old event listners * removed old magic vision component check * Removed Data field attribute * added back Data field attribute * added access atribute * moved Status effect update to server * removed shared System moved all to server side * Update Content.Shared/_CP14/StatusEffect/Components/CP14EntityEffectsStatusEffectComponent.cs Co-authored-by: Red <96445749+TheShuEd@users.noreply.github.com> * Fix typo in DataField attribute for Effects list * Obliterated CP14MagicVisionComponent from existence * Fix comment capitalization in CP14EntityEffectsStatusEffectComponent * Fix summary capitalization in CP14EntityEffectsStatusEffectComponent * Refactor CP14MagicVisionSystem.cs by removing blank lines Removed unnecessary blank lines for cleaner code. * Remove status effect event handlers Removed event subscription and related methods for status effects. * Remove empty line in OnExamined method * Added a Spell toggle status effect * fixed datfields shouldnt be static * imlpemented status applying in yml * cleaned white space :3 * changed toggle to has status effect * fix leftover * added check for firsttime predict so it doesnt get applied twice * added getvismaskevent to statuseffect relay * changed event lisnter to use CP14MagicVisionStatusEffectComponent to prevent future conflicts * removed unneeded _status property * added check for if its the last status effect on remove * added check if Status effect is already present if applied * changed check to component instead of entity * changed ent target to player.localEntity * fix: removed has effect check as it already has the effect when first apllied * changed event to on player attached * removed first time predicted check * changed cooldown to 0.5 seconds * made Spell mana change effect not save * added status efect applied event listner back and moved apply and remove overlay to separete methods * removed replaced action event * added fix to let nextupdate catch up to CurTime * fix: status effect not beeing removed when crit or dead * fix: Action beeing able to be used while dead * fix: added prediction check to applied back for double apply and remove bug --------- Co-authored-by: Red <96445749+TheShuEd@users.noreply.github.com> --- .../CP14ClientMagicVisionSystem.cs | 106 +++++++++--------- .../MagicVision/CP14MagicVisionOverlay.cs | 3 - .../MagicVision/CP14MagicVisionSystem.cs | 37 +++--- .../CP14EntityEffectsStatusEffectSystem.cs | 38 +++++++ .../CP14EntityEffectsStatusEffectComponent.cs | 29 +++++ .../StatusEffectSystem.Relay.cs | 3 + .../Spells/CP14SpellToggleStatusEffect.cs | 24 ++++ .../CP14SharedMagicVisionSystem.cs | 14 ++- .../Components/CP14MagicVisionComponent.cs | 11 -- .../CP14MagicVisionStatusEffectComponent.cs | 17 +++ .../Actions/Spells/Meta/mana_vision.yml | 10 +- .../Entities/StatusEffects/magic_vision.yml | 18 +++ 12 files changed, 221 insertions(+), 89 deletions(-) create mode 100644 Content.Server/_CP14/StatusEffect/CP14EntityEffectsStatusEffectSystem.cs create mode 100644 Content.Server/_CP14/StatusEffect/Components/CP14EntityEffectsStatusEffectComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellToggleStatusEffect.cs delete mode 100644 Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionComponent.cs create mode 100644 Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionStatusEffectComponent.cs create mode 100644 Resources/Prototypes/_CP14/Entities/StatusEffects/magic_vision.yml diff --git a/Content.Client/_CP14/MagicVision/CP14ClientMagicVisionSystem.cs b/Content.Client/_CP14/MagicVision/CP14ClientMagicVisionSystem.cs index 7d840f09b4..5aa3a38bc7 100644 --- a/Content.Client/_CP14/MagicVision/CP14ClientMagicVisionSystem.cs +++ b/Content.Client/_CP14/MagicVision/CP14ClientMagicVisionSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using Content.Shared._CP14.MagicVision; using Content.Shared.Examine; +using Content.Shared.StatusEffectNew; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Player; @@ -9,6 +10,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Client._CP14.MagicVision; @@ -21,6 +23,8 @@ public sealed class CP14ClientMagicVisionSystem : CP14SharedMagicVisionSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly StatusEffectsSystem _status = default!; + private CP14MagicVisionOverlay? _overlay; private CP14MagicVisionNoirOverlay? _overlay2; @@ -36,71 +40,39 @@ public sealed class CP14ClientMagicVisionSystem : CP14SharedMagicVisionSystem SubscribeLocalEvent(OnHandleStateMarker); - SubscribeLocalEvent(OnPlayerAttached); - SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent>(OnPlayerAttached); + SubscribeLocalEvent>(OnPlayerDetached); + + SubscribeLocalEvent(OnStatusEffectApplied); + SubscribeLocalEvent(OnStatusEffectRemoved); - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnComponentShutdown); } - private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + private void OnPlayerAttached(Entity ent, ref StatusEffectRelayedEvent args) { - if (_player.LocalEntity != ent) - return; - if (_overlay != null) - { - _overlayMan.RemoveOverlay(_overlay); - _overlay = null; - } - if (_overlay2 != null) - { - _overlayMan.RemoveOverlay(_overlay2); - _overlay2 = null; - } - - _audio.PlayGlobal(_endSound, ent); + ApplyOverlay(ent); } - - private void OnComponentInit(Entity ent, ref ComponentInit args) + private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) { - if (_player.LocalEntity != ent) + //Prevents it from beeing applied twice + if (_timing.IsFirstTimePredicted == true) return; - _overlay = new CP14MagicVisionOverlay(); - _overlayMan.AddOverlay(_overlay); - _overlay.StartOverlay = _timing.CurTime; - - _overlay2 = new CP14MagicVisionNoirOverlay(); - _overlayMan.AddOverlay(_overlay2); - - _audio.PlayGlobal(_startSound, ent); + ApplyOverlay(ent); } - private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) + private void OnPlayerDetached(Entity ent, ref StatusEffectRelayedEvent args) { - _overlay = new CP14MagicVisionOverlay(); - _overlayMan.AddOverlay(_overlay); - _overlay.StartOverlay = _timing.CurTime; - - _overlay2 = new CP14MagicVisionNoirOverlay(); - _overlayMan.AddOverlay(_overlay2); - - _audio.PlayGlobal(_startSound, ent); + RemoveOverlay(ent); } - private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) + private void OnStatusEffectRemoved(Entity ent, ref StatusEffectRemovedEvent args) { - if (_overlay != null) - { - _overlayMan.RemoveOverlay(_overlay); - _overlay = null; - } - if (_overlay2 != null) - { - _overlayMan.RemoveOverlay(_overlay2); - _overlay2 = null; - } - _audio.PlayGlobal(_endSound, ent); + //Prevents it from beeing removed twice + if (_timing.IsFirstTimePredicted == true) + return; + + RemoveOverlay(ent); } protected override void OnExamined(Entity ent, ref ExaminedEvent args) @@ -156,4 +128,36 @@ public sealed class CP14ClientMagicVisionSystem : CP14SharedMagicVisionSystem var alpha = 1 - progress; _sprite.SetColor(ent.Owner, Color.White.WithAlpha((float)alpha)); } + + private void ApplyOverlay(Entity ent) + { + _overlay = new CP14MagicVisionOverlay(); + _overlayMan.AddOverlay(_overlay); + _overlay.StartOverlay = _timing.CurTime; + + _overlay2 = new CP14MagicVisionNoirOverlay(); + _overlayMan.AddOverlay(_overlay2); + + _audio.PlayGlobal(_startSound, ent); + } + + private void RemoveOverlay(Entity ent) + { + // Check if it is the last Magic Vision Status Effect + if (_status.HasEffectComp(_player.LocalEntity)) + return; + + if (_overlay != null) + { + _overlayMan.RemoveOverlay(_overlay); + _overlay = null; + } + if (_overlay2 != null) + { + _overlayMan.RemoveOverlay(_overlay2); + _overlay2 = null; + } + + _audio.PlayGlobal(_endSound, ent); + } } diff --git a/Content.Client/_CP14/MagicVision/CP14MagicVisionOverlay.cs b/Content.Client/_CP14/MagicVision/CP14MagicVisionOverlay.cs index 68f7690b9b..0eefb3c4de 100644 --- a/Content.Client/_CP14/MagicVision/CP14MagicVisionOverlay.cs +++ b/Content.Client/_CP14/MagicVision/CP14MagicVisionOverlay.cs @@ -44,9 +44,6 @@ public sealed class CP14MagicVisionOverlay : Overlay if (playerEntity == null) return; - if (!_entityManager.HasComponent(playerEntity)) - return; - var curTime = _timing.CurTime; var timeLeft = (float)(curTime - StartOverlay).TotalSeconds; diff --git a/Content.Server/_CP14/MagicVision/CP14MagicVisionSystem.cs b/Content.Server/_CP14/MagicVision/CP14MagicVisionSystem.cs index 4511765154..208576a13d 100644 --- a/Content.Server/_CP14/MagicVision/CP14MagicVisionSystem.cs +++ b/Content.Server/_CP14/MagicVision/CP14MagicVisionSystem.cs @@ -1,6 +1,6 @@ using Content.Shared._CP14.MagicVision; -using Content.Shared.Eye; using Robust.Shared.Timing; +using Content.Shared.StatusEffectNew; namespace Content.Server._CP14.MagicVision; @@ -13,26 +13,19 @@ public sealed class CP14MagicVisionSystem : CP14SharedMagicVisionSystem { base.Initialize(); - SubscribeLocalEvent(OnMagicVisionToggle); - SubscribeLocalEvent(OnGetVisMask); + SubscribeLocalEvent>(OnGetVisMask); + + SubscribeLocalEvent(OnApplied); + SubscribeLocalEvent(OnRemoved); } - private void OnGetVisMask(Entity ent, ref GetVisMaskEvent args) + private void OnGetVisMask(Entity ent, ref StatusEffectRelayedEvent args) { - args.VisibilityMask |= (int)VisibilityFlags.CP14MagicVision; - } + var appliedMask = (int)CP14MagicVisionStatusEffectComponent.VisibilityMask; + var newArgs = args.Args; - private void OnMagicVisionToggle(Entity ent, ref CP14MagicVisionToggleActionEvent args) - { - if (!HasComp(ent)) - { - AddComp(ent); - } - else - { - RemComp(ent); - } - _eye.RefreshVisibilityMask(ent.Owner); + newArgs.VisibilityMask |= appliedMask; + args = args with { Args = newArgs }; } public override void Update(float frameTime) @@ -51,4 +44,14 @@ public sealed class CP14MagicVisionSystem : CP14SharedMagicVisionSystem QueueDel(uid); } } + + private void OnApplied(Entity ent, ref StatusEffectAppliedEvent args) + { + _eye.RefreshVisibilityMask(args.Target); + } + + private void OnRemoved(Entity ent, ref StatusEffectRemovedEvent args) + { + _eye.RefreshVisibilityMask(args.Target); + } } diff --git a/Content.Server/_CP14/StatusEffect/CP14EntityEffectsStatusEffectSystem.cs b/Content.Server/_CP14/StatusEffect/CP14EntityEffectsStatusEffectSystem.cs new file mode 100644 index 0000000000..daa32713ac --- /dev/null +++ b/Content.Server/_CP14/StatusEffect/CP14EntityEffectsStatusEffectSystem.cs @@ -0,0 +1,38 @@ +using Content.Shared._CP14.StatusEffect; +using Content.Shared.StatusEffectNew; +using Content.Shared.StatusEffectNew.Components; +using Robust.Shared.Timing; + +namespace Content.Server._CP14.StatusEffect; + +public sealed partial class CP14EntityEffectsStatusEffectSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly EntityManager _entityManager = default!; + + public override void Update(float frameTime) + { + base.Update(frameTime); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out var entityEffect, out var statusEffect)) + { + if (entityEffect.NextUpdateTime > _timing.CurTime) + continue; + + if (statusEffect.AppliedTo is not EntityUid targetUid) + continue; + + entityEffect.NextUpdateTime = _timing.CurTime + entityEffect.Frequency; + foreach (var effect in entityEffect.Effects) + { + //Apply Effect on target + effect.Effect(new(targetUid, _entityManager)); + } + } + } + + public override void Initialize() + { + base.Initialize(); + } +} diff --git a/Content.Server/_CP14/StatusEffect/Components/CP14EntityEffectsStatusEffectComponent.cs b/Content.Server/_CP14/StatusEffect/Components/CP14EntityEffectsStatusEffectComponent.cs new file mode 100644 index 0000000000..2027483606 --- /dev/null +++ b/Content.Server/_CP14/StatusEffect/Components/CP14EntityEffectsStatusEffectComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared.EntityEffects; + +namespace Content.Server._CP14.StatusEffect; + +/// +/// Applies Entity Effects at a given frequency +/// +[RegisterComponent, AutoGenerateComponentState, Access(typeof(CP14EntityEffectsStatusEffectSystem))] + +public sealed partial class CP14EntityEffectsStatusEffectComponent : Component +{ + /// + /// List of Effects that will be applied + /// + [DataField] + public List Effects = []; + + /// + /// How often objects will try to apply . In Seconds. + /// + [DataField] + public TimeSpan Frequency = TimeSpan.FromSeconds(5); + + /// + /// The time of the next Effect trigger + /// + [DataField] + public TimeSpan NextUpdateTime { get; set; } = TimeSpan.Zero; +} diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs index 1f977c141e..94c225054a 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -7,6 +7,7 @@ using Content.Shared.Speech; using Content.Shared.StatusEffectNew.Components; using Content.Shared.Stunnable; using Robust.Shared.Player; +using Content.Shared.Mobs; namespace Content.Shared.StatusEffectNew; @@ -16,6 +17,8 @@ public sealed partial class StatusEffectsSystem { //CP14 Zone SubscribeLocalEvent(RelayStatusEffectEvent); + SubscribeLocalEvent(RefRelayStatusEffectEvent); + SubscribeLocalEvent(RefRelayStatusEffectEvent); //CP14 Zone end SubscribeLocalEvent(RelayStatusEffectEvent); diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellToggleStatusEffect.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellToggleStatusEffect.cs new file mode 100644 index 0000000000..1f2ecbaa24 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellToggleStatusEffect.cs @@ -0,0 +1,24 @@ +using Content.Shared.StatusEffectNew; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellToggleStatusEffect : CP14SpellEffect +{ + [DataField(required: true)] + public EntProtoId StatusEffect = default; + + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + if (args.Target is null) + return; + + var effectSys = entManager.System(); + + if (!effectSys.HasStatusEffect(args.Target.Value, StatusEffect)) + effectSys.TrySetStatusEffectDuration(args.Target.Value, StatusEffect); + else + effectSys.TryRemoveStatusEffect(args.Target.Value, StatusEffect); + + } +} diff --git a/Content.Shared/_CP14/MagicVision/CP14SharedMagicVisionSystem.cs b/Content.Shared/_CP14/MagicVision/CP14SharedMagicVisionSystem.cs index 3e15d27d17..b998ad5c0b 100644 --- a/Content.Shared/_CP14/MagicVision/CP14SharedMagicVisionSystem.cs +++ b/Content.Shared/_CP14/MagicVision/CP14SharedMagicVisionSystem.cs @@ -4,6 +4,7 @@ using Content.Shared._CP14.AuraDNA; using Content.Shared._CP14.MagicVision.Components; using Content.Shared.Actions; using Content.Shared.Examine; +using Content.Shared.Mobs; using Content.Shared.StatusEffectNew; using Robust.Shared.Map; using Robust.Shared.Network; @@ -27,6 +28,15 @@ public abstract class CP14SharedMagicVisionSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>(OnMobStateChange); + } + + private void OnMobStateChange(Entity ent, ref StatusEffectRelayedEvent args) + { + if (args.Args.NewMobState == MobState.Alive) return; + + //Removes MagicVisionStatusEffect entity when MobState is Crit,Dead or Invalid + TryQueueDel(ent); } protected virtual void OnExamined(Entity ent, ref ExaminedEvent args) @@ -148,7 +158,3 @@ public abstract class CP14SharedMagicVisionSystem : EntitySystem Dirty(ent, markerComp); } } - -public sealed partial class CP14MagicVisionToggleActionEvent : InstantActionEvent -{ -} diff --git a/Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionComponent.cs b/Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionComponent.cs deleted file mode 100644 index 08a7cac7b4..0000000000 --- a/Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared._CP14.MagicVision; - -/// -/// Allows to see magic vision trace entities -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class CP14MagicVisionComponent : Component -{ -} diff --git a/Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionStatusEffectComponent.cs b/Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionStatusEffectComponent.cs new file mode 100644 index 0000000000..6232171459 --- /dev/null +++ b/Content.Shared/_CP14/MagicVision/Components/CP14MagicVisionStatusEffectComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.Eye; +using Robust.Shared.GameStates; + +namespace Content.Shared._CP14.MagicVision; + +/// +/// Allows to see magic vision trace entities +/// Use only in conjunction with , on the status effect entity. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class CP14MagicVisionStatusEffectComponent : Component +{ + /// + /// VisionMask to see Magic Vision layer + /// + public const VisibilityFlags VisibilityMask = VisibilityFlags.CP14MagicVision; +} diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_vision.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_vision.yml index 20e80d8515..c06545d815 100644 --- a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_vision.yml +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_vision.yml @@ -5,16 +5,20 @@ description: You focus on magical flows to track recent events and scan the aura imprints of other living beings. components: - type: Action - useDelay: 5 + useDelay: 0.5 itemIconStyle: BigAction checkCanInteract: false + checkConsciousness: true sound: !type:SoundPathSpecifier path: /Audio/Magic/rumble.ogg icon: sprite: _CP14/Actions/Spells/meta.rsi state: magic_vision - type: InstantAction - event: !type:CP14MagicVisionToggleActionEvent + event: !type:CP14InstantModularEffectEvent + effects: + - !type:CP14SpellToggleStatusEffect + statusEffect: CP14MetaMagicVisionSpellStatusEffect - type: entity id: CP14ManaVisionPointer @@ -51,4 +55,4 @@ color: "#42a4f5" - type: Clickable - type: Visibility - layer: 16 #magic vision only \ No newline at end of file + layer: 16 #magic vision only diff --git a/Resources/Prototypes/_CP14/Entities/StatusEffects/magic_vision.yml b/Resources/Prototypes/_CP14/Entities/StatusEffects/magic_vision.yml new file mode 100644 index 0000000000..0a4b5dbdff --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/StatusEffects/magic_vision.yml @@ -0,0 +1,18 @@ +- type: entity + id: CP14MagicVisionStatusEffect + parent: MobStatusEffectBase + name: Magical vision + components: + - type: StatusEffect + - type: CP14MagicVisionStatusEffect + +- type: entity + id: CP14MetaMagicVisionSpellStatusEffect + parent: CP14MagicVisionStatusEffect + name: Magical vision spell + components: + - type: CP14EntityEffectsStatusEffect + effects: + - !type:CP14ManaChange + manaDelta: -5 + safe: false