From 5b4440d18dcba39a078e6b3ad67057ad227a5e02 Mon Sep 17 00:00:00 2001 From: Red <96445749+TheShuEd@users.noreply.github.com> Date: Wed, 9 Jul 2025 09:16:49 +0300 Subject: [PATCH] Merge pull request #1516 from crystallpunk-14/ed-08-07-2025-shader-sea Status effect spells --- .../StatusEffectSystem.Relay.cs | 6 ++ .../Spells/CP14SpellApplyStatusEffect.cs | 29 +++++++ .../CP14ApplySpellStatusEffectComponent.cs | 24 ++++++ .../CP14ApplySpellStatusEffectSystem.cs | 74 ++++++++++++++++++ .../CP14DamageModifierStatusEffect.cs | 16 ++++ .../Prototypes/_CP14/Alerts/status_effect.yml | 11 +++ .../Actions/Spells/Light/sphere_of_light.yml | 54 +++---------- .../Actions/Spells/Meta/mana_armor.yml | 37 +++++++++ .../_CP14/Entities/StatusEffects/misc.yml | 45 +++++++++++ .../_CP14/Skill/Basic/metamagic.yml | 14 ++++ .../Actions/Spells/meta.rsi/magic_armor.png | Bin 0 -> 606 bytes .../_CP14/Actions/Spells/meta.rsi/meta.json | 3 + .../Effects/Magic/cast_rune.rsi/meta.json | 11 +++ .../Effects/Magic/cast_rune.rsi/sphere.png | Bin 0 -> 2185 bytes 14 files changed, 282 insertions(+), 42 deletions(-) create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyStatusEffect.cs create mode 100644 Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectComponent.cs create mode 100644 Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectSystem.cs create mode 100644 Content.Shared/_CP14/StatusEffect/CP14DamageModifierStatusEffect.cs create mode 100644 Resources/Prototypes/_CP14/Alerts/status_effect.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_armor.yml create mode 100644 Resources/Prototypes/_CP14/Entities/StatusEffects/misc.yml create mode 100644 Resources/Textures/_CP14/Actions/Spells/meta.rsi/magic_armor.png create mode 100644 Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/sphere.png diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs index 6d97a75edd..0bf02b6cd6 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -1,3 +1,5 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Events; using Content.Shared.StatusEffectNew.Components; using Robust.Shared.Player; @@ -7,6 +9,10 @@ public abstract partial class SharedStatusEffectsSystem { protected void InitializeRelay() { + //CP14 Zone + SubscribeLocalEvent(RelayStatusEffectEvent); + //CP14 Zone end + SubscribeLocalEvent(RelayStatusEffectEvent); SubscribeLocalEvent(RelayStatusEffectEvent); } diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyStatusEffect.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyStatusEffect.cs new file mode 100644 index 0000000000..ae9b514549 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyStatusEffect.cs @@ -0,0 +1,29 @@ +using Content.Shared.StatusEffectNew; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellApplyStatusEffect : CP14SpellEffect +{ + [DataField(required: true)] + public EntProtoId StatusEffect = default; + + [DataField(required: true)] + public TimeSpan Duration = TimeSpan.FromSeconds(1f); + + [DataField] + public bool Refresh = true; + + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + if (args.Target is null) + return; + + var effectSys = entManager.System(); + + if (!Refresh) + effectSys.TryAddStatusEffectDuration(args.Target.Value, StatusEffect, Duration); + else + effectSys.TrySetStatusEffectDuration(args.Target.Value, StatusEffect, Duration); + } +} diff --git a/Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectComponent.cs b/Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectComponent.cs new file mode 100644 index 0000000000..d3cbd6fe6e --- /dev/null +++ b/Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectComponent.cs @@ -0,0 +1,24 @@ +using Content.Shared._CP14.MagicSpell.Spells; +using Robust.Shared.GameStates; + +namespace Content.Shared._CP14.StatusEffect; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause] +[Access(typeof(CP14ApplySpellStatusEffectSystem))] +public sealed partial class CP14ApplySpellStatusEffectComponent : Component +{ + [DataField(serverOnly: true)] + public List StartEffect = new(); + + [DataField(serverOnly: true)] + public List EndEffect = new(); + + [DataField(serverOnly: true)] + public List UpdateEffect = new(); + + [DataField] + public TimeSpan UpdateFrequency = TimeSpan.FromSeconds(1f); + + [DataField, AutoPausedField] + public TimeSpan NextUpdateTime = TimeSpan.Zero; +} diff --git a/Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectSystem.cs b/Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectSystem.cs new file mode 100644 index 0000000000..5877ba26ad --- /dev/null +++ b/Content.Shared/_CP14/StatusEffect/CP14ApplySpellStatusEffectSystem.cs @@ -0,0 +1,74 @@ +using Content.Shared._CP14.MagicSpell.Spells; +using Content.Shared.Damage; +using Content.Shared.StatusEffectNew; +using Content.Shared.StatusEffectNew.Components; +using Robust.Shared.Timing; + +namespace Content.Shared._CP14.StatusEffect; + +public sealed partial class CP14ApplySpellStatusEffectSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(SpellEffectApply); + SubscribeLocalEvent(SpellEffectRemove); + + SubscribeLocalEvent>(OnDamageModify); + } + + private void OnDamageModify(Entity ent, ref StatusEffectRelayedEvent args) + { + DamageSpecifier newDamage = new(); + foreach (var (type, damage) in args.Args.Damage.DamageDict) + { + var dmg = damage * ent.Comp.GlobalDefence; + + if (ent.Comp.Defence is not null && ent.Comp.Defence.TryGetValue(type, out var typeDefence)) + dmg *= typeDefence; + + newDamage.DamageDict[type] = dmg; + } + args.Args.Damage = newDamage; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out var spellEffect, out var statusEffect)) + { + if (spellEffect.NextUpdateTime > _timing.CurTime) + continue; + + if (statusEffect.AppliedTo is null) + continue; + + spellEffect.NextUpdateTime += spellEffect.UpdateFrequency; + + foreach (var effect in spellEffect.UpdateEffect) + { + effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(null, null, statusEffect.AppliedTo, Transform(statusEffect.AppliedTo.Value).Coordinates)); + } + } + } + + private void SpellEffectApply(Entity ent, ref StatusEffectAppliedEvent args) + { + foreach (var effect in ent.Comp.StartEffect) + { + effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(null, null, args.Target, Transform(args.Target).Coordinates)); + } + } + + private void SpellEffectRemove(Entity ent, ref StatusEffectRemovedEvent args) + { + foreach (var effect in ent.Comp.EndEffect) + { + effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(null, null, args.Target, Transform(args.Target).Coordinates)); + } + } +} diff --git a/Content.Shared/_CP14/StatusEffect/CP14DamageModifierStatusEffect.cs b/Content.Shared/_CP14/StatusEffect/CP14DamageModifierStatusEffect.cs new file mode 100644 index 0000000000..b91879961a --- /dev/null +++ b/Content.Shared/_CP14/StatusEffect/CP14DamageModifierStatusEffect.cs @@ -0,0 +1,16 @@ +using Content.Shared.Damage.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.StatusEffect; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(CP14ApplySpellStatusEffectSystem))] +public sealed partial class CP14DamageModifierStatusEffectComponent : Component +{ + [DataField] + public Dictionary, float>? Defence = null; + + [DataField] + public float GlobalDefence = 1f; +} diff --git a/Resources/Prototypes/_CP14/Alerts/status_effect.yml b/Resources/Prototypes/_CP14/Alerts/status_effect.yml new file mode 100644 index 0000000000..e02a919650 --- /dev/null +++ b/Resources/Prototypes/_CP14/Alerts/status_effect.yml @@ -0,0 +1,11 @@ +- type: alert + id: CP14Glowing + icons: + - sprite: _CP14/Actions/Spells/light.rsi + state: sphere_of_light + +- type: alert + id: CP14MagicArmor + icons: + - sprite: _CP14/Actions/Spells/meta.rsi + state: magic_armor \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Light/sphere_of_light.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Light/sphere_of_light.yml index 9d497e5564..52a6a9d0b5 100644 --- a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Light/sphere_of_light.yml +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Light/sphere_of_light.yml @@ -15,9 +15,9 @@ - !type:CP14SpellSpawnEntityOnTarget spawns: - CP14ImpactEffectSphereOfLight - - !type:CP14SpellSpawnInHandEntity - spawns: - - CP14SphereOfLight + - !type:CP14SpellApplyStatusEffect + statusEffect: CP14StatusEffectGlowing + duration: 120 - type: CP14MagicEffectVerbalAspect startSpeech: "Appare in manu tua..." endSpeech: "sphaera lucis" @@ -28,8 +28,15 @@ icon: sprite: _CP14/Actions/Spells/light.rsi state: sphere_of_light - - type: InstantAction - event: !type:CP14DelayedInstantActionEvent + - type: TargetAction + range: 5 + - type: EntityTargetAction + whitelist: + components: + - MobState + - Item + - Anchorable + event: !type:CP14DelayedEntityTargetActionEvent cooldown: 30 castDelay: 0.5 breakOnMove: false @@ -60,43 +67,6 @@ color: "#efedff" shader: unshaded -- type: entity - id: CP14SphereOfLight - name: Sphere of light - parent: BaseItem - description: A lump of bright light in the shape of a sphere. - categories: [ ForkFiltered ] - components: - - type: TimedDespawn - lifetime: 120 # 2 min - - type: Sprite - sprite: _CP14/Effects/Magic/sphere_of_light.rsi - noRot: true - layers: - - state: icon - shader: unshaded - - type: Item - size: Ginormous - inhandVisuals: - left: - - state: inhand-left-unshaded - shader: unshaded - right: - - state: inhand-right-unshaded - shader: unshaded - - type: LandAtCursor - - type: MovementIgnoreGravity - - type: PointLight - radius: 5.0 - energy: 1 - color: "#efedff" - - type: Damageable - - type: EmitSoundOnLand - sound: - path: /Audio/Effects/drop.ogg - params: - volume: -100 #Yes, it's stupid, but it's easier - - type: entity parent: CP14BaseSpellScrollLight id: CP14SpellScrollSphereOfLight diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_armor.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_armor.yml new file mode 100644 index 0000000000..5b8c6c6324 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/mana_armor.yml @@ -0,0 +1,37 @@ +- type: entity + id: CP14ActionSpellMagicArmor + parent: CP14ActionSpellBase + name: Magic Armor + description: You surround the target with a short-lived magical barrier that absorbs some of the damage it receives. + components: + - type: CP14MagicEffectCastSlowdown + speedMultiplier: 0.5 + - type: CP14MagicEffectManaCost + manaCost: 15 + - type: CP14MagicEffectCastingVisual + proto: CP14RuneManaTrance + - type: CP14MagicEffect + magicType: Fire + effects: + - !type:CP14SpellSpawnEntityOnTarget + spawns: + - CP14ImpactEffectManaGift + - !type:CP14SpellApplyStatusEffect + statusEffect: CP14StatusEffectMagicArmor + duration: 20 + - type: Action + icon: + sprite: _CP14/Actions/Spells/meta.rsi + state: magic_armor + - type: TargetAction + range: 5 + - type: EntityTargetAction + whitelist: + components: + - MobState + - Item + - Anchorable + event: !type:CP14DelayedEntityTargetActionEvent + cooldown: 10 + castDelay: 1.0 + breakOnMove: false \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/StatusEffects/misc.yml b/Resources/Prototypes/_CP14/Entities/StatusEffects/misc.yml new file mode 100644 index 0000000000..b105b49bd8 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/StatusEffects/misc.yml @@ -0,0 +1,45 @@ +- type: entity + id: CP14StatusEffectGlowing + components: + - type: StatusEffect + alert: CP14Glowing + - type: Sprite + drawdepth: Effects + sprite: _CP14/Effects/Magic/sphere_of_light.rsi + offset: 0, 1 + noRot: true + layers: + - state: icon + shader: unshaded + - type: PointLight + radius: 5.0 + energy: 1 + color: "#efedff" + +- type: entity + id: CP14StatusEffectMagicArmor + components: + - type: StatusEffect + alert: CP14MagicArmor + - type: Sprite + drawdepth: Mobs + sprite: _CP14/Effects/Magic/cast_rune.rsi + noRot: true + layers: + - state: sphere + shader: unshaded + color: "#5096d488" + - state: circle_increase + sprite: _CP14/Effects/Magic/cast_impact.rsi + shader: unshaded + color: "#74eaf222" + - type: PointLight + radius: 2.5 + energy: 0.9 + color: "#5096d4" + - type: CP14DamageModifierStatusEffect + globalDefence: 0.6 + #defence: #TODO When stone armor spell appears, divide responsibilities: magical armor protects against elemental damage, stone armor protects against physical damage. + # Cold: 0.5 + # Heat: 0.5 + # Shock: 0.5 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Skill/Basic/metamagic.yml b/Resources/Prototypes/_CP14/Skill/Basic/metamagic.yml index 247891f258..b8ca138775 100644 --- a/Resources/Prototypes/_CP14/Skill/Basic/metamagic.yml +++ b/Resources/Prototypes/_CP14/Skill/Basic/metamagic.yml @@ -153,6 +153,20 @@ - !type:NeedPrerequisite prerequisite: MetamagicT2 +- type: cp14Skill + id: CP14ActionSpellMagicArmor + skillUiPosition: 8, 4 + tree: Metamagic + icon: + sprite: _CP14/Actions/Spells/meta.rsi + state: magic_armor + effects: + - !type:AddAction + action: CP14ActionSpellMagicArmor + restrictions: + - !type:NeedPrerequisite + prerequisite: MetamagicT2 + # T3 - type: cp14Skill diff --git a/Resources/Textures/_CP14/Actions/Spells/meta.rsi/magic_armor.png b/Resources/Textures/_CP14/Actions/Spells/meta.rsi/magic_armor.png new file mode 100644 index 0000000000000000000000000000000000000000..56d6d834911bae6848061124f028186addeb0a12 GIT binary patch literal 606 zcmV-k0-^nhP)Px%7)eAyR9J=OS3PgSFc5vMIyK5j|4t^xNL8e+`2$(Ik#Y>8R5~Ll}J3H=AmvMi(jP=fr{%`tDiA7LF z$XQD}&Ih#I=Ul=70NEYBUCU$mF$qx-QgSUZf!2FC0{|#t8rW$0XBFT~d2&i{E@AQk zE$ui2@+ECZRhWr}8|+V)kte?u=MttWGJJl#U-Kl-0ZB|0G?Y{#tMxxf008{m(~g2Z zaE5&H0c{Uws!e9L9Yg}J5=*XnjhOTa+4GseUrV(DvEFO1p=7t*WTuBR5UL;&AoWtv zTSZ8%>W>bwmPO~lJ#Rohpgjx@C}Bda0hk6jVngQ^B3zJ?jaq@K$R>)u1t~*{pbjFM z1CrRJ>FBR<`+UIlTu2^76ZLu^anZA?$Z+^}ZSL8nR6-@N?H22EX$=LRk$^M&g!GdW zvHs|S2hv1@{*cHAwEfXxxyfcbp=a8>SZ*@oKZ_nll3`QZ6)$B>l)kOjdqQ*$Y`ev} zf&D!P3e0v!i6<>#$?t-6jcRJU;#V>MO4ihNrTJ-j?z)s}ZISHwdhn7OV)?9Vxm2NG s1+;*V{4w#7Oh8M-5hC`GApbOe08DkvT>GS|d;kCd07*qoM6N<$g5up4@Bjb+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Actions/Spells/meta.rsi/meta.json b/Resources/Textures/_CP14/Actions/Spells/meta.rsi/meta.json index 8ba88a282d..08186a4bf0 100644 --- a/Resources/Textures/_CP14/Actions/Spells/meta.rsi/meta.json +++ b/Resources/Textures/_CP14/Actions/Spells/meta.rsi/meta.json @@ -13,6 +13,9 @@ { "name": "counter_spell_small" }, + { + "name": "magic_armor" + }, { "name": "magic_music" }, diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json index 4044ea30de..0dd2414546 100644 --- a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json +++ b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json @@ -66,6 +66,17 @@ 0.2 ] ] + }, + { + "name": "sphere", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] } ] } diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/sphere.png b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/sphere.png new file mode 100644 index 0000000000000000000000000000000000000000..5cfa008092e5994cf793f9f67022ef603f575225 GIT binary patch literal 2185 zcmZve`9IT-1IOQdHrJY?0N^-Ftlgno4x`D(bJ!L( zVbX`<3bnH^1uFVZ{tEzn7#tSu5ShD{+Y>ToE^Ky5!Kw(WqCOguTNe5JxmYRNw8?1y zeunsOKx6i5_1_{pwX5D*-?GeWc!kmdKgReOQIp$z6F0|S`Fa)nH`A`|5ybIdYhx(V zUIn<-^Pzc%3F;)blCB;3hvdcOVcFbsELvV^Y*+ILSaY$;UT-9XgMR~pP|hP6-nr&* zK)Ewu@Nq)Elt}XN1GF;7wPy!t|bX&+2b5U;C_cjy;xy z$8dXg@KNGtGzw`t+SJC~?W-KxX6^U4uD|uA4Yo-`#g+GI?;oSV*lw#NE!Hk^rU&X3}P}QxC@7%i7RK-<2 zj%=ub6y(E?ok_y$1Y;B=c>Kb+k7R1S`d4B0RHs$=|3WN`z*2pZf77!?%?wZuwN9w^ zl1_EOs3xnp@|wVyZ6RL-YW*M&m)XAdv2#Ln5!A9@yL=~aXbwL;Q{@K`_v&7Y2nxap zGLx53PT2AzSI^$LjR)**>tTVC_d7o$tsU8jMPpuvAc;a% zPu>PS=1OQlr;!SuHi=KCHjC&i>h>ic<}(v(K{fsC37w)5xXw|V*SE&)71g*0&HLiK zrlZ*9^22>)Ej$5ozSCCo;UfJ=l4@)`gl=4_IG{6(4whk4il&vry(u5)7v<;i1OHI`qs<0mm*J zSrqV6iSY%0=(=+oXkZP!zFi#Z6*z^z;Apo!1>k38Ao9jXk3>8CeEnON8ov>OyX-cg zxv`oEp5YKr?@%`8s8D)GKqfQ(3rPzgjai5>QCy_<{yl~KeARWKPdNNrXZix@;&3Kk(Dr+{TsS!JGN=Eb2$w9Coll|FarM-@IcL0wb^S@h8hm%!2|F#x8V>3HtOdr|$ zlL}$>kR=wR#ZEX|ChbLfsrO&4LswvCQI0q8!mCj>v$te2V?Pp^3=jj7N~QKrIY~hb zm=X4aE(vPt{!9J?$!hmiI7b`Lv#*%fhGI9XZ1hHw8QzlOWw5)_cb)kVCp%Di=C?1t z?9NuMPAxJ!Qj&y6pgdSEJ81-gc(FD(PFa{`g(!EbLH=IV_j{C9WTmfkL>l={)zl{S zqPn4cBFQuiIkwd=&KCC!$tn3_+ONb!t zX14OxZBY~L z$Z}sc_dRiiDAy7Fe85V5d-|#PET$>#>H7yET~La*oN2$tz5yjJ=pVy*cRR^dHy(7a zfuc@*wP4us@;{%2<-3X&Op%{;A71+|Sck#FNzOla-PN*c)FdQ4A3`m#?y?N}Z;~(* zMzC9Xy&vYVd2n#OtzZK(_V(ia@tp^O#1C?E6MFGaFxl>{($zWN#2R3A%Lx!KlYlNC z$>_}?m7!`+#8_eNL`SQDziXXn6y9k_c5zi}pEiQyDsw~9m2+Z#1g^}6B|_>a+bYKj z{k-kMKHcoAg`YT2seYZ|;mV5ekD~R0|loO2`~RApQ#fKtPU1!B;BqunN6qB zS2AC