From 909dc361586ed69d06a2bafdbf51b37c7ba80d2b Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Fri, 14 Feb 2025 11:50:57 +0300 Subject: [PATCH] Spells content (#897) * modifiers * counter spell, and 2 new spell scrolls * Update magical_acceleration.yml * Update T0_counter_spell.yml --- .../_CP14/ModularCraft/Modifiers/EditArmor.cs | 22 ++++++ .../Modifiers/EditClothingSpeed.cs | 26 +++++++ .../Modifiers/EditManacostModify.cs | 40 ++++++++++ Content.Shared/Armor/SharedArmorSystem.cs | 26 +++++++ .../Clothing/ClothingSpeedModifierSystem.cs | 16 ++++ .../CP14MagicManacostModifyComponent.cs | 2 +- .../_CP14/MagicSpell/CP14SharedMagicSystem.cs | 18 +++++ .../Components/CP14MagicCasterComponent.cs | 11 +++ .../Spells/C14SpellInterruptSpell.cs | 39 ++++++++++ .../Spells/Healing/magical_acceleration.yml | 24 +++++- .../Actions/Spells/Meta/T0_counter_spell.yml | 73 ++++++++++++++++++ .../Spawners/Random/Loot/demiplane.yml | 2 + .../_CP14/Loadouts/Jobs/general.yml | 16 +++- .../Magic/cast_impact.rsi/circle_increase.png | Bin 0 -> 6261 bytes .../Effects/Magic/cast_impact.rsi/meta.json | 15 ++++ .../Magic/spells_icons.rsi/counter_spell.png | Bin 0 -> 527 bytes .../Effects/Magic/spells_icons.rsi/meta.json | 5 +- 17 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 Content.Server/_CP14/ModularCraft/Modifiers/EditArmor.cs create mode 100644 Content.Server/_CP14/ModularCraft/Modifiers/EditClothingSpeed.cs create mode 100644 Content.Server/_CP14/ModularCraft/Modifiers/EditManacostModify.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Components/CP14MagicCasterComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/C14SpellInterruptSpell.cs create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/T0_counter_spell.yml create mode 100644 Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/circle_increase.png create mode 100644 Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/counter_spell.png diff --git a/Content.Server/_CP14/ModularCraft/Modifiers/EditArmor.cs b/Content.Server/_CP14/ModularCraft/Modifiers/EditArmor.cs new file mode 100644 index 0000000000..a6888bfa0f --- /dev/null +++ b/Content.Server/_CP14/ModularCraft/Modifiers/EditArmor.cs @@ -0,0 +1,22 @@ +using Content.Shared._CP14.ModularCraft; +using Content.Shared._CP14.ModularCraft.Components; +using Content.Shared.Armor; +using Content.Shared.Damage; + +namespace Content.Server._CP14.ModularCraft.Modifiers; + +public sealed partial class EditArmor : CP14ModularCraftModifier +{ + [DataField(required: true)] + public DamageModifierSet Modifiers = new(); + + public override void Effect(EntityManager entManager, Entity start, Entity? part) + { + if (!entManager.TryGetComponent(start, out var armor)) + return; + + var armorSystem = entManager.System(); + + armorSystem.EditArmorCoefficients(start, Modifiers, armor); + } +} diff --git a/Content.Server/_CP14/ModularCraft/Modifiers/EditClothingSpeed.cs b/Content.Server/_CP14/ModularCraft/Modifiers/EditClothingSpeed.cs new file mode 100644 index 0000000000..6fdc67d07e --- /dev/null +++ b/Content.Server/_CP14/ModularCraft/Modifiers/EditClothingSpeed.cs @@ -0,0 +1,26 @@ +using Content.Shared._CP14.ModularCraft; +using Content.Shared._CP14.ModularCraft.Components; +using Content.Shared.Armor; +using Content.Shared.Clothing; +using Content.Shared.Damage; + +namespace Content.Server._CP14.ModularCraft.Modifiers; + +public sealed partial class EditClothingSpeed : CP14ModularCraftModifier +{ + [DataField] + public float WalkModifier = 1f; + + [DataField] + public float SprintModifier = 1f; + + public override void Effect(EntityManager entManager, Entity start, Entity? part) + { + if (!entManager.TryGetComponent(start, out var speed)) + return; + + var speedModifierSystem = entManager.System(); + + speedModifierSystem.EditSpeedModifiers(start, WalkModifier, SprintModifier, speed); + } +} diff --git a/Content.Server/_CP14/ModularCraft/Modifiers/EditManacostModify.cs b/Content.Server/_CP14/ModularCraft/Modifiers/EditManacostModify.cs new file mode 100644 index 0000000000..7864826922 --- /dev/null +++ b/Content.Server/_CP14/ModularCraft/Modifiers/EditManacostModify.cs @@ -0,0 +1,40 @@ +using Content.Shared._CP14.ModularCraft; +using Content.Shared._CP14.ModularCraft.Components; +using Content.Shared._CP14.MagicManacostModify; +using Content.Shared._CP14.MagicRitual.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; + +namespace Content.Server._CP14.ModularCraft.Modifiers; + +public sealed partial class EditManacostModify : CP14ModularCraftModifier +{ + [DataField] + public Dictionary, FixedPoint2> Modifiers = new(); + + [DataField] + public FixedPoint2 GlobalModifier = 1f; + + public override void Effect(EntityManager entManager, Entity start, Entity? part) + { + if (!entManager.TryGetComponent(start, out var manacostModifyComp)) + return; + + foreach (var (magicType, modifier) in Modifiers) + { + if (manacostModifyComp.Modifiers.ContainsKey(magicType)) + { + if (modifier >= 1f) + manacostModifyComp.Modifiers[magicType] += modifier - 1f; + else + manacostModifyComp.Modifiers[magicType] -= 1f - modifier; + } + else + { + manacostModifyComp.Modifiers[magicType] = modifier; + } + } + + manacostModifyComp.GlobalModifier += GlobalModifier; + } +} diff --git a/Content.Shared/Armor/SharedArmorSystem.cs b/Content.Shared/Armor/SharedArmorSystem.cs index 010ee5e65b..55cb39397d 100644 --- a/Content.Shared/Armor/SharedArmorSystem.cs +++ b/Content.Shared/Armor/SharedArmorSystem.cs @@ -79,4 +79,30 @@ public abstract class SharedArmorSystem : EntitySystem return msg; } + + //CP14 public armor edit API + public void EditArmorCoefficients(EntityUid uid, DamageModifierSet modifiers, ArmorComponent? armor = null) + { + if (!Resolve(uid, ref armor)) + return; + + //Merge old and new coefficients + foreach (var (armorType, coefficient) in modifiers.Coefficients) + { + if (armor.Modifiers.Coefficients.ContainsKey(armorType)) + armor.Modifiers.Coefficients[armorType] += coefficient; + else + armor.Modifiers.Coefficients[armorType] = coefficient; + } + + //Merge old and new flat reductions + foreach (var (armorType, reduction) in modifiers.FlatReduction) + { + if (armor.Modifiers.FlatReduction.ContainsKey(armorType)) + armor.Modifiers.FlatReduction[armorType] += reduction; + else + armor.Modifiers.FlatReduction[armorType] = reduction; + } + } + //CP14 public armor edit API end } diff --git a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs index 56758654ed..8673e856a1 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs @@ -116,4 +116,20 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); } } + + //CP14 - Public API for edit component + public void EditSpeedModifiers(EntityUid uid, float walkModifier, float sprintModifier, ClothingSpeedModifierComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.WalkModifier += walkModifier; + component.SprintModifier += sprintModifier; + + if (_container.TryGetContainingContainer((uid, null, null), out var container)) + { + _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); + } + } + //CP14 - Public API for edit component end } diff --git a/Content.Shared/_CP14/MagicManacostModify/CP14MagicManacostModifyComponent.cs b/Content.Shared/_CP14/MagicManacostModify/CP14MagicManacostModifyComponent.cs index 688957520e..26c325c8bb 100644 --- a/Content.Shared/_CP14/MagicManacostModify/CP14MagicManacostModifyComponent.cs +++ b/Content.Shared/_CP14/MagicManacostModify/CP14MagicManacostModifyComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared._CP14.MagicManacostModify; /// /// Changes the manacost of spells for the bearer /// -[RegisterComponent, Access(typeof(CP14MagicManacostModifySystem))] +[RegisterComponent] public sealed partial class CP14MagicManacostModifyComponent : Component { [DataField] diff --git a/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs b/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs index 9064f18faa..85575b0d8a 100644 --- a/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs +++ b/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs @@ -52,9 +52,27 @@ public abstract partial class CP14SharedMagicSystem : EntitySystem SubscribeLocalEvent(OnMagicEffectInit); SubscribeLocalEvent(OnMagicEffectShutdown); + SubscribeLocalEvent(OnStartCast); + SubscribeLocalEvent(OnEndCast); + SubscribeLocalEvent(OnStaminaConsume); } + private void OnStartCast(Entity ent, ref CP14StartCastMagicEffectEvent args) + { + var caster = EnsureComp(args.Performer); + + caster.CastedSpells.Add(ent); + } + + private void OnEndCast(Entity ent, ref CP14EndCastMagicEffectEvent args) + { + if (TryComp(args.Performer, out var caster)) + { + caster.CastedSpells.Remove(ent); + } + } + public override void Update(float frameTime) { base.Update(frameTime); diff --git a/Content.Shared/_CP14/MagicSpell/Components/CP14MagicCasterComponent.cs b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicCasterComponent.cs new file mode 100644 index 0000000000..bb73fec3d1 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicCasterComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared._CP14.MagicSpell.Components; + +/// +/// +/// +[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] +public sealed partial class CP14MagicCasterComponent : Component +{ + [DataField] + public List CastedSpells = new(); +} diff --git a/Content.Shared/_CP14/MagicSpell/Spells/C14SpellInterruptSpell.cs b/Content.Shared/_CP14/MagicSpell/Spells/C14SpellInterruptSpell.cs new file mode 100644 index 0000000000..d8ab0f5567 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/C14SpellInterruptSpell.cs @@ -0,0 +1,39 @@ +using Content.Shared._CP14.MagicSpell.Components; +using Content.Shared.Electrocution; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellInterruptSpell : CP14SpellEffect +{ + [DataField] + public TimeSpan Duration = TimeSpan.FromSeconds(5); + + [DataField] + public int Damage = 10; + + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + if (args.Target is null) + return; + + if (!entManager.TryGetComponent(args.Target.Value, out var caster)) + return; + + var interrupt = false; + foreach (var spell in caster.CastedSpells) + { + if (entManager.HasComponent(spell)) + { + interrupt = true; + break; + } + } + + if (!interrupt) + return; + + var electrocutionSystem = entManager.System(); + + electrocutionSystem.TryDoElectrocution(args.Target.Value, args.User, Damage, Duration, true, ignoreInsulation: true ); + } +} diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Healing/magical_acceleration.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Healing/magical_acceleration.yml index 142ac1f7a7..3b8befd4f9 100644 --- a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Healing/magical_acceleration.yml +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Healing/magical_acceleration.yml @@ -41,4 +41,26 @@ shader: unshaded color: "#4097bd" - type: PointLight - color: "#4097bd" \ No newline at end of file + color: "#4097bd" + +- type: entity + parent: CP14BaseSpellScrollHealing + id: CP14SpellScrollMagicalAcceleration + name: magical accseleration spell scroll + components: + - type: CP14SpellStorage + spells: + - CP14ActionSpellMagicalAcceleration + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + CP14Ash1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/T0_counter_spell.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/T0_counter_spell.yml new file mode 100644 index 0000000000..6fd4dc5a73 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Meta/T0_counter_spell.yml @@ -0,0 +1,73 @@ +- type: entity + id: CP14ActionSpellCounterSpell + name: counter spell + description: By affecting the magical energy itself, you can interrupt the cast of someone else's spell. + components: + - type: Sprite + sprite: _CP14/Effects/Magic/spells_icons.rsi + state: counter_spell + - type: CP14MagicEffectCastSlowdown + speedMultiplier: 0.7 + - type: CP14MagicEffectManaCost + manaCost: 15 + - type: CP14MagicEffect + magicType: Meta + telegraphyEffects: + - !type:CP14SpellSpawnEntityOnTarget + spawns: + - CP14ImpactEffectCounterSpell + effects: + - !type:CP14SpellInterruptSpell + - type: CP14MagicEffectCastingVisual + proto: CP14RuneCounterSpell + - type: EntityTargetAction + whitelist: + components: + - CP14MagicEnergyContainer + itemIconStyle: BigAction + interactOnMiss: false + sound: !type:SoundPathSpecifier + path: /Audio/Magic/rumble.ogg + icon: + sprite: _CP14/Effects/Magic/spells_icons.rsi + state: counter_spell + event: !type:CP14DelayedEntityTargetActionEvent + cooldown: 10 + castDelay: 0.75 + breakOnMove: false + +- type: entity + id: CP14RuneCounterSpell + parent: CP14BaseMagicRune + categories: [ HideSpawnMenu ] + components: + - type: PointLight + color: "#5096d4" + - type: Sprite + layers: + - state: medium_circle + color: "#5096d4" + shader: unshaded + +- type: entity + id: CP14ImpactEffectCounterSpell + parent: CP14BaseMagicImpact + categories: [ HideSpawnMenu ] + components: + - type: Sprite + layers: + - state: circle_decrease + color: "#5096d4" + shader: unshaded + - state: circle_increase + color: "#5096d4" + shader: unshaded + +- type: entity + parent: CP14BaseSpellScrollMeta + id: CP14SpellScrollCounterSpell + name: counter spell spell scroll #Spell spell spell spell + components: + - type: CP14SpellStorage + spells: + - CP14ActionSpellCounterSpell \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Loot/demiplane.yml b/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Loot/demiplane.yml index ce0aee38ad..e51e6dc0e1 100644 --- a/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Loot/demiplane.yml +++ b/Resources/Prototypes/_CP14/Entities/Markers/Spawners/Random/Loot/demiplane.yml @@ -37,6 +37,8 @@ - id: CP14SpellScrollFlashLight - id: CP14SpellScrollWaterCreation - id: CP14SpellScrollPlantGrowth + - id: CP14SpellScrollCounterSpell + - id: CP14ActionSpellMagicalAcceleration - id: CP14EnergyCrystalSmallEmpty - id: CP14BaseSharpeningStone - id: CP14ScrapCopper diff --git a/Resources/Prototypes/_CP14/Loadouts/Jobs/general.yml b/Resources/Prototypes/_CP14/Loadouts/Jobs/general.yml index e2831ad891..808d0d7849 100644 --- a/Resources/Prototypes/_CP14/Loadouts/Jobs/general.yml +++ b/Resources/Prototypes/_CP14/Loadouts/Jobs/general.yml @@ -590,7 +590,7 @@ id: CP14GeneralSpells name: cp14-loadout-general-spells minLimit: 0 - maxLimit: 2 + maxLimit: 3 loadouts: - CP14ActionSpellFlameCreation - CP14ActionSpellCureWounds @@ -600,6 +600,7 @@ - CP14ActionSpellSphereOfLight - CP14ActionSpellManaConsume - CP14ActionSpellManaGift + - CP14ActionSpellCounterSpell - CP14ActionSpellShadowGrab - CP14ActionSpellWaterCreation - CP14ActionSpellFreeze @@ -772,6 +773,19 @@ traits: - CP14ManaWasting +- type: loadout + id: CP14ActionSpellCounterSpell + dummyEntity: CP14ActionSpellCounterSpell + actions: + - CP14ActionSpellCounterSpell + effects: + - !type:JobRequirementLoadoutEffect + requirement: + !type:TraitsRequirement + inverted: true + traits: + - CP14ManaWasting + - type: loadout id: CP14ActionSpellBeerCreation dummyEntity: CP14ActionSpellBeerCreation diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/circle_increase.png b/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/circle_increase.png new file mode 100644 index 0000000000000000000000000000000000000000..caade16d0c7297169de08a8f9de2fceb49ab54b7 GIT binary patch literal 6261 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