diff --git a/Content.Server/_CP14/Magic/CP14MagicSystem.cs b/Content.Server/_CP14/Magic/CP14MagicSystem.cs new file mode 100644 index 0000000000..f0ee72f2d7 --- /dev/null +++ b/Content.Server/_CP14/Magic/CP14MagicSystem.cs @@ -0,0 +1,37 @@ +using Content.Server.Chat.Systems; +using Content.Shared._CP14.Magic; +using Content.Shared._CP14.Magic.Components; +using Content.Shared._CP14.Magic.Events; + +namespace Content.Server._CP14.Magic; + +public sealed partial class CP14MagicSystem : CP14SharedMagicSystem +{ + [Dependency] private readonly ChatSystem _chat = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnSpellSpoken); + + SubscribeLocalEvent(OnSpawnMagicVisualEffect); + SubscribeLocalEvent(OnDespawnMagicVisualEffect); + } + + private void OnSpellSpoken(Entity ent, ref CP14VerbalAspectSpeechEvent args) + { + if (args.Performer is not null && args.Speech is not null) + _chat.TrySendInGameICMessage(args.Performer.Value, args.Speech, InGameICChatType.Speak, false); + } + + private void OnSpawnMagicVisualEffect(Entity ent, ref CP14StartCastMagicEffectEvent args) + { + var vfx = SpawnAttachedTo(ent.Comp.Proto, Transform(args.Performer).Coordinates); + ent.Comp.SpawnedEntity = vfx; + } + + private void OnDespawnMagicVisualEffect(Entity ent, ref CP14StopCastMagicEffectEvent args) + { + QueueDel(ent.Comp.SpawnedEntity); + ent.Comp.SpawnedEntity = null; + } +} diff --git a/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.cs b/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.cs index 0ec735fe69..df2aeb72fb 100644 --- a/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.cs +++ b/Content.Server/_CP14/MagicEnergy/CP14MagicEnergySystem.cs @@ -2,7 +2,6 @@ using Content.Server._CP14.MagicEnergy.Components; using Content.Shared._CP14.MagicEnergy; using Content.Shared._CP14.MagicEnergy.Components; using Content.Shared.Examine; -using Content.Shared.FixedPoint; using Content.Shared.Inventory; using Robust.Server.GameObjects; using Robust.Shared.Timing; @@ -18,6 +17,7 @@ public sealed partial class CP14MagicEnergySystem : SharedCP14MagicEnergySystem public override void Initialize() { SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnEnergyChange); SubscribeLocalEvent(OnExamined); @@ -58,8 +58,6 @@ public sealed partial class CP14MagicEnergySystem : SharedCP14MagicEnergySystem args.PushMarkup(GetEnergyExaminedText(ent, magicContainer)); } - - public override void Update(float frameTime) { base.Update(frameTime); @@ -92,61 +90,4 @@ public sealed partial class CP14MagicEnergySystem : SharedCP14MagicEnergySystem ChangeEnergy(energyEnt.Value, energyComp, draw.Energy, draw.Safe); } } - - public bool TryConsumeEnergy(EntityUid uid, FixedPoint2 energy, CP14MagicEnergyContainerComponent? component = null, bool safe = false) - { - if (!Resolve(uid, ref component)) - return false; - - if (energy <= 0) - return true; - - // Attempting to absorb more energy than is contained in the carrier will still waste all the energy - if (component.Energy < energy) - { - ChangeEnergy(uid, component, -component.Energy); - return false; - } - - ChangeEnergy(uid, component, -energy, safe); - return true; - } - - public void ChangeEnergy(EntityUid uid, CP14MagicEnergyContainerComponent component, FixedPoint2 energy, bool safe = false) - { - if (!safe) - { - //Overload - if (component.Energy + energy > component.MaxEnergy) - { - RaiseLocalEvent(uid, new CP14MagicEnergyOverloadEvent() - { - OverloadEnergy = (component.Energy + energy) - component.MaxEnergy, - }); - } - - //Burn out - if (component.Energy + energy < 0) - { - RaiseLocalEvent(uid, new CP14MagicEnergyBurnOutEvent() - { - BurnOutEnergy = -energy - component.Energy - }); - } - } - - var oldEnergy = component.Energy; - var newEnergy = Math.Clamp((float)component.Energy + (float)energy, 0, (float)component.MaxEnergy); - component.Energy = newEnergy; - - if (oldEnergy != newEnergy) - { - RaiseLocalEvent(uid, new CP14MagicEnergyLevelChangeEvent() - { - OldValue = component.Energy, - NewValue = newEnergy, - MaxValue = component.MaxEnergy, - }); - } - } } diff --git a/Content.Shared/_CP14/Magic/CP14IDelayedMagicEffect.cs b/Content.Shared/_CP14/Magic/CP14IDelayedMagicEffect.cs new file mode 100644 index 0000000000..3070769bfc --- /dev/null +++ b/Content.Shared/_CP14/Magic/CP14IDelayedMagicEffect.cs @@ -0,0 +1,13 @@ +namespace Content.Shared._CP14.Magic; + +public interface ICP14DelayedMagicEffect // The speak n spell interface +{ + /// + /// Localized string spoken by the caster when casting this spell. + /// + public float Delay { get; } + + public bool BreakOnMove { get; } + + public bool BreakOnDamage { get; } +} diff --git a/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs b/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs new file mode 100644 index 0000000000..f330e17de2 --- /dev/null +++ b/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.Spells.cs @@ -0,0 +1,130 @@ +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/CP14SharedMagicSystem.cs b/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.cs new file mode 100644 index 0000000000..3a0b8d922b --- /dev/null +++ b/Content.Shared/_CP14/Magic/CP14SharedMagicSystem.cs @@ -0,0 +1,219 @@ +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.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; +using Robust.Shared.Map; +using Robust.Shared.Network; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; + +namespace Content.Shared._CP14.Magic; + +public partial class CP14SharedMagicSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedGunSystem _gunSystem = default!; + [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() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeCastMagicEffect); + + SubscribeLocalEvent(OnSomaticAspectBeforeCast); + + SubscribeLocalEvent(OnVerbalAspectBeforeCast); + SubscribeLocalEvent(OnVerbalAspectAfterCast); + + SubscribeLocalEvent(OnAfterCastMagicEffect); + + SubscribeLocalEvent(OnInstantAction); + SubscribeLocalEvent(OnEntityTargetAction); + SubscribeLocalEvent(OnWorldTargetAction); + + InitializeSpells(); + } + + private void OnSomaticAspectBeforeCast(Entity ent, ref CP14BeforeCastMagicEffectEvent args) + { + if (TryComp(args.Performer, out var hands) || hands is not null) + { + foreach (var hand in hands.Hands) + { + if (hand.Value.IsEmpty) + return; + } + } + args.PushReason(Loc.GetString("cp14-magic-spell-need-somatic-component")); + args.Cancel(); + } + + private void OnVerbalAspectBeforeCast(Entity ent, ref CP14BeforeCastMagicEffectEvent args) + { + if (HasComp(args.Performer)) + { + args.PushReason(Loc.GetString("cp14-magic-spell-need-verbal-component")); + args.Cancel(); + } + else + { + if (!args.Cancelled) + { + var ev = new CP14VerbalAspectSpeechEvent + { + Performer = args.Performer, + Speech = ent.Comp.StartSpeech, + }; + RaiseLocalEvent(ent, ref ev); + } + } + } + + private void OnVerbalAspectAfterCast(Entity ent, ref CP14AfterCastMagicEffectEvent args) + { + var ev = new CP14VerbalAspectSpeechEvent + { + Performer = args.Performer, + Speech = ent.Comp.EndSpeech, + }; + RaiseLocalEvent(ent, ref ev); + } + + private void OnInstantAction(CP14DelayedInstantActionEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + if (args is not ICP14DelayedMagicEffect delayedEffect) + return; + + if (!TryCastSpell(args.Action, args.Performer)) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, delayedEffect.Delay, new CP14DelayedInstantActionDoAfterEvent(), args.Action) + { + BreakOnMove = delayedEffect.BreakOnMove, + BreakOnDamage = delayedEffect.BreakOnDamage, + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + } + + private void OnWorldTargetAction(CP14DelayedWorldTargetActionEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + if (args is not ICP14DelayedMagicEffect delayedEffect) + return; + + if (!TryCastSpell(args.Action, args.Performer)) + return; + + var doAfter = new CP14DelayedWorldTargetActionDoAfterEvent() + { + Target = EntityManager.GetNetCoordinates(args.Target) + }; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, delayedEffect.Delay, doAfter, args.Action) + { + BreakOnMove = delayedEffect.BreakOnMove, + BreakOnDamage = delayedEffect.BreakOnDamage, + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + } + + private void OnEntityTargetAction(CP14DelayedEntityTargetActionEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + if (args is not ICP14DelayedMagicEffect delayedEffect) + return; + + if (!TryCastSpell(args.Action, args.Performer)) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, delayedEffect.Delay, new CP14DelayedEntityTargetActionDoAfterEvent(), args.Action, args.Target) + { + BreakOnMove = delayedEffect.BreakOnMove, + BreakOnDamage = delayedEffect.BreakOnDamage, + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + } + + private bool TryCastSpell(EntityUid spell, EntityUid performer) + { + var ev = new CP14BeforeCastMagicEffectEvent + { + Performer = performer, + }; + RaiseLocalEvent(spell, ref ev); + if (ev.Reason != string.Empty && _net.IsServer) + { + _popup.PopupEntity(ev.Reason, performer, performer); + } + + if (!ev.Cancelled) + { + var evStart = new CP14StartCastMagicEffectEvent() + { + Performer = performer, + }; + RaiseLocalEvent(spell, ref evStart); + } + return !ev.Cancelled; + } + + private void OnBeforeCastMagicEffect(Entity ent, ref CP14BeforeCastMagicEffectEvent args) + { + if (!TryComp(args.Performer, out var magicContainer)) + { + args.Cancel(); + return; + } + + if (!_magicEnergy.HasEnergy(args.Performer, ent.Comp.ManaCost, magicContainer, ent.Comp.Safe)) + { + args.PushReason(Loc.GetString("cp14-magic-spell-not-enough-mana")); + args.Cancel(); + } + else if(!_magicEnergy.HasEnergy(args.Performer, ent.Comp.ManaCost, magicContainer, true) && _net.IsServer) + { + _popup.PopupEntity(Loc.GetString("cp14-magic-spell-not-enough-mana-cast-warning-"+_random.Next(5)), args.Performer, args.Performer, PopupType.SmallCaution); + } + } + + private void OnAfterCastMagicEffect(Entity ent, ref CP14AfterCastMagicEffectEvent args) + { + if (_net.IsClient) + return; + + if (!HasComp(args.Performer)) + return; + + _magicEnergy.TryConsumeEnergy(args.Performer.Value, ent.Comp.ManaCost, safe: ent.Comp.Safe); + } +} diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectCastingVisualComponent.cs b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectCastingVisualComponent.cs new file mode 100644 index 0000000000..6475b0c220 --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectCastingVisualComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Magic.Components; + +/// +/// Creates a temporary entity that exists while the spell is cast, and disappears at the end. For visual special effects. +/// +[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] +public sealed partial class CP14MagicEffectCastingVisualComponent : Component +{ + [DataField] + public EntityUid? SpawnedEntity; + + [DataField(required: true)] + public EntProtoId Proto = default!; +} diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectComponent.cs b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectComponent.cs new file mode 100644 index 0000000000..bc3bc40b4e --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.FixedPoint; + +namespace Content.Shared._CP14.Magic.Components; + +/// +/// Restricts the use of this action, by spending mana or user requirements. +/// +[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] +public sealed partial class CP14MagicEffectComponent : Component +{ + [DataField] + public FixedPoint2 ManaCost = 0f; + + [DataField] + public bool Safe = false; +} diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectSomaticAspectComponent.cs b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectSomaticAspectComponent.cs new file mode 100644 index 0000000000..2e4f816cc8 --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectSomaticAspectComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._CP14.Magic.Components; + +/// +/// Requires the user to have at least one free hand to use this spell +/// +[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] +public sealed partial class CP14MagicEffectSomaticAspectComponent : Component +{ +} diff --git a/Content.Shared/_CP14/Magic/Components/CP14MagicEffectVerbalAspectComponent.cs b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectVerbalAspectComponent.cs new file mode 100644 index 0000000000..e319fca45a --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/CP14MagicEffectVerbalAspectComponent.cs @@ -0,0 +1,27 @@ +using Content.Shared.FixedPoint; + +namespace Content.Shared._CP14.Magic.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 +/// +[RegisterComponent, Access(typeof(CP14SharedMagicSystem))] +public sealed partial class CP14MagicEffectVerbalAspectComponent : Component +{ + [DataField] + public string StartSpeech = string.Empty; + + [DataField] + public string EndSpeech = string.Empty; +} + +/// +/// patch to send an event to the server for saying a phrase out loud +/// +[ByRefEvent] +public sealed class CP14VerbalAspectSpeechEvent : EntityEventArgs +{ + public EntityUid? Performer { get; init; } + + public string? Speech { get; init; } +} diff --git a/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs new file mode 100644 index 0000000000..619efcb9aa --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedApplyEntityEffectsSpellComponent.cs @@ -0,0 +1,13 @@ +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 new file mode 100644 index 0000000000..7a47ea0573 --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedProjectileSpellComponent.cs @@ -0,0 +1,16 @@ +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 new file mode 100644 index 0000000000..a8b1396913 --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSelfEntityEffectSpellComponent.cs @@ -0,0 +1,13 @@ +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 new file mode 100644 index 0000000000..3172c995c8 --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnEntitiesSpellComponent.cs @@ -0,0 +1,16 @@ +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 new file mode 100644 index 0000000000..992a3a02ef --- /dev/null +++ b/Content.Shared/_CP14/Magic/Components/Spells/CP14DelayedSpawnOnWorldTargetSpellComponent.cs @@ -0,0 +1,16 @@ +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/Magic/Events/CP14CastMagicEffectEvent.cs b/Content.Shared/_CP14/Magic/Events/CP14CastMagicEffectEvent.cs new file mode 100644 index 0000000000..2f15277140 --- /dev/null +++ b/Content.Shared/_CP14/Magic/Events/CP14CastMagicEffectEvent.cs @@ -0,0 +1,41 @@ +namespace Content.Shared._CP14.Magic.Events; + +[ByRefEvent] +public sealed class CP14BeforeCastMagicEffectEvent : CancellableEntityEventArgs +{ + /// + /// The Performer of the event, to check if they meet the requirements. + /// + public EntityUid Performer { get; init; } + + public string Reason = string.Empty; + + public void PushReason(string reason) + { + Reason += $"{reason}\n"; + } +} + +[ByRefEvent] +public sealed class CP14AfterCastMagicEffectEvent : EntityEventArgs +{ + public EntityUid? Performer { get; init; } +} +/// +/// is invoked if all conditions are met and the spell has begun to be cast +/// +[ByRefEvent] +public sealed class CP14StartCastMagicEffectEvent : EntityEventArgs +{ + public EntityUid Performer { get; init; } +} + +/// +/// is invoked on the spell itself when the spell process has been completed or interrupted +/// +[ByRefEvent] +public sealed class CP14StopCastMagicEffectEvent : EntityEventArgs +{ +} + + diff --git a/Content.Shared/_CP14/Magic/Events/CP14DelayedActionEvents.cs b/Content.Shared/_CP14/Magic/Events/CP14DelayedActionEvents.cs new file mode 100644 index 0000000000..d8eb86482b --- /dev/null +++ b/Content.Shared/_CP14/Magic/Events/CP14DelayedActionEvents.cs @@ -0,0 +1,64 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Robust.Shared.Map; +using Robust.Shared.Serialization; + +namespace Content.Shared._CP14.Magic.Events; + +//World target +public sealed partial class CP14DelayedWorldTargetActionEvent : WorldTargetActionEvent, ICP14DelayedMagicEffect +{ + [DataField] + public float Delay { get; private set; } = 1f; + + [DataField] + public bool BreakOnMove { get; private set; } = true; + + [DataField] + public bool BreakOnDamage { get; private set; } = true; +} + +[Serializable, NetSerializable] +public sealed partial class CP14DelayedWorldTargetActionDoAfterEvent : DoAfterEvent +{ + [DataField] + public NetCoordinates Target; + public override DoAfterEvent Clone() => this; +} + + +//Entity Target +public sealed partial class CP14DelayedEntityTargetActionEvent : EntityTargetActionEvent, ICP14DelayedMagicEffect +{ + [DataField] + public float Delay { get; private set; } = 1f; + + [DataField] + public bool BreakOnMove { get; private set; } = true; + + [DataField] + public bool BreakOnDamage { get; private set; } = true; +} + +[Serializable, NetSerializable] +public sealed partial class CP14DelayedEntityTargetActionDoAfterEvent : SimpleDoAfterEvent +{ +} + +//Instant +public sealed partial class CP14DelayedInstantActionEvent : InstantActionEvent, ICP14DelayedMagicEffect +{ + [DataField] + public float Delay { get; private set; } = 1f; + + [DataField] + public bool BreakOnMove { get; private set; } = true; + + [DataField] + public bool BreakOnDamage { get; private set; } = true; +} + +[Serializable, NetSerializable] +public sealed partial class CP14DelayedInstantActionDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/_CP14/MagicEnergy/Components/CP14MagicEnergyContainerComponent.cs b/Content.Shared/_CP14/MagicEnergy/Components/CP14MagicEnergyContainerComponent.cs index 2bbbe22ddf..adcebc3eca 100644 --- a/Content.Shared/_CP14/MagicEnergy/Components/CP14MagicEnergyContainerComponent.cs +++ b/Content.Shared/_CP14/MagicEnergy/Components/CP14MagicEnergyContainerComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; namespace Content.Shared._CP14.MagicEnergy.Components; @@ -13,4 +15,7 @@ public sealed partial class CP14MagicEnergyContainerComponent : Component [DataField] public FixedPoint2 MaxEnergy = 100f; + + [DataField] + public ProtoId? MagicAlert = null; } diff --git a/Content.Shared/_CP14/MagicEnergy/SharedCP14MagicEnergySystem.cs b/Content.Shared/_CP14/MagicEnergy/SharedCP14MagicEnergySystem.cs index 9f5013468e..08a75a2e06 100644 --- a/Content.Shared/_CP14/MagicEnergy/SharedCP14MagicEnergySystem.cs +++ b/Content.Shared/_CP14/MagicEnergy/SharedCP14MagicEnergySystem.cs @@ -1,12 +1,34 @@ using Content.Shared._CP14.MagicEnergy.Components; -using Content.Shared.Examine; +using Content.Shared.Alert; using Content.Shared.FixedPoint; using Content.Shared.Inventory; +using Content.Shared.Rounding; namespace Content.Shared._CP14.MagicEnergy; public partial class SharedCP14MagicEnergySystem : EntitySystem { + [Dependency] private readonly AlertsSystem _alerts = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentShutdown); + } + + private void OnComponentStartup(Entity ent, ref ComponentStartup args) + { + UpdateMagicAlert(ent); + } + + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.MagicAlert == null) + return; + + _alerts.ClearAlert(ent, ent.Comp.MagicAlert.Value); + } + public string GetEnergyExaminedText(EntityUid uid, CP14MagicEnergyContainerComponent ent) { var power = (int)((ent.Energy / ent.MaxEnergy) * 100); @@ -22,6 +44,92 @@ public partial class SharedCP14MagicEnergySystem : EntitySystem ("power", power), ("color", color)); } + + public void ChangeEnergy(EntityUid uid, CP14MagicEnergyContainerComponent component, FixedPoint2 energy, bool safe = false) + { + if (!safe) + { + //Overload + if (component.Energy + energy > component.MaxEnergy) + { + RaiseLocalEvent(uid, new CP14MagicEnergyOverloadEvent() + { + OverloadEnergy = (component.Energy + energy) - component.MaxEnergy, + }); + } + + //Burn out + if (component.Energy + energy < 0) + { + RaiseLocalEvent(uid, new CP14MagicEnergyBurnOutEvent() + { + BurnOutEnergy = -energy - component.Energy + }); + } + } + + var oldEnergy = component.Energy; + var newEnergy = Math.Clamp((float)component.Energy + (float)energy, 0, (float)component.MaxEnergy); + component.Energy = newEnergy; + + if (oldEnergy != newEnergy) + { + RaiseLocalEvent(uid, new CP14MagicEnergyLevelChangeEvent() + { + OldValue = component.Energy, + NewValue = newEnergy, + MaxValue = component.MaxEnergy, + }); + } + + UpdateMagicAlert((uid, component)); + } + + public bool HasEnergy(EntityUid uid, FixedPoint2 energy, CP14MagicEnergyContainerComponent? component = null, bool safe = false) + { + if (!Resolve(uid, ref component)) + return false; + + if (safe == false) + return true; + + return component.Energy > energy; + } + + public bool TryConsumeEnergy(EntityUid uid, FixedPoint2 energy, CP14MagicEnergyContainerComponent? component = null, bool safe = false) + { + if (!Resolve(uid, ref component)) + return false; + + if (energy <= 0) + return true; + + // Attempting to absorb more energy than is contained in the container available only in non-safe methods (with container destruction) + if (component.Energy < energy) + { + if (safe) + { + return false; + } + else + { + ChangeEnergy(uid, component, -energy, safe); + return true; + } + } + + ChangeEnergy(uid, component, -energy, safe); + return true; + } + + private void UpdateMagicAlert(Entity ent) + { + if (ent.Comp.MagicAlert == null) + return; + + var level = ContentHelpers.RoundToLevels(MathF.Max(0f, (float) ent.Comp.Energy), (float) ent.Comp.MaxEnergy, 10); + _alerts.ShowAlert(ent, ent.Comp.MagicAlert.Value, (short)level); + } } /// diff --git a/Content.Shared/_CP14/MagicWeakness/CP14MagicUnsafeDamageComponent.cs b/Content.Shared/_CP14/MagicWeakness/CP14MagicUnsafeDamageComponent.cs new file mode 100644 index 0000000000..6871f89ad6 --- /dev/null +++ b/Content.Shared/_CP14/MagicWeakness/CP14MagicUnsafeDamageComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; + +namespace Content.Shared._CP14.MagicWeakness; + +/// +/// imposes damage on excessive use of magic +/// +[RegisterComponent, Access(typeof(CP14MagicWeaknessSystem))] +public sealed partial class CP14MagicUnsafeDamageComponent : Component +{ + [DataField] + public DamageSpecifier DamagePerEnergy = new() + { + DamageDict = new Dictionary + { + {"Blunt", 0.5}, + {"Heat", 0.5}, + }, + }; +} diff --git a/Content.Shared/_CP14/MagicWeakness/CP14MagicUnsafeSleepComponent.cs b/Content.Shared/_CP14/MagicWeakness/CP14MagicUnsafeSleepComponent.cs new file mode 100644 index 0000000000..6ec14dae43 --- /dev/null +++ b/Content.Shared/_CP14/MagicWeakness/CP14MagicUnsafeSleepComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; + +namespace Content.Shared._CP14.MagicWeakness; + +/// +/// imposes debuffs on excessive use of magic +/// +[RegisterComponent, Access(typeof(CP14MagicWeaknessSystem))] +public sealed partial class CP14MagicUnsafeSleepComponent : Component +{ + [DataField] + public float SleepPerEnergy = 0.5f; + + /// + /// At the specified amount of extra mana expenditure, the character falls asleep. + /// + [DataField] + public FixedPoint2 SleepThreshold = 20f; +} diff --git a/Content.Shared/_CP14/MagicWeakness/CP14MagicWeaknessSystem.cs b/Content.Shared/_CP14/MagicWeakness/CP14MagicWeaknessSystem.cs new file mode 100644 index 0000000000..860342fc08 --- /dev/null +++ b/Content.Shared/_CP14/MagicWeakness/CP14MagicWeaknessSystem.cs @@ -0,0 +1,43 @@ +using Content.Shared._CP14.MagicEnergy; +using Content.Shared.Bed.Sleep; +using Content.Shared.Damage; +using Content.Shared.Popups; +using Content.Shared.StatusEffect; + +namespace Content.Shared._CP14.MagicWeakness; + +public partial class CP14MagicWeaknessSystem : EntitySystem +{ + [ValidatePrototypeId] + private const string StatusEffectKey = "ForcedSleep"; + + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMagicEnergyBurnOutDamage); + SubscribeLocalEvent(OnMagicEnergyBurnOutSleep); + } + + private void OnMagicEnergyBurnOutSleep(Entity ent, ref CP14MagicEnergyBurnOutEvent args) + { + if (args.BurnOutEnergy > ent.Comp.SleepThreshold) + { + _popup.PopupEntity(Loc.GetString("cp14-magic-energy-damage-burn-out-fall"), ent, ent, PopupType.LargeCaution); + _statusEffects.TryAddStatusEffect(ent, + StatusEffectKey, + TimeSpan.FromSeconds(ent.Comp.SleepPerEnergy * (float)args.BurnOutEnergy), + false); + } + } + + private void OnMagicEnergyBurnOutDamage(Entity ent, ref CP14MagicEnergyBurnOutEvent args) + { + _popup.PopupEntity(Loc.GetString("cp14-magic-energy-damage-burn-out"), ent, ent, PopupType.LargeCaution); + _damageable.TryChangeDamage(ent, ent.Comp.DamagePerEnergy * args.BurnOutEnergy); + } +} diff --git a/Resources/Locale/en-US/_CP14/alerts/alerts.ftl b/Resources/Locale/en-US/_CP14/alerts/alerts.ftl new file mode 100644 index 0000000000..15af3f2557 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/alerts/alerts.ftl @@ -0,0 +1,2 @@ +cp14-alerts-magic-energy-name = Magic energy +cp14-alerts-magic-energy-desc = A reserve of your internal magic power that allows you to use spells. \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/magicEnergy/magic-energy.ftl b/Resources/Locale/en-US/_CP14/magicEnergy/magic-energy.ftl index 2a3de7acf9..d00edb6038 100644 --- a/Resources/Locale/en-US/_CP14/magicEnergy/magic-energy.ftl +++ b/Resources/Locale/en-US/_CP14/magicEnergy/magic-energy.ftl @@ -4,4 +4,4 @@ cp14-magic-energy-crystal-slot-name = Energy crystal cp14-magic-energy-no-crystal = No energy crystal! cp14-magic-energy-insufficient = Not enough energy! -cp14-magic-energy-insufficient-unsafe = Crystal cracks from lack of energy! \ No newline at end of file +cp14-magic-energy-insufficient-unsafe = Crystal cracks from lack of energy! diff --git a/Resources/Locale/en-US/_CP14/magicEnergy/magic-spells.ftl b/Resources/Locale/en-US/_CP14/magicEnergy/magic-spells.ftl new file mode 100644 index 0000000000..78e11469c7 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicEnergy/magic-spells.ftl @@ -0,0 +1,10 @@ +cp14-magic-spell-not-enough-mana = Not enough mana! + +cp14-magic-spell-not-enough-mana-cast-warning-0 = Everything inside you starts to whine unpleasantly... +cp14-magic-spell-not-enough-mana-cast-warning-1 = Your head starts to buzz... +cp14-magic-spell-not-enough-mana-cast-warning-2 = Your hands are shaking... +cp14-magic-spell-not-enough-mana-cast-warning-3 = Your throat starts to lump... +cp14-magic-spell-not-enough-mana-cast-warning-4 = Your head becomes heavy... + +cp14-magic-energy-damage-burn-out = The pain is piercing your body! +cp14-magic-energy-damage-burn-out-fall = You pass out from the intense pain! \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/alerts/alerts.ftl b/Resources/Locale/ru-RU/_CP14/alerts/alerts.ftl new file mode 100644 index 0000000000..06d3f4009a --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/alerts/alerts.ftl @@ -0,0 +1,2 @@ +cp14-alerts-magic-energy-name = Магическая энергия +cp14-alerts-magic-energy-desc = Запас вашей внутренней магической силы, позволяющий вам использовать заклинания. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-energy.ftl b/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-energy.ftl index 08146f578b..b9fb9344e8 100644 --- a/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-energy.ftl +++ b/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-energy.ftl @@ -4,4 +4,4 @@ cp14-magic-energy-crystal-slot-name = Энергокристалл cp14-magic-energy-no-crystal = Отсутствует энергокристалл! cp14-magic-energy-insufficient = Не достаточно энергии! -cp14-magic-energy-insufficient-unsafe = Кристалл трескается от нехватки энергии! \ No newline at end of file +cp14-magic-energy-insufficient-unsafe = Кристалл трескается от нехватки энергии! diff --git a/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-spells.ftl b/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-spells.ftl new file mode 100644 index 0000000000..5821960e74 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicEnergy/magic-spells.ftl @@ -0,0 +1,13 @@ +cp14-magic-spell-not-enough-mana = Недостаточно магической энергии! + +cp14-magic-spell-not-enough-mana-cast-warning-0 = Внутри вас все начинает неприятно ныть... +cp14-magic-spell-not-enough-mana-cast-warning-1 = Голова начинает шуметь... +cp14-magic-spell-not-enough-mana-cast-warning-2 = Ваши руки дрожат... +cp14-magic-spell-not-enough-mana-cast-warning-3 = К горлу подступает ком... +cp14-magic-spell-not-enough-mana-cast-warning-4 = Голова наливается свинцом... + +cp14-magic-energy-damage-burn-out = Боль пронзает ваше тело! +cp14-magic-energy-damage-burn-out-fall = Вы теряете сознание от сильной боли! + +cp14-magic-spell-need-verbal-component = Вы не можете произнести заклинание вслух. +cp14-magic-spell-need-somatic-component = Вам нужна свободная рука. \ No newline at end of file diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index c08453460f..8f7bc7f154 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -5,6 +5,7 @@ id: BaseAlertOrder order: - category: Health + - alertType: CP14MagicEnergy - category: Stamina - alertType: SuitPower - category: Internals diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 3a555beebf..258ff3acbc 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -207,7 +207,7 @@ id: MobRespirator abstract: true components: - - type: Internals + #- type: Internals #CP14 dont need this - type: Respirator damage: types: diff --git a/Resources/Prototypes/_CP14/Alerts/alerts.yml b/Resources/Prototypes/_CP14/Alerts/alerts.yml new file mode 100644 index 0000000000..8f2b52b8fa --- /dev/null +++ b/Resources/Prototypes/_CP14/Alerts/alerts.yml @@ -0,0 +1,29 @@ +- type: alert + id: CP14MagicEnergy + icons: + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_0 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_1 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_2 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_3 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_4 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_5 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_6 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_7 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_8 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_9 + - sprite: /Textures/_CP14/Interface/Alerts/magic_energy.rsi + state: mana_10 + name: cp14-alerts-magic-energy-name + description: cp14-alerts-magic-energy-desc + minSeverity: 0 + maxSeverity: 10 \ 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 new file mode 100644 index 0000000000..c3024be673 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/test_spell.yml @@ -0,0 +1,153 @@ +- 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 new file mode 100644 index 0000000000..2c9a9205a2 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Effects/cast_vfx.yml @@ -0,0 +1,31 @@ +- type: entity + id: CP14BaseMagicRune + name: magic rune + description: manifestation of magical energy in the physical plane + abstract: true + components: + - type: Sprite + drawDepth: FloorTiles + sprite: _CP14/Effects/Magic/cast_rune.rsi + - type: Tag + tags: + - HideContextMenu + - type: PointLight + radius: 1.5 + energy: 2.5 + netsync: false + +- type: entity + id: CP14FireballRune + parent: CP14BaseMagicRune + components: + - type: PointLight + color: "#d47d26" + - type: Sprite + layers: + - state: medium_circle + color: "#d47d26" + shader: unshaded + - state: sun + color: "#d47d26" + shader: unshaded \ 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 f6f987babc..89edfa5029 100644 --- a/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml @@ -54,10 +54,6 @@ sprite: Objects/Misc/handcuffs.rsi state: body-overlay-2 visible: false - - map: [ "clownedon" ] # Dynamically generated - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - type: DamageVisuals thresholds: [ 10, 20, 30, 50, 70, 100 ] targetLayers: @@ -144,9 +140,7 @@ prototype: CP14Human requiredLegs: 2 - type: Identity - - type: IdExaminable - type: Hands - - type: Internals - type: Inventory - type: InventorySlots - type: FloatingVisuals @@ -171,8 +165,6 @@ - type: SleepEmitSound - type: SSDIndicator - type: StandingState - - type: Fingerprint - - type: Dna - type: MindContainer showExamineInfo: true - type: InteractionPopup @@ -180,11 +172,9 @@ interactSuccessString: hugging-success-generic interactSuccessSound: /Audio/Effects/thudswoosh.ogg messagePerceivedByOthers: hugging-success-generic-others - - type: CanHostGuardian - type: NpcFactionMember factions: - NanoTrasen - - type: CreamPied - type: Stripping - type: Strippable - type: UserInterface @@ -215,9 +205,16 @@ deathPenalty: 0.01 # However they really ought to be living and intact, otherwise they're worth 100x less. - type: Tag tags: - - CanPilot - FootstepSound - - DoorBumpOpener + - type: CP14MagicEnergyContainer + magicAlert: CP14MagicEnergy + maxEnergy: 100 + energy: 100 + - type: CP14MagicEnergyDraw + energy: 5 + delay: 5 + - type: CP14MagicUnsafeDamage + - type: CP14MagicUnsafeSleep - type: entity @@ -342,10 +339,6 @@ sprite: Objects/Misc/handcuffs.rsi state: body-overlay-2 visible: false - - map: [ "clownedon" ] # Dynamically generated - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - type: Appearance - type: HumanoidAppearance species: CP14Human diff --git a/Resources/Prototypes/_CP14/Entities/Mobs/Species/tiefling.yml b/Resources/Prototypes/_CP14/Entities/Mobs/Species/tiefling.yml index 0071227d63..0f48312d60 100644 --- a/Resources/Prototypes/_CP14/Entities/Mobs/Species/tiefling.yml +++ b/Resources/Prototypes/_CP14/Entities/Mobs/Species/tiefling.yml @@ -77,4 +77,3 @@ 32: sprite: _CP14/Mobs/Species/Human/displacement.rsi state: female_shirt - diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/double_outer.png b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/double_outer.png new file mode 100644 index 0000000000..46c9e34428 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/double_outer.png differ diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/medium_circle.png b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/medium_circle.png new file mode 100644 index 0000000000..308926d4e8 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/medium_circle.png differ diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/medium_line.png b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/medium_line.png new file mode 100644 index 0000000000..f553ec7a55 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/medium_line.png differ diff --git a/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json new file mode 100644 index 0000000000..a9e0676cbb --- /dev/null +++ b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/meta.json @@ -0,0 +1,71 @@ +{ + "version": 1, + "size": { + "x": 48, + "y": 48 + }, + "license": "All rights reserved for the CrystallPunk14 project only", + "copyright": "Created by TheShuEd for CrystallPunk", + "states": [ + { + "name": "double_outer", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "medium_circle", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "medium_line", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "sun", + "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_rune.rsi/sun.png b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/sun.png new file mode 100644 index 0000000000..6b5b703900 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/cast_rune.rsi/sun.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_0.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_0.png new file mode 100644 index 0000000000..31f6340b4b Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_0.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_1.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_1.png new file mode 100644 index 0000000000..65899e38c1 Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_1.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_10.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_10.png new file mode 100644 index 0000000000..3f1dc63bca Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_10.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_2.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_2.png new file mode 100644 index 0000000000..182cf54b62 Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_2.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_3.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_3.png new file mode 100644 index 0000000000..b45fb943dd Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_3.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_4.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_4.png new file mode 100644 index 0000000000..1d3cdfefbe Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_4.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_5.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_5.png new file mode 100644 index 0000000000..b82c2a7fce Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_5.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_6.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_6.png new file mode 100644 index 0000000000..ddf5ec5644 Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_6.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_7.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_7.png new file mode 100644 index 0000000000..31f8169139 Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_7.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_8.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_8.png new file mode 100644 index 0000000000..54c98fb07a Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_8.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_9.png b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_9.png new file mode 100644 index 0000000000..2f9b675c95 Binary files /dev/null and b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/mana_9.png differ diff --git a/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/meta.json b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/meta.json new file mode 100644 index 0000000000..c99c223279 --- /dev/null +++ b/Resources/Textures/_CP14/Interface/Alerts/magic_energy.rsi/meta.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by TheShuEd (Github) for CrystallPunk", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "mana_0" + }, + { + "name": "mana_1" + }, + { + "name": "mana_2" + }, + { + "name": "mana_3" + }, + { + "name": "mana_4" + }, + { + "name": "mana_5" + }, + { + "name": "mana_6" + }, + { + "name": "mana_7" + }, + { + "name": "mana_8" + }, + { + "name": "mana_9" + }, + { + "name": "mana_10" + } + ] +} \ No newline at end of file