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>
This commit is contained in:
kin98
2025-10-31 19:11:15 +01:00
committed by GitHub
parent ecd5bf3019
commit 0fdc56c796
12 changed files with 221 additions and 89 deletions

View File

@@ -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<CP14MagicVisionMarkerComponent, AfterAutoHandleStateEvent>(OnHandleStateMarker);
SubscribeLocalEvent<CP14MagicVisionComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<CP14MagicVisionComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectRelayedEvent<LocalPlayerAttachedEvent>>(OnPlayerAttached);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectRelayedEvent<LocalPlayerDetachedEvent>>(OnPlayerDetached);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusEffectApplied);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectRemovedEvent>(OnStatusEffectRemoved);
SubscribeLocalEvent<CP14MagicVisionComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<CP14MagicVisionComponent, ComponentShutdown>(OnComponentShutdown);
}
private void OnComponentShutdown(Entity<CP14MagicVisionComponent> ent, ref ComponentShutdown args)
private void OnPlayerAttached(Entity<CP14MagicVisionStatusEffectComponent> ent, ref StatusEffectRelayedEvent<LocalPlayerAttachedEvent> 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<CP14MagicVisionComponent> ent, ref ComponentInit args)
private void OnStatusEffectApplied(Entity<CP14MagicVisionStatusEffectComponent> 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<CP14MagicVisionComponent> ent, ref LocalPlayerAttachedEvent args)
private void OnPlayerDetached(Entity<CP14MagicVisionStatusEffectComponent> ent, ref StatusEffectRelayedEvent<LocalPlayerDetachedEvent> 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<CP14MagicVisionComponent> ent, ref LocalPlayerDetachedEvent args)
private void OnStatusEffectRemoved(Entity<CP14MagicVisionStatusEffectComponent> 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<CP14MagicVisionMarkerComponent> 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<CP14MagicVisionStatusEffectComponent> 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<CP14MagicVisionStatusEffectComponent> ent)
{
// Check if it is the last Magic Vision Status Effect
if (_status.HasEffectComp<CP14MagicVisionStatusEffectComponent>(_player.LocalEntity))
return;
if (_overlay != null)
{
_overlayMan.RemoveOverlay(_overlay);
_overlay = null;
}
if (_overlay2 != null)
{
_overlayMan.RemoveOverlay(_overlay2);
_overlay2 = null;
}
_audio.PlayGlobal(_endSound, ent);
}
}

View File

@@ -44,9 +44,6 @@ public sealed class CP14MagicVisionOverlay : Overlay
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<CP14MagicVisionComponent>(playerEntity))
return;
var curTime = _timing.CurTime;
var timeLeft = (float)(curTime - StartOverlay).TotalSeconds;

View File

@@ -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<MetaDataComponent, CP14MagicVisionToggleActionEvent>(OnMagicVisionToggle);
SubscribeLocalEvent<CP14MagicVisionComponent, GetVisMaskEvent>(OnGetVisMask);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectRelayedEvent<GetVisMaskEvent>>(OnGetVisMask);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectAppliedEvent>(OnApplied);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectRemovedEvent>(OnRemoved);
}
private void OnGetVisMask(Entity<CP14MagicVisionComponent> ent, ref GetVisMaskEvent args)
private void OnGetVisMask(Entity<CP14MagicVisionStatusEffectComponent> ent, ref StatusEffectRelayedEvent<GetVisMaskEvent> args)
{
args.VisibilityMask |= (int)VisibilityFlags.CP14MagicVision;
}
var appliedMask = (int)CP14MagicVisionStatusEffectComponent.VisibilityMask;
var newArgs = args.Args;
private void OnMagicVisionToggle(Entity<MetaDataComponent> ent, ref CP14MagicVisionToggleActionEvent args)
{
if (!HasComp<CP14MagicVisionComponent>(ent))
{
AddComp<CP14MagicVisionComponent>(ent);
}
else
{
RemComp<CP14MagicVisionComponent>(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<CP14MagicVisionStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
{
_eye.RefreshVisibilityMask(args.Target);
}
private void OnRemoved(Entity<CP14MagicVisionStatusEffectComponent> ent, ref StatusEffectRemovedEvent args)
{
_eye.RefreshVisibilityMask(args.Target);
}
}

View File

@@ -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<CP14EntityEffectsStatusEffectComponent, StatusEffectComponent>();
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();
}
}

View File

@@ -0,0 +1,29 @@
using Content.Shared.EntityEffects;
namespace Content.Server._CP14.StatusEffect;
/// <summary>
/// Applies Entity Effects at a given frequency
/// </summary>
[RegisterComponent, AutoGenerateComponentState, Access(typeof(CP14EntityEffectsStatusEffectSystem))]
public sealed partial class CP14EntityEffectsStatusEffectComponent : Component
{
/// <summary>
/// List of Effects that will be applied
/// </summary>
[DataField]
public List<EntityEffect> Effects = [];
/// <summary>
/// How often objects will try to apply <see cref="Effects"/>. In Seconds.
/// </summary>
[DataField]
public TimeSpan Frequency = TimeSpan.FromSeconds(5);
/// <summary>
/// The time of the next Effect trigger
/// </summary>
[DataField]
public TimeSpan NextUpdateTime { get; set; } = TimeSpan.Zero;
}

View File

@@ -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<StatusEffectContainerComponent, DamageModifyEvent>(RelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, GetVisMaskEvent>(RefRelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, MobStateChangedEvent>(RefRelayStatusEffectEvent);
//CP14 Zone end
SubscribeLocalEvent<StatusEffectContainerComponent, LocalPlayerAttachedEvent>(RelayStatusEffectEvent);

View File

@@ -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<StatusEffectsSystem>();
if (!effectSys.HasStatusEffect(args.Target.Value, StatusEffect))
effectSys.TrySetStatusEffectDuration(args.Target.Value, StatusEffect);
else
effectSys.TryRemoveStatusEffect(args.Target.Value, StatusEffect);
}
}

View File

@@ -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<CP14MagicVisionMarkerComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<CP14MagicVisionStatusEffectComponent, StatusEffectRelayedEvent<MobStateChangedEvent>>(OnMobStateChange);
}
private void OnMobStateChange(Entity<CP14MagicVisionStatusEffectComponent> ent, ref StatusEffectRelayedEvent<MobStateChangedEvent> 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<CP14MagicVisionMarkerComponent> ent, ref ExaminedEvent args)
@@ -148,7 +158,3 @@ public abstract class CP14SharedMagicVisionSystem : EntitySystem
Dirty(ent, markerComp);
}
}
public sealed partial class CP14MagicVisionToggleActionEvent : InstantActionEvent
{
}

View File

@@ -1,11 +0,0 @@
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.MagicVision;
/// <summary>
/// Allows to see magic vision trace entities
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14MagicVisionComponent : Component
{
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.Eye;
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.MagicVision;
/// <summary>
/// Allows to see magic vision trace entities
/// Use only in conjunction with <see cref="StatusEffectComponent"/>, on the status effect entity.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14MagicVisionStatusEffectComponent : Component
{
/// <summary>
/// VisionMask to see Magic Vision layer
/// </summary>
public const VisibilityFlags VisibilityMask = VisibilityFlags.CP14MagicVision;
}

View File

@@ -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

View File

@@ -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