From 56da23925f756a720cd81c1dd87cdb7bc68f574b Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:52:27 +0300 Subject: [PATCH] Magic Items (#370) * attuning to items start * verbs attuning, forgot oldest attuning * Update CP14SharedMagicAttuningSystem.cs * some fixes * add deattuning * fix popups on owner * file restructurization * MORE FOLDER RENAMING GODD * magic via holding yea! * fix deattuning * vfx bugfixs * wearing magic items * Cure wounds spell * refactor spells * more redo * finish refactor --- .../MagicEnergy/CP14MagicEnergySystem.Draw.cs | 1 - .../{Magic => MagicSpell}/CP14MagicSystem.cs | 11 +- .../Magic/CP14SharedMagicSystem.Spells.cs | 130 ---------- ...DelayedApplyEntityEffectsSpellComponent.cs | 13 - .../CP14DelayedProjectileSpellComponent.cs | 16 -- ...14DelayedSelfEntityEffectSpellComponent.cs | 13 - .../CP14DelayedSpawnEntitiesSpellComponent.cs | 16 -- ...DelayedSpawnOnWorldTargetSpellComponent.cs | 16 -- .../CP14MagicAttuningItemComponent.cs | 16 ++ .../CP14MagicAttuningMindComponent.cs | 22 ++ .../CP14SharedMagicAttuningSystem.cs | 235 ++++++++++++++++++ .../CP14SharedMagicSystem.cs | 70 +++++- .../CP14MagicEffectCastingVisualComponent.cs | 2 +- .../Components/CP14MagicEffectComponent.cs | 6 +- .../CP14MagicEffectSomaticAspectComponent.cs | 2 +- .../CP14MagicEffectVerbalAspectComponent.cs | 4 +- .../Events/CP14CastMagicEffectEvent.cs | 2 +- .../Events/CP14DelayedActionEvents.cs | 2 +- .../Events}/CP14IDelayedMagicEffect.cs | 2 +- .../Spells/CP14SpellApplyEntityEffect.cs | 22 ++ .../MagicSpell/Spells/CP14SpellEffect.cs | 25 ++ .../MagicSpell/Spells/CP14SpellProjectile.cs | 52 ++++ .../MagicSpell/Spells/CP14SpellSpawnEntity.cs | 27 ++ .../CP14SpellStorageAccessHoldingComponent.cs | 9 + .../CP14SpellStorageAccessWearingComponent.cs | 9 + .../CP14SpellStorageComponent.cs | 22 ++ .../CP14SpellStorageRequireAttuneComponent.cs | 9 + .../CP14SpellStorageSystem.cs | 109 ++++++++ .../en-US/_CP14/magicEnergy/magic-focus.ftl | 7 + .../ru-RU/_CP14/magicEnergy/magic-focus.ftl | 7 + .../Entities/Actions/Spells/cure_wounds.yml | 62 +++++ .../_CP14/Entities/Actions/test_spell.yml | 153 ------------ .../_CP14/Entities/Effects/cast_vfx.yml | 26 +- .../Prototypes/_CP14/Entities/FARMINGTEST.yml | 9 +- .../_CP14/Entities/Mobs/Species/base.yml | 6 +- Resources/Prototypes/_CP14/Maps/debug.yml | 10 +- .../Effects/Magic/cast_impact.rsi/meta.json | 26 ++ .../Magic/cast_impact.rsi/particles_up.png | Bin 0 -> 2872 bytes .../Magic/spells_icons.rsi/cure_wounds.png | Bin 0 -> 1754 bytes .../Effects/Magic/spells_icons.rsi/meta.json | 14 ++ 40 files changed, 787 insertions(+), 396 deletions(-) rename Content.Server/_CP14/{Magic => MagicSpell}/CP14MagicSystem.cs (80%) delete mode 100644 Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs delete mode 100644 Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs delete mode 100644 Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedProjectileSpellComponent.cs delete mode 100644 Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSelfEntityEffectSpellComponent.cs delete mode 100644 Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnEntitiesSpellComponent.cs delete mode 100644 Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnOnWorldTargetSpellComponent.cs create mode 100644 Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningItemComponent.cs create mode 100644 Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningMindComponent.cs create mode 100644 Content.Shared/_CP14/MagicAttuning/CP14SharedMagicAttuningSystem.cs rename Content.Shared/_CP14/{Magic => MagicSpell}/CP14SharedMagicSystem.cs (74%) rename Content.Shared/_CP14/{Magic => MagicSpell}/Components/CP14MagicEffectCastingVisualComponent.cs (89%) rename Content.Shared/_CP14/{Magic => MagicSpell}/Components/CP14MagicEffectComponent.cs (68%) rename Content.Shared/_CP14/{Magic => MagicSpell}/Components/CP14MagicEffectSomaticAspectComponent.cs (81%) rename Content.Shared/_CP14/{Magic => MagicSpell}/Components/CP14MagicEffectVerbalAspectComponent.cs (89%) rename Content.Shared/_CP14/{Magic => MagicSpell}/Events/CP14CastMagicEffectEvent.cs (95%) rename Content.Shared/_CP14/{Magic => MagicSpell}/Events/CP14DelayedActionEvents.cs (97%) rename Content.Shared/_CP14/{Magic => MagicSpell/Events}/CP14IDelayedMagicEffect.cs (85%) create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyEntityEffect.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellEffect.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellProjectile.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellSpawnEntity.cs create mode 100644 Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessHoldingComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessWearingComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageRequireAttuneComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageSystem.cs create mode 100644 Resources/Locale/en-US/_CP14/magicEnergy/magic-focus.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/magicEnergy/magic-focus.ftl create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/cure_wounds.yml delete mode 100644 Resources/Prototypes/_CP14/Entities/Actions/test_spell.yml create mode 100644 Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/meta.json create mode 100644 Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/particles_up.png create mode 100644 Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/cure_wounds.png create mode 100644 Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/meta.json diff --git a/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.Draw.cs b/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.Draw.cs index c4383465a3..5622ab0a79 100644 --- a/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.Draw.cs +++ b/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.Draw.cs @@ -10,7 +10,6 @@ public partial class CP14MagicEnergySystem { SubscribeLocalEvent(OnDrawMapInit); SubscribeLocalEvent(OnRandomRangeMapInit); - } private void OnRandomRangeMapInit(Entity random, ref MapInitEvent args) diff --git a/Content.Server/_CP14/Magic/CP14MagicSystem.cs b/Content.Server/_CP14/MagicSpell/CP14MagicSystem.cs similarity index 80% rename from Content.Server/_CP14/Magic/CP14MagicSystem.cs rename to Content.Server/_CP14/MagicSpell/CP14MagicSystem.cs index f0ee72f2d7..35185dbe42 100644 --- a/Content.Server/_CP14/Magic/CP14MagicSystem.cs +++ b/Content.Server/_CP14/MagicSpell/CP14MagicSystem.cs @@ -1,13 +1,15 @@ using Content.Server.Chat.Systems; -using Content.Shared._CP14.Magic; -using Content.Shared._CP14.Magic.Components; -using Content.Shared._CP14.Magic.Events; +using Content.Shared._CP14.MagicSpell; +using Content.Shared._CP14.MagicSpell.Components; +using Content.Shared._CP14.MagicSpell.Events; +using Robust.Server.GameObjects; -namespace Content.Server._CP14.Magic; +namespace Content.Server._CP14.MagicSpell; public sealed partial class CP14MagicSystem : CP14SharedMagicSystem { [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -26,6 +28,7 @@ public sealed partial class CP14MagicSystem : CP14SharedMagicSystem private void OnSpawnMagicVisualEffect(Entity ent, ref CP14StartCastMagicEffectEvent args) { var vfx = SpawnAttachedTo(ent.Comp.Proto, Transform(args.Performer).Coordinates); + _transform.SetParent(vfx, args.Performer); ent.Comp.SpawnedEntity = vfx; } diff --git a/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs b/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs deleted file mode 100644 index f330e17de2..0000000000 --- a/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Content.Shared._CP14.Magic.Components.Spells; -using Content.Shared._CP14.Magic.Events; -using Content.Shared.EntityEffects; - -namespace Content.Shared._CP14.Magic; - -public partial class CP14SharedMagicSystem -{ - private void InitializeSpells() - { - // Instants - SubscribeLocalEvent(OnCastEntitiesSpawn); - SubscribeLocalEvent(OnCastSelfEntityEffects); - //Entity Target - SubscribeLocalEvent(OnCastApplyEntityEffects); - //World Target - SubscribeLocalEvent(OnCastProjectileSpell); - SubscribeLocalEvent(OnCastSpawnOnPoint); - } - - //TODO: Fuck,there's a lot of code repetition here that needs to be squeezed together somehow. Event calls, checks, and all this stuff - private void OnCastEntitiesSpawn(Entity spell, ref CP14DelayedInstantActionDoAfterEvent args) - { - var stopEv = new CP14StopCastMagicEffectEvent(); - RaiseLocalEvent(spell, ref stopEv); - - if (args.Cancelled || args.Handled || !_net.IsServer) - return; - - args.Handled = true; - - foreach (var spawn in spell.Comp.Spawns) - { - SpawnAtPosition(spawn, Transform(args.User).Coordinates); - } - - var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; - RaiseLocalEvent(spell, ref ev); - } - - private void OnCastSelfEntityEffects(Entity spell, ref CP14DelayedInstantActionDoAfterEvent args) - { - var stopEv = new CP14StopCastMagicEffectEvent(); - RaiseLocalEvent(spell, ref stopEv); - - if (args.Cancelled || args.Handled) - return; - - args.Handled = true; - - foreach (var effect in spell.Comp.Effects) - { - effect.Effect(new EntityEffectBaseArgs(args.User, EntityManager)); - } - - var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; - RaiseLocalEvent(spell, ref ev); - } - - private void OnCastApplyEntityEffects(Entity spell, ref CP14DelayedEntityTargetActionDoAfterEvent args) - { - var stopEv = new CP14StopCastMagicEffectEvent(); - RaiseLocalEvent(spell, ref stopEv); - - if (args.Cancelled || args.Handled || args.Target == null) - return; - - args.Handled = true; - - foreach (var effect in spell.Comp.Effects) - { - effect.Effect(new EntityEffectBaseArgs(args.Target.Value, EntityManager)); - } - - var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; - RaiseLocalEvent(spell, ref ev); - } - - private void OnCastProjectileSpell(Entity spell, ref CP14DelayedWorldTargetActionDoAfterEvent args) - { - var stopEv = new CP14StopCastMagicEffectEvent(); - RaiseLocalEvent(spell, ref stopEv); - - if (args.Cancelled || args.Handled || !_net.IsServer) - return; - - args.Handled = true; - - var xform = Transform(args.User); - var fromCoords = xform.Coordinates; - var toCoords = GetCoordinates(args.Target); - var userVelocity = _physics.GetMapLinearVelocity(args.User); - - // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. - var fromMap = fromCoords.ToMap(EntityManager, _transform); - var spawnCoords = _mapManager.TryFindGridAt(fromMap, out var gridUid, out _) - ? fromCoords.WithEntityId(gridUid, EntityManager) - : new(_mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); - - var ent = Spawn(spell.Comp.Prototype, spawnCoords); - var direction = toCoords.ToMapPos(EntityManager, _transform) - - spawnCoords.ToMapPos(EntityManager, _transform); - _gunSystem.ShootProjectile(ent, direction, userVelocity, args.User, args.User); - - var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; - RaiseLocalEvent(spell, ref ev); - } - - private void OnCastSpawnOnPoint(Entity spell, ref CP14DelayedWorldTargetActionDoAfterEvent args) - { - var stopEv = new CP14StopCastMagicEffectEvent(); - RaiseLocalEvent(spell, ref stopEv); - - if (args.Cancelled || args.Handled || !_net.IsServer) - return; - - args.Handled = true; - - var xform = Transform(args.User); - var toCoords = GetCoordinates(args.Target); - - foreach (var spawn in spell.Comp.Spawns) - { - SpawnAtPosition(spawn, toCoords); - } - - var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; - RaiseLocalEvent(spell, ref ev); - } -} diff --git a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs deleted file mode 100644 index 619efcb9aa..0000000000 --- a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Content.Shared.EntityEffects; - -namespace Content.Shared._CP14.Magic.Components.Spells; - -/// -/// Stores a list of effects for delayed actions. -/// -[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] -public sealed partial class CP14DelayedApplyEntityEffectsSpellComponent : Component -{ - [DataField(required: true, serverOnly: true)] - public List Effects = new(); -} diff --git a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedProjectileSpellComponent.cs b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedProjectileSpellComponent.cs deleted file mode 100644 index 7a47ea0573..0000000000 --- a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedProjectileSpellComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Shared._CP14.Magic.Components.Spells; - -/// -/// Stores a list of effects for delayed actions. -/// -[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] -public sealed partial class CP14DelayedProjectileSpellComponent : Component -{ - /// - /// What entity should be spawned. - /// - [DataField(required: true)] - public EntProtoId Prototype; -} diff --git a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSelfEntityEffectSpellComponent.cs b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSelfEntityEffectSpellComponent.cs deleted file mode 100644 index a8b1396913..0000000000 --- a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSelfEntityEffectSpellComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Content.Shared.EntityEffects; - -namespace Content.Shared._CP14.Magic.Components.Spells; - -/// -/// Stores a list of effects for delayed actions. -/// -[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] -public sealed partial class CP14DelayedSelfEntityEffectSpellComponent : Component -{ - [DataField(required: true, serverOnly: true)] - public List Effects = new(); -} diff --git a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnEntitiesSpellComponent.cs b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnEntitiesSpellComponent.cs deleted file mode 100644 index 3172c995c8..0000000000 --- a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnEntitiesSpellComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Shared._CP14.Magic.Components.Spells; - -/// -/// Stores a list of effects for delayed actions. -/// -[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] -public sealed partial class CP14DelayedSpawnEntitiesSpellComponent : Component -{ - /// - /// What entities should be spawned. - /// - [DataField(required: true)] - public HashSet Spawns = new(); -} diff --git a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnOnWorldTargetSpellComponent.cs b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnOnWorldTargetSpellComponent.cs deleted file mode 100644 index 992a3a02ef..0000000000 --- a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnOnWorldTargetSpellComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Shared._CP14.Magic.Components.Spells; - -/// -/// Stores a list of effects for delayed actions. -/// -[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] -public sealed partial class CP14DelayedSpawnOnWorldTargetSpellComponent : Component -{ - /// - /// What entities should be spawned. - /// - [DataField(required: true)] - public HashSet Spawns = new(); -} diff --git a/Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningItemComponent.cs b/Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningItemComponent.cs new file mode 100644 index 0000000000..918062e73e --- /dev/null +++ b/Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningItemComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Shared._CP14.MagicAttuning; + +/// +/// Reflects the fact that this subject can be focused on (Magical attune as a mechanic from DnD.) +/// +[RegisterComponent, Access(typeof(CP14SharedMagicAttuningSystem))] +public sealed partial class CP14MagicAttuningItemComponent : Component +{ + /// + /// how long it takes to focus on that object + /// + [DataField] + public TimeSpan FocusTime = TimeSpan.FromSeconds(5f); + + public Entity? Link = null; +} diff --git a/Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningMindComponent.cs b/Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningMindComponent.cs new file mode 100644 index 0000000000..9fa7175bf9 --- /dev/null +++ b/Content.Shared/_CP14/MagicAttuning/CP14MagicAttuningMindComponent.cs @@ -0,0 +1,22 @@ +namespace Content.Shared._CP14.MagicAttuning; + +/// +/// A mind that can focus on objects +/// +[RegisterComponent, Access(typeof(CP14SharedMagicAttuningSystem))] +public sealed partial class CP14MagicAttuningMindComponent : Component +{ + [DataField] + public int MaxAttuning = 3; + /// + /// The entities that this being is focused on + /// + [DataField] + public List AttunedTo = new(); + + /// + /// cheat: if added to an entity with MindContainer, automatically copied to the mind, removing it from the body. This is to make it easy to add the component to prototype creatures. + /// + [DataField] + public bool AutoCopyToMind = false; +} diff --git a/Content.Shared/_CP14/MagicAttuning/CP14SharedMagicAttuningSystem.cs b/Content.Shared/_CP14/MagicAttuning/CP14SharedMagicAttuningSystem.cs new file mode 100644 index 0000000000..f6cddb1ec9 --- /dev/null +++ b/Content.Shared/_CP14/MagicAttuning/CP14SharedMagicAttuningSystem.cs @@ -0,0 +1,235 @@ +using Content.Shared.DoAfter; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Serialization; + +namespace Content.Shared._CP14.MagicAttuning; + +/// +/// This system controls the customization to magic items by the players. +/// +public sealed partial class CP14SharedMagicAttuningSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnInteractionVerb); + SubscribeLocalEvent(OnAttuneDoAfter); + SubscribeLocalEvent(OnMindAdded); + } + + private void OnMindAdded(Entity ent, ref MindAddedMessage args) + { + if (!ent.Comp.AutoCopyToMind) + return; + + if (HasComp(ent)) + return; + + if (!_mind.TryGetMind(ent, out var mindId, out var mind)) + return; + + var attuneMind = AddComp(mindId); + attuneMind.MaxAttuning = ent.Comp.MaxAttuning; + } + + public bool IsAttunedTo(EntityUid mind, EntityUid item) + { + if (!TryComp(item, out var attuningItem)) + return false; + + if (!TryComp(mind, out var attuningMind)) + return false; + + return attuningMind.AttunedTo.Contains(item); + } + + private void OnInteractionVerb(Entity attuningItem, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (!_mind.TryGetMind(args.User, out var mindId, out var mind)) + return; + + if (!TryComp(mindId, out var attumingMind)) + return; + + var user = args.User; + if (attumingMind.AttunedTo.Contains(args.Target)) + { + args.Verbs.Add(new() + { + Act = () => + { + RemoveAttune((mindId, attumingMind), attuningItem); + }, + Text = Loc.GetString("cp14-magic-deattuning-verb-text"), + Message = Loc.GetString("cp14-magic-attuning-verb-message"), + }); + } + else + { + args.Verbs.Add(new() + { + Act = () => + { + TryStartAttune(user, attuningItem); + }, + Text = Loc.GetString("cp14-magic-attuning-verb-text"), + Message = Loc.GetString("cp14-magic-attuning-verb-message"), + }); + } + } + + public bool TryStartAttune(EntityUid user, Entity item) + { + if (!_mind.TryGetMind(user, out var mindId, out var mind)) + return false; + + if (!TryComp(mindId, out var attuningMind)) + return false; + + if (attuningMind.MaxAttuning <= 0) + return false; + + //if there's an overabundance of ties, we report that the oldest one is torn. + if (attuningMind.AttunedTo.Count >= attuningMind.MaxAttuning) + { + var oldestAttune = attuningMind.AttunedTo[0]; + _popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot", ("item", MetaData(oldestAttune).EntityName)), user, user); + } + + //we notify the current owner of the item that someone is cutting ties. + if (item.Comp.Link is not null && + item.Comp.Link.Value.Owner != mindId && + TryComp(item.Comp.Link.Value.Owner, out var ownerMind) && + ownerMind.OwnedEntity is not null) + { + _popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot", ("item", MetaData(item).EntityName)), ownerMind.OwnedEntity.Value, ownerMind.OwnedEntity.Value); + } + + var doAfterArgs = new DoAfterArgs(EntityManager, + user, + item.Comp.FocusTime, + new CP14MagicAttuneDoAfterEvent(), + mindId, + item) + { + BreakOnDamage = true, + BreakOnMove = true, + DistanceThreshold = 2f, + BlockDuplicate = true, + }; + + _doAfter.TryStartDoAfter(doAfterArgs); + return true; + } + + private void OnAttuneDoAfter(Entity ent, ref CP14MagicAttuneDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Target is null) + return; + + if (ent.Comp.AttunedTo.Count >= ent.Comp.MaxAttuning) + { + var oldestAttune = ent.Comp.AttunedTo[0]; + RemoveAttune(ent, oldestAttune); + } + + AddAttune(ent, args.Target.Value); + } + + private void RemoveAttune(Entity attuningMind, EntityUid item) + { + if (!attuningMind.Comp.AttunedTo.Contains(item)) + return; + + attuningMind.Comp.AttunedTo.Remove(item); + + if (!TryComp(item, out var attuningItem)) + return; + + if (!TryComp(attuningMind, out var mind)) + return; + + attuningItem.Link = null; + + var ev = new RemovedAttuneFromMindEvent(attuningMind, mind.OwnedEntity, item); + RaiseLocalEvent(attuningMind, ev); + RaiseLocalEvent(item, ev); + + if (mind.OwnedEntity is not null) + { + _popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot-end", ("item", MetaData(item).EntityName)), mind.OwnedEntity.Value, mind.OwnedEntity.Value); + } + } + + private void AddAttune(Entity attuningMind, EntityUid item) + { + if (attuningMind.Comp.AttunedTo.Contains(item)) + return; + + if (!TryComp(item, out var attuningItem)) + return; + + if (!TryComp(attuningMind, out var mind)) + return; + + if (attuningItem.Link is not null) + RemoveAttune(attuningItem.Link.Value, item); + + attuningMind.Comp.AttunedTo.Add(item); + attuningItem.Link = attuningMind; + + + var ev = new AddedAttuneToMindEvent(attuningMind, mind.OwnedEntity, item); + RaiseLocalEvent(attuningMind, ev); + RaiseLocalEvent(item, ev); + } +} + +[Serializable, NetSerializable] +public sealed partial class CP14MagicAttuneDoAfterEvent : SimpleDoAfterEvent +{ +} + +/// +/// is evoked on both the item and the mind when a new connection between them appears. +/// +public sealed class AddedAttuneToMindEvent : EntityEventArgs +{ + public readonly EntityUid Mind; + public readonly EntityUid? User; + public readonly EntityUid Item; + + public AddedAttuneToMindEvent(EntityUid mind, EntityUid? user, EntityUid item) + { + Mind = mind; + User = user; + Item = item; + } +} +/// +/// is evoked on both the item and the mind when the connection is broken +/// +public sealed class RemovedAttuneFromMindEvent : EntityEventArgs +{ + public readonly EntityUid Mind; + public readonly EntityUid? User; + public readonly EntityUid Item; + + public RemovedAttuneFromMindEvent(EntityUid mind, EntityUid? user, EntityUid item) + { + Mind = mind; + User = user; + Item = item; + } +} diff --git a/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.cs b/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs similarity index 74% rename from Content.Shared/_CP14/Magic/CP14SharedMagicSystem.cs rename to Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs index 3a0b8d922b..ce0d8f0aba 100644 --- a/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.cs +++ b/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.cs @@ -1,10 +1,10 @@ -using Content.Shared._CP14.Magic.Components; -using Content.Shared._CP14.Magic.Events; using Content.Shared._CP14.MagicEnergy; using Content.Shared._CP14.MagicEnergy.Components; +using Content.Shared._CP14.MagicSpell.Components; +using Content.Shared._CP14.MagicSpell.Events; +using Content.Shared._CP14.MagicSpell.Spells; using Content.Shared.DoAfter; using Content.Shared.Hands.Components; -using Content.Shared.Hands.EntitySystems; using Content.Shared.Popups; using Content.Shared.Speech.Muting; using Content.Shared.Weapons.Ranged.Systems; @@ -13,8 +13,11 @@ using Robust.Shared.Network; using Robust.Shared.Physics.Systems; using Robust.Shared.Random; -namespace Content.Shared._CP14.Magic; +namespace Content.Shared._CP14.MagicSpell; +/// +/// This system handles the basic mechanics of spell use, such as doAfter, event invocation, and energy spending. +/// public partial class CP14SharedMagicSystem : EntitySystem { [Dependency] private readonly SharedPhysicsSystem _physics = default!; @@ -26,7 +29,6 @@ public partial class CP14SharedMagicSystem : EntitySystem [Dependency] private readonly SharedCP14MagicEnergySystem _magicEnergy = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; public override void Initialize() { @@ -34,6 +36,10 @@ public partial class CP14SharedMagicSystem : EntitySystem SubscribeLocalEvent(OnBeforeCastMagicEffect); + SubscribeLocalEvent(OnDelayedInstantActionDoAfter); + SubscribeLocalEvent(OnDelayedEntityTargetDoAfter); + SubscribeLocalEvent(OnDelayedWorldTargetDoAfter); + SubscribeLocalEvent(OnSomaticAspectBeforeCast); SubscribeLocalEvent(OnVerbalAspectBeforeCast); @@ -44,8 +50,57 @@ public partial class CP14SharedMagicSystem : EntitySystem SubscribeLocalEvent(OnInstantAction); SubscribeLocalEvent(OnEntityTargetAction); SubscribeLocalEvent(OnWorldTargetAction); + } - InitializeSpells(); + private void OnDelayedWorldTargetDoAfter(Entity ent, ref CP14DelayedWorldTargetActionDoAfterEvent args) + { + var stopEv = new CP14StopCastMagicEffectEvent(); + RaiseLocalEvent(ent, ref stopEv); + + if (args.Cancelled || !_net.IsServer) + return; + + foreach (var effect in ent.Comp.Effects) + { + effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(args.User, null, GetCoordinates(args.Target))); + } + + var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; + RaiseLocalEvent(ent, ref ev); + } + + private void OnDelayedEntityTargetDoAfter(Entity ent, ref CP14DelayedEntityTargetActionDoAfterEvent args) + { + var stopEv = new CP14StopCastMagicEffectEvent(); + RaiseLocalEvent(ent, ref stopEv); + + if (args.Cancelled || !_net.IsServer) + return; + + foreach (var effect in ent.Comp.Effects) + { + effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(args.User, args.Target, null)); + } + + var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; + RaiseLocalEvent(ent, ref ev); + } + + private void OnDelayedInstantActionDoAfter(Entity ent, ref CP14DelayedInstantActionDoAfterEvent args) + { + var stopEv = new CP14StopCastMagicEffectEvent(); + RaiseLocalEvent(ent, ref stopEv); + + if (args.Cancelled || !_net.IsServer) + return; + + foreach (var effect in ent.Comp.Effects) + { + effect.Effect(EntityManager, new CP14SpellEffectBaseArgs(args.User, args.User, Transform(args.User).Coordinates)); + } + + var ev = new CP14AfterCastMagicEffectEvent {Performer = args.User}; + RaiseLocalEvent(ent, ref ev); } private void OnSomaticAspectBeforeCast(Entity ent, ref CP14BeforeCastMagicEffectEvent args) @@ -85,6 +140,9 @@ public partial class CP14SharedMagicSystem : EntitySystem private void OnVerbalAspectAfterCast(Entity ent, ref CP14AfterCastMagicEffectEvent args) { + if (_net.IsClient) + return; + var ev = new CP14VerbalAspectSpeechEvent { Performer = args.Performer, diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectCastingVisualComponent.cs b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectCastingVisualComponent.cs similarity index 89% rename from Content.Shared/_CP14/Magic/Components/CP14MagicEffectCastingVisualComponent.cs rename to Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectCastingVisualComponent.cs index 6475b0c220..3e5e62d9b7 100644 --- a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectCastingVisualComponent.cs +++ b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectCastingVisualComponent.cs @@ -1,6 +1,6 @@ using Robust.Shared.Prototypes; -namespace Content.Shared._CP14.Magic.Components; +namespace Content.Shared._CP14.MagicSpell.Components; /// /// Creates a temporary entity that exists while the spell is cast, and disappears at the end. For visual special effects. diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectComponent.cs b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectComponent.cs similarity index 68% rename from Content.Shared/_CP14/Magic/Components/CP14MagicEffectComponent.cs rename to Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectComponent.cs index bc3bc40b4e..bc521e78c9 100644 --- a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectComponent.cs +++ b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectComponent.cs @@ -1,6 +1,7 @@ +using Content.Shared._CP14.MagicSpell.Spells; using Content.Shared.FixedPoint; -namespace Content.Shared._CP14.Magic.Components; +namespace Content.Shared._CP14.MagicSpell.Components; /// /// Restricts the use of this action, by spending mana or user requirements. @@ -13,4 +14,7 @@ public sealed partial class CP14MagicEffectComponent : Component [DataField] public bool Safe = false; + + [DataField] + public List Effects = new(); } diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectSomaticAspectComponent.cs b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectSomaticAspectComponent.cs similarity index 81% rename from Content.Shared/_CP14/Magic/Components/CP14MagicEffectSomaticAspectComponent.cs rename to Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectSomaticAspectComponent.cs index 2e4f816cc8..78211a4af5 100644 --- a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectSomaticAspectComponent.cs +++ b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectSomaticAspectComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Shared._CP14.Magic.Components; +namespace Content.Shared._CP14.MagicSpell.Components; /// /// Requires the user to have at least one free hand to use this spell diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectVerbalAspectComponent.cs b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectVerbalAspectComponent.cs similarity index 89% rename from Content.Shared/_CP14/Magic/Components/CP14MagicEffectVerbalAspectComponent.cs rename to Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectVerbalAspectComponent.cs index e319fca45a..8afa40ca6e 100644 --- a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectVerbalAspectComponent.cs +++ b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectVerbalAspectComponent.cs @@ -1,6 +1,4 @@ -using Content.Shared.FixedPoint; - -namespace Content.Shared._CP14.Magic.Components; +namespace Content.Shared._CP14.MagicSpell.Components; /// /// Requires the user to be able to speak in order to use this spell. Also forces the user to use certain phrases at the beginning and end of a spell cast diff --git a/Content.Shared/_CP14/Magic/Events/CP14CastMagicEffectEvent.cs b/Content.Shared/_CP14/MagicSpell/Events/CP14CastMagicEffectEvent.cs similarity index 95% rename from Content.Shared/_CP14/Magic/Events/CP14CastMagicEffectEvent.cs rename to Content.Shared/_CP14/MagicSpell/Events/CP14CastMagicEffectEvent.cs index 2f15277140..27e8302b93 100644 --- a/Content.Shared/_CP14/Magic/Events/CP14CastMagicEffectEvent.cs +++ b/Content.Shared/_CP14/MagicSpell/Events/CP14CastMagicEffectEvent.cs @@ -1,4 +1,4 @@ -namespace Content.Shared._CP14.Magic.Events; +namespace Content.Shared._CP14.MagicSpell.Events; [ByRefEvent] public sealed class CP14BeforeCastMagicEffectEvent : CancellableEntityEventArgs diff --git a/Content.Shared/_CP14/Magic/Events/CP14DelayedActionEvents.cs b/Content.Shared/_CP14/MagicSpell/Events/CP14DelayedActionEvents.cs similarity index 97% rename from Content.Shared/_CP14/Magic/Events/CP14DelayedActionEvents.cs rename to Content.Shared/_CP14/MagicSpell/Events/CP14DelayedActionEvents.cs index d8eb86482b..7e12413bfa 100644 --- a/Content.Shared/_CP14/Magic/Events/CP14DelayedActionEvents.cs +++ b/Content.Shared/_CP14/MagicSpell/Events/CP14DelayedActionEvents.cs @@ -3,7 +3,7 @@ using Content.Shared.DoAfter; using Robust.Shared.Map; using Robust.Shared.Serialization; -namespace Content.Shared._CP14.Magic.Events; +namespace Content.Shared._CP14.MagicSpell.Events; //World target public sealed partial class CP14DelayedWorldTargetActionEvent : WorldTargetActionEvent, ICP14DelayedMagicEffect diff --git a/Content.Shared/_CP14/Magic/CP14IDelayedMagicEffect.cs b/Content.Shared/_CP14/MagicSpell/Events/CP14IDelayedMagicEffect.cs similarity index 85% rename from Content.Shared/_CP14/Magic/CP14IDelayedMagicEffect.cs rename to Content.Shared/_CP14/MagicSpell/Events/CP14IDelayedMagicEffect.cs index 3070769bfc..0e52bc538a 100644 --- a/Content.Shared/_CP14/Magic/CP14IDelayedMagicEffect.cs +++ b/Content.Shared/_CP14/MagicSpell/Events/CP14IDelayedMagicEffect.cs @@ -1,4 +1,4 @@ -namespace Content.Shared._CP14.Magic; +namespace Content.Shared._CP14.MagicSpell.Events; public interface ICP14DelayedMagicEffect // The speak n spell interface { diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyEntityEffect.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyEntityEffect.cs new file mode 100644 index 0000000000..73165a919b --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellApplyEntityEffect.cs @@ -0,0 +1,22 @@ +using Content.Shared.EntityEffects; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellApplyEntityEffect : CP14SpellEffect +{ + [DataField(required: true, serverOnly: true)] + public List Effects = new(); + + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + if (args.Target is null) + return; + + var targetEntity = args.Target.Value; + + foreach (var effect in Effects) + { + effect.Effect(new EntityEffectBaseArgs(targetEntity, entManager)); + } + } +} diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellEffect.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellEffect.cs new file mode 100644 index 0000000000..c7b034045c --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellEffect.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using Robust.Shared.Map; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +[ImplicitDataDefinitionForInheritors] +[MeansImplicitUse] +public abstract partial class CP14SpellEffect +{ + public abstract void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args); +} + +public record class CP14SpellEffectBaseArgs +{ + public EntityUid? User; + public EntityUid? Target; + public EntityCoordinates? Position; + + public CP14SpellEffectBaseArgs(EntityUid? user, EntityUid? target, EntityCoordinates? position) + { + User = user; + Target = target; + Position = position; + } +} diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellProjectile.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellProjectile.cs new file mode 100644 index 0000000000..87f4f0531e --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellProjectile.cs @@ -0,0 +1,52 @@ +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Map; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellProjectile : CP14SpellEffect +{ + [DataField(required: true)] + public EntProtoId Prototype; + + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + EntityCoordinates? targetPoint = null; + if (args.Position is not null) + targetPoint = args.Position.Value; + else if (args.Target is not null && entManager.TryGetComponent(args.Target.Value, out var transformComponent)) + targetPoint = transformComponent.Coordinates; + + if (targetPoint is null) + return; + + + var transform = entManager.System(); + var physics = entManager.System(); + var gunSystem = entManager.System(); + var mapManager = IoCManager.Resolve(); + + if (!entManager.TryGetComponent(args.User, out var xform)) + return; + + var fromCoords = xform.Coordinates; + + if (fromCoords == targetPoint) + return; + + var userVelocity = physics.GetMapLinearVelocity(args.User.Value); + + // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. + var fromMap = transform.ToMapCoordinates(fromCoords); + var spawnCoords = mapManager.TryFindGridAt(fromMap, out var gridUid, out _) + ? transform.WithEntityId(fromCoords, gridUid) + : new(mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); + + + var ent = entManager.SpawnAtPosition(Prototype, spawnCoords); + var direction = targetPoint.Value.ToMapPos(entManager, transform) - + spawnCoords.ToMapPos(entManager, transform); + gunSystem.ShootProjectile(ent, direction, userVelocity, args.User.Value, args.User); + } +} diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellSpawnEntity.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellSpawnEntity.cs new file mode 100644 index 0000000000..127545e7b9 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellSpawnEntity.cs @@ -0,0 +1,27 @@ +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellSpawnEntity : CP14SpellEffect +{ + [DataField] + public List Spawns = new(); + + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + EntityCoordinates? targetPoint = null; + if (args.Position is not null) + targetPoint = args.Position.Value; + else if (args.Target is not null && entManager.TryGetComponent(args.Target.Value, out var transformComponent)) + targetPoint = transformComponent.Coordinates; + + if (targetPoint is null) + return; + + foreach (var spawn in Spawns) + { + entManager.SpawnAtPosition(spawn, targetPoint.Value); + } + } +} diff --git a/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessHoldingComponent.cs b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessHoldingComponent.cs new file mode 100644 index 0000000000..204adaa393 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessHoldingComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._CP14.MagicSpellStorage; + +/// +/// Denotes that this item's spells can be accessed while holding it in your hand +/// +[RegisterComponent, Access(typeof(CP14SpellStorageSystem))] +public sealed partial class CP14SpellStorageAccessHoldingComponent : Component +{ +} diff --git a/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessWearingComponent.cs b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessWearingComponent.cs new file mode 100644 index 0000000000..d6fb440b74 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageAccessWearingComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._CP14.MagicSpellStorage; + +/// +/// Denotes that this item's spells can be accessed while wearing it in your body +/// +[RegisterComponent, Access(typeof(CP14SpellStorageSystem))] +public sealed partial class CP14SpellStorageAccessWearingComponent : Component +{ +} diff --git a/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageComponent.cs b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageComponent.cs new file mode 100644 index 0000000000..3aec79a052 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicSpellStorage; + +/// +/// A component that allows you to store spells in items +/// +[RegisterComponent, Access(typeof(CP14SpellStorageSystem))] +public sealed partial class CP14SpellStorageComponent : Component +{ + /// + /// list of spell prototypes used for initialization. + /// + [DataField] + public List Spells = new(); + + /// + /// created after the initialization of spell entities. + /// + [DataField] + public List SpellEntities = new(); +} diff --git a/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageRequireAttuneComponent.cs b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageRequireAttuneComponent.cs new file mode 100644 index 0000000000..f81ce3851e --- /dev/null +++ b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageRequireAttuneComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._CP14.MagicSpellStorage; + +/// +/// The ability to access spellcasting is limited by the attuning requirement +/// +[RegisterComponent, Access(typeof(CP14SpellStorageSystem))] +public sealed partial class CP14SpellStorageRequireAttuneComponent : Component +{ +} diff --git a/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageSystem.cs b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageSystem.cs new file mode 100644 index 0000000000..f65d3bd353 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpellStorage/CP14SpellStorageSystem.cs @@ -0,0 +1,109 @@ +using Content.Shared._CP14.MagicAttuning; +using Content.Shared.Actions; +using Content.Shared.Clothing; +using Content.Shared.Hands; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Mind; + +namespace Content.Shared._CP14.MagicSpellStorage; + +/// +/// this part of the system is responsible for storing spells in items, and the methods players use to obtain them. +/// +public sealed partial class CP14SpellStorageSystem : EntitySystem +{ + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly CP14SharedMagicAttuningSystem _attuning = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnMagicStorageInit); + + SubscribeLocalEvent(OnEquippedHand); + SubscribeLocalEvent(OnHandAddedAttune); + + SubscribeLocalEvent(OnClothingEquipped); + SubscribeLocalEvent(OnClothingUnequipped); + + SubscribeLocalEvent(OnRemovedAttune); + } + + /// + /// When we initialize, we create action entities, and add them to this item. + /// + private void OnMagicStorageInit(Entity mStorage, ref MapInitEvent args) + { + foreach (var spell in mStorage.Comp.Spells) + { + var spellEnt = _actionContainer.AddAction(mStorage, spell); + if (spellEnt is null) + continue; + + mStorage.Comp.SpellEntities.Add(spellEnt.Value); + } + } + + private void OnEquippedHand(Entity ent, ref GotEquippedHandEvent args) + { + if (!TryComp(ent, out var spellStorage)) + return; + + TryGrantAccess((ent, spellStorage), args.User); + } + + private void OnHandAddedAttune(Entity ent, ref AddedAttuneToMindEvent args) + { + if (!TryComp(ent, out var spellStorage)) + return; + + if (args.User is null) + return; + + if (!_hands.IsHolding(args.User.Value, ent)) + return; + + TryGrantAccess((ent, spellStorage), args.User.Value); + } + + private void OnClothingEquipped(Entity ent, ref ClothingGotEquippedEvent args) + { + if (!TryComp(ent, out var spellStorage)) + return; + + TryGrantAccess((ent, spellStorage), args.Wearer); + } + + private void OnClothingUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) + { + _actions.RemoveProvidedActions(args.Wearer, ent); + } + + private bool TryGrantAccess(Entity storage, EntityUid user) + { + if (!_mind.TryGetMind(user, out var mindId, out var mind)) + return false; + + if (mind.OwnedEntity is null) + return false; + + if (TryComp(storage, out var reqAttune)) + { + if (!_attuning.IsAttunedTo(mindId, storage)) + return false; + } + + _actions.GrantActions(user, storage.Comp.SpellEntities, storage); + return true; + } + + private void OnRemovedAttune(Entity ent, ref RemovedAttuneFromMindEvent args) + { + if (args.User is null) + return; + + _actions.RemoveProvidedActions(args.User.Value, ent); + } +} diff --git a/Resources/Locale/en-US/_CP14/magicEnergy/magic-focus.ftl b/Resources/Locale/en-US/_CP14/magicEnergy/magic-focus.ftl new file mode 100644 index 0000000000..3f024e3ce9 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicEnergy/magic-focus.ftl @@ -0,0 +1,7 @@ +cp14-magic-attuning-verb-text = Attune yourself +cp14-magic-attuning-verb-message = Some magic items require a creature to form a bond with them before their magical properties can be used. + +cp14-magic-deattuning-verb-text = Break attune + +cp14-magic-attune-oldest-forgot = Your connection to {$item} is weakening ... +cp14-magic-attune-oldest-forgot-end = Your connection to {$item} has broken... \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-focus.ftl b/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-focus.ftl new file mode 100644 index 0000000000..5aff91f9dc --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-focus.ftl @@ -0,0 +1,7 @@ +cp14-magic-attuning-verb-text = Настроиться на магический предмет +cp14-magic-attuning-verb-message = Некоторые магические предметы требуют того, чтобы существо образовало с ними связь, прежде чем оно сможет использовать их магические свойства. + +cp14-magic-deattuning-verb-text = Разорвать связь с магическим предметом + +cp14-magic-attune-oldest-forgot = Ваша связь с {$item} слабеет... +cp14-magic-attune-oldest-forgot-end = Ваша связь с {$item} оборвалась... \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/cure_wounds.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/cure_wounds.yml new file mode 100644 index 0000000000..639466a19b --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/cure_wounds.yml @@ -0,0 +1,62 @@ +- type: entity + id: CP14ActionSpellCureWounds + name: Cure wounds + description: You touch the creature, healing its body from physical damage + components: + - type: CP14MagicEffect + manaCost: 10 + effects: + - !type:CP14SpellSpawnEntity + spawns: + - CP14CureWoundsImpactEffect + - !type:CP14SpellApplyEntityEffect + effects: + - !type:HealthChange + damage: + types: + Slash: -8 + Blunt: -8 + Piercing: -8 + Heat: -8 + - !type:Jitter + - type: CP14MagicEffectVerbalAspect + startSpeech: "Et curabuntur..." + endSpeech: "vulnera tua" + - type: CP14MagicEffectSomaticAspect + - type: CP14MagicEffectCastingVisual + proto: CP14CureWoundsRune + - type: EntityTargetAction + useDelay: 5 + itemIconStyle: BigAction + interactOnMiss: false + sound: !type:SoundPathSpecifier + path: /Audio/Magic/rumble.ogg + icon: + sprite: _CP14/Effects/Magic/spells_icons.rsi + state: cure_wounds + event: !type:CP14DelayedEntityTargetActionEvent + delay: 2 + +- type: entity + id: CP14CureWoundsRune + noSpawn: true + parent: CP14BaseMagicRune + components: + - type: PointLight + color: "#328643" + - type: Sprite + layers: + - state: sun + color: "#79b330" + shader: unshaded + +- type: entity + id: CP14CureWoundsImpactEffect + parent: CP14BaseMagicImpact + noSpawn: true + components: + - type: Sprite + layers: + - state: particles_up + color: "#79b330" + shader: unshaded \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/test_spell.yml b/Resources/Prototypes/_CP14/Entities/Actions/test_spell.yml deleted file mode 100644 index c3024be673..0000000000 --- a/Resources/Prototypes/_CP14/Entities/Actions/test_spell.yml +++ /dev/null @@ -1,153 +0,0 @@ -- type: entity - id: CP14ActionSpellHealingWord - name: Healing word - description: bruh - components: - - type: CP14MagicEffect - manaCost: 10 - - type: EntityTargetAction - useDelay: 1 - itemIconStyle: BigAction - interactOnMiss: true - sound: !type:SoundPathSpecifier - path: /Audio/Magic/rumble.ogg - icon: - sprite: Objects/Magic/magicactions.rsi - state: gib - event: !type:CP14DelayedEntityTargetActionEvent - - type: CP14DelayedApplyEntityEffectsSpell - effects: - - !type:HealthChange - damage: - types: - Slash: -5 - Blunt: -5 - Piercing: -5 - Heat: -10 - - !type:Jitter - -- type: entity - id: CP14ActionSpellFireball - name: Fireball - description: Fires an explosive fireball towards the clicked location. - components: - - type: CP14MagicEffect - manaCost: 70 - - type: CP14MagicEffectVerbalAspect - startSpeech: "O tenebrae, ubi lux non penetrat..." - endSpeech: "Quaeso, quemdam inter vos quaero!" - - type: CP14MagicEffectSomaticAspect - - type: CP14MagicEffectCastingVisual - proto: CP14FireballRune - - type: WorldTargetAction - useDelay: 3 - itemIconStyle: BigAction - checkCanAccess: false - raiseOnUser: true - range: 60 - sound: !type:SoundPathSpecifier - path: /Audio/Magic/fireball.ogg - icon: - sprite: Objects/Magic/magicactions.rsi - state: fireball - event: !type:CP14DelayedWorldTargetActionEvent - delay: 3 - - type: CP14DelayedProjectileSpell - prototype: ProjectileFireball - -- type: entity - id: CP14ActionSpellSelfHeal - name: Self Heal - description: Toggles your suit's phase cloak. Beware that if you are hit, all abilities are disabled for 5 seconds, including your cloak! - components: - - type: InstantAction - # have to plan (un)cloaking ahead of time - useDelay: 5 - priority: -9 - event: !type:CP14DelayedInstantActionEvent - - type: CP14DelayedSelfEntityEffectSpell - effects: - - !type:HealthChange - damage: - types: - Slash: -5 - Blunt: -5 - Piercing: -5 - Heat: -10 - - !type:Jitter - -- type: entity - id: CP14ActionSpelldaggerSpawn - name: Gravity well - description: Toggles your suit's phase cloak. Beware that if you are hit, all abilities are disabled for 5 seconds, including your cloak! - components: - - type: CP14MagicEffect - manaCost: 20 - - type: WorldTargetAction - useDelay: 1 - itemIconStyle: BigAction - checkCanAccess: false - raiseOnUser: true - range: 4 - sound: !type:SoundPathSpecifier - path: /Audio/Magic/fireball.ogg - icon: - sprite: Objects/Magic/magicactions.rsi - state: fireball - event: !type:CP14DelayedWorldTargetActionEvent - - type: CP14DelayedSpawnOnWorldTargetSpell - spawns: - - AdminInstantEffectGravityWell - -- type: listing - id: CP14SpellbookJutter - name: jit - description: jit - productAction: CP14ActionSpellHealingWord - cost: - WizCoin: 1 - categories: - - SpellbookOffensive - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - - -- type: listing - id: CP14SpellbookFireball - name: fifire - description: fifire - productAction: CP14ActionSpellFireball - cost: - WizCoin: 1 - categories: - - SpellbookOffensive - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: CP14SpellbookFireball3 - name: selfheal - description: selfheal - productAction: CP14ActionSpellSelfHeal - cost: - WizCoin: 1 - categories: - - SpellbookOffensive - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: CP14SpellbookFireball33 - name: Gravity Well - description: well... - productAction: CP14ActionSpelldaggerSpawn - cost: - WizCoin: 1 - categories: - - SpellbookOffensive - conditions: - - !type:ListingLimitedStockCondition - stock: 1 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Effects/cast_vfx.yml b/Resources/Prototypes/_CP14/Entities/Effects/cast_vfx.yml index 2c9a9205a2..28190daab1 100644 --- a/Resources/Prototypes/_CP14/Entities/Effects/cast_vfx.yml +++ b/Resources/Prototypes/_CP14/Entities/Effects/cast_vfx.yml @@ -5,6 +5,7 @@ abstract: true components: - type: Sprite + noRot: true drawDepth: FloorTiles sprite: _CP14/Effects/Magic/cast_rune.rsi - type: Tag @@ -16,16 +17,19 @@ netsync: false - type: entity - id: CP14FireballRune - parent: CP14BaseMagicRune + id: CP14BaseMagicImpact + name: magic impact + description: manifestation of magical energy in the physical plane + abstract: true components: - - type: PointLight - color: "#d47d26" + - type: TimedDespawn + lifetime: 1.6 + - type: AnimationPlayer + - type: EffectVisuals + - type: Tag + tags: + - HideContextMenu - type: Sprite - layers: - - state: medium_circle - color: "#d47d26" - shader: unshaded - - state: sun - color: "#d47d26" - shader: unshaded \ No newline at end of file + drawdepth: Effects + sprite: _CP14/Effects/Magic/cast_impact.rsi + noRot: true \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/FARMINGTEST.yml b/Resources/Prototypes/_CP14/Entities/FARMINGTEST.yml index 6a83f97a5d..341bc60de1 100644 --- a/Resources/Prototypes/_CP14/Entities/FARMINGTEST.yml +++ b/Resources/Prototypes/_CP14/Entities/FARMINGTEST.yml @@ -7,4 +7,11 @@ sprite: Objects/Specific/Hydroponics/seeds.rsi state: seed - type: CP14Seed - plantProto: CP14PlantWheat \ No newline at end of file + plantProto: CP14PlantWheat + - type: CP14MagicAttuningItem + focusTime: 1 + - type: CP14SpellStorageAccessHolding + - type: CP14SpellStorage + spells: + - CP14ActionSpellCureWounds + - type: CP14SpellStorageRequireAttune \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml b/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml index 89edfa5029..e59b2e9103 100644 --- a/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml @@ -211,10 +211,12 @@ maxEnergy: 100 energy: 100 - type: CP14MagicEnergyDraw - energy: 5 - delay: 5 + energy: 1 + delay: 6 # 10m to full restore - type: CP14MagicUnsafeDamage - type: CP14MagicUnsafeSleep + - type: CP14MagicAttuningMind + autoCopyToMind: true - type: entity diff --git a/Resources/Prototypes/_CP14/Maps/debug.yml b/Resources/Prototypes/_CP14/Maps/debug.yml index 510b8492d5..84ad547b9d 100644 --- a/Resources/Prototypes/_CP14/Maps/debug.yml +++ b/Resources/Prototypes/_CP14/Maps/debug.yml @@ -5,25 +5,25 @@ minPlayers: 0 stations: Empty: - stationProto: StandardNanotrasenStation + stationProto: StandardStationArena components: - type: StationNameSetup mapNameTemplate: "Empty" - type: StationJobs availableJobs: - CP14Adventurer: [ -1, -1 ] #CrystallPunk Dev replacement + CP14Adventurer: [ -1, -1 ] - type: gameMap id: Dev mapName: Dev - mapPath: /Maps/_CP14/dev_map.yml #CrystallPunk Dev replacement + mapPath: /Maps/_CP14/dev_map.yml minPlayers: 0 stations: Dev: - stationProto: StandardNanotrasenStation + stationProto: StandardStationArena components: - type: StationNameSetup mapNameTemplate: "Dev" - type: StationJobs availableJobs: - CP14Adventurer: [ -1, -1 ] #CrystallPunk Dev replacement + CP14Adventurer: [ -1, -1 ] diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/meta.json b/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/meta.json new file mode 100644 index 0000000000..d9e4483942 --- /dev/null +++ b/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "size": { + "x": 48, + "y": 48 + }, + "license": "All rights reserved for the CrystallPunk14 project only", + "copyright": "Created by TheShuEd for CrystallPunk", + "states": [ + { + "name": "particles_up", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/particles_up.png b/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/particles_up.png new file mode 100644 index 0000000000000000000000000000000000000000..d89de3177823f0e88cbd2bc6f7ff1fb97a670dc5 GIT binary patch literal 2872 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 F{x7VPOhEtu literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/cure_wounds.png b/Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/cure_wounds.png new file mode 100644 index 0000000000000000000000000000000000000000..9c0fae029a3d95467581a8ebbe210c878baeb3a2 GIT binary patch literal 1754 zcmV<01||84P)Px*lu1NER9J)YSn=RW7mC8S9ag&;^ZZ4!ckl!}sq521K@5TPKx z6?`cc!K8vOLMsS``rwN%q2P-?+0a6eP*Izb7?P5)5iC&%H8GPJXXeZ~XJ5X{^5LA5 zjF}`eBQ98M_T{YgJ8Q4+TmLQW(tiBz(}_Y|mCnRdMI5j(b}3$e;lr@gcxE@rfAYJ7 zVyzl$HMNP>lZ3WnJ`&M3PzO@LoIZO<{_g-kdgmDdjJ29(RvT%mGtQeUSsG5Nnk&Fe zjMB&}N$>PibK=eCb_8J`b>qb|$D~~kWHw@r8t2udR?^gJ!zS7Rx}STh;QHUw*~$UX z^^u}RM_xM-rxln2*v$~CNQh!xkQUIJYlW+2X*hVGx>v%ek^or4Tis@)+pPF# zvBP}3JZwZUf{5TBOg=r|+ye%XQDytxrk@97q2rftt!DcRXY7ro)>Rh=C%ersd+D$B zPOBC(Bbqob_{b}RA_@IGsWx)EG25(GR|d7KPxkW9ieUG4-O|mK+5(`X%C4F5sxd&^ z3=jcrW`1T0v(!)Ld=y)HSfQK?*zO3MTz*mH|*7CPksVC0gu5M(7rSy3ei zoR22DAWx|#ohA|8f%@HXpFY)~! zytgIcgSO*68{iwid{z)m1=_%Z!2Ca!8W%pjr#P;#5|w3C1`6)%OMlun`K|!~UjFUT z6s4sw$JlE?3H*EF!=HcsvfF9AT>-pt;h-p_str%7p2+jyzJA+URCB)X#*LLQAyhZoO=6^sFEnuqDsek=~=@c`gzhiw>Z!G&?-P) z$@f*|Hz2={Do+AWFkU4s#!~Hn1Mm@i#DqZ22Wiz(E-m%#w-(yr3_!m~1^~k{@c>07 zFB!wTB3x8hmbc=5e^5php7OVi2 zUIIYn`L+>0@R62})O^JFxHbN^O*kmVkBsFR+G_=dg&8fh%jZXx83EN?t30+*B(-yk z^E~~j{&Ke&DVzzJjZB~|_m$RvHRHAbYa^?GsuH5&t}k|&ZC0boOYWmtS7nqWN>oYr z_40$mB5B@Smgf^gHq)r+=XR>L9@+!j0&ElslX+gnD0!>qj;@Zf%hrU$QKbS#QN^hu zstkQFZJWekgw|jF* zG8Q5a{aoA8M)c=X?`c#Cij*J)NIAYE2}lNFK@wGxsA5AD8COXPKlaK;iI+}o)d#y7 zMv<|c$|wE>;2QTOy#X;G0&;D9U-F4NkcalcjsYf;Eif+U6au!Ec6vU2rUJs`_gidN w(~xl!Z5FPdnIxPx*z}`gdVMNDA2|{K1HTijH^-@nQ~&?~07*qoM6N<$g89%-G5`Po literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/meta.json b/Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/meta.json new file mode 100644 index 0000000000..2365585050 --- /dev/null +++ b/Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "All rights reserved for the CrystallPunk14 project only", + "copyright": "Created by TheShuEd", + "states": [ + { + "name": "cure_wounds" + } + ] +} \ No newline at end of file