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 0000000000..d89de31778 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/particles_up.png differ 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 0000000000..9c0fae029a Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/spells_icons.rsi/cure_wounds.png differ 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