using System.Text; 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._CP14.MagicSpellStorage; using Content.Shared.Actions; using Content.Shared.Damage.Systems; using Content.Shared.DoAfter; using Content.Shared.FixedPoint; using Content.Shared.Movement.Systems; using Content.Shared.Popups; using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; namespace Content.Shared._CP14.MagicSpell; /// /// This system handles the basic mechanics of spell use, such as doAfter, event invocation, and energy spending. /// public abstract partial class CP14SharedMagicSystem : EntitySystem { [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly SharedCP14MagicEnergySystem _magicEnergy = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly MetaDataSystem _meta = default!; [Dependency] private readonly SharedActionsSystem _action = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly IGameTiming _timing = default!; private EntityQuery _magicContainerQuery; private EntityQuery _magicEffectQuery; public override void Initialize() { base.Initialize(); InitializeDelayedActions(); InitializeToggleableActions(); InitializeInstantActions(); InitializeChecks(); InitializeSlowdown(); _magicContainerQuery = GetEntityQuery(); _magicEffectQuery = GetEntityQuery(); SubscribeLocalEvent(OnMagicEffectInit); SubscribeLocalEvent(OnMagicEffectShutdown); SubscribeLocalEvent(OnStartCast); SubscribeLocalEvent(OnEndCast); SubscribeLocalEvent(OnStaminaConsume); } private void OnStartCast(Entity ent, ref CP14StartCastMagicEffectEvent args) { var caster = EnsureComp(args.Performer); caster.CastedSpells.Add(ent); } private void OnEndCast(Entity ent, ref CP14EndCastMagicEffectEvent args) { if (!TryComp(args.Performer, out var caster)) return; caster.CastedSpells.Remove(ent); //Break all casts List castedSpells = new(); foreach (var casted in caster.CastedSpells) { castedSpells.Add(casted); } foreach (var casted in castedSpells) { if (!_magicEffectQuery.TryComp(casted, out var castedComp)) continue; _doAfter.Cancel(castedComp.ActiveDoAfter); } } public override void Update(float frameTime) { base.Update(frameTime); UpdateToggleableActions(); } /// /// Auto generation description for spell action /// private void OnMagicEffectInit(Entity ent, ref MapInitEvent args) { var meta = MetaData(ent); var sb = new StringBuilder(); sb.Append(meta.EntityDescription); if (TryComp(ent, out var manaCost) && manaCost.ManaCost > 0) { sb.Append($"\n\n{Loc.GetString("cp14-magic-manacost")}: [color=#5da9e8]{manaCost.ManaCost}[/color]"); } if (TryComp(ent, out var staminaCost) && staminaCost.Stamina > 0) { sb.Append($"\n\n{Loc.GetString("cp14-magic-staminacost")}: [color=#3fba54]{staminaCost.Stamina}[/color]"); } if (_proto.TryIndex(ent.Comp.MagicType, out var indexedMagic)) { sb.Append($"\n{Loc.GetString("cp14-magic-magic-type")}: [color={indexedMagic.Color.ToHex()}]{Loc.GetString(indexedMagic.Name)}[/color]"); } if (TryComp(ent, out var verbal)) { sb.Append("\n" + Loc.GetString("cp14-magic-verbal-aspect")); } if (TryComp(ent, out var somatic)) { sb.Append("\n" + Loc.GetString("cp14-magic-somatic-aspect") + " " + somatic.FreeHandRequired); } if (TryComp(ent, out var music)) { sb.Append("\n" + Loc.GetString("cp14-magic-music-aspect")); } _meta.SetEntityDescription(ent, sb.ToString()); } private void OnMagicEffectShutdown(Entity ent, ref ComponentShutdown args) { if (_doAfter.IsRunning(ent.Comp.ActiveDoAfter)) _doAfter.Cancel(ent.Comp.ActiveDoAfter); } /// /// Checking to see if the spell can be used at all /// private bool CanCastSpell(Entity ent, EntityUid performer) { var ev = new CP14CastMagicEffectAttemptEvent(performer); RaiseLocalEvent(ent, ref ev); if (ev.Reason != string.Empty && _net.IsServer) { _popup.PopupEntity(ev.Reason, performer, performer); } return !ev.Cancelled; } private void CastTelegraphy(Entity ent, CP14SpellEffectBaseArgs args) { if (!_net.IsServer) return; foreach (var effect in ent.Comp.TelegraphyEffects) { effect.Effect(EntityManager, args); } } private void CastSpell(Entity ent, CP14SpellEffectBaseArgs args) { var ev = new CP14MagicEffectConsumeResourceEvent(args.User); RaiseLocalEvent(ent, ref ev); if (_net.IsServer) { foreach (var effect in ent.Comp.Effects) { effect.Effect(EntityManager, args); } } } protected FixedPoint2 CalculateManacost(Entity ent, EntityUid? caster) { var manaCost = ent.Comp.ManaCost; if (ent.Comp.CanModifyManacost && _magicEffectQuery.TryComp(ent, out var magicEffect)) { var manaEv = new CP14CalculateManacostEvent(caster, ent.Comp.ManaCost, magicEffect.MagicType); if (caster is not null) RaiseLocalEvent(caster.Value, manaEv); if (magicEffect.SpellStorage is not null) RaiseLocalEvent(magicEffect.SpellStorage.Value, manaEv); manaCost = manaEv.GetManacost(); } return manaCost; } private void OnStaminaConsume(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { if (args.Performer is null) return; _stamina.TakeStaminaDamage(args.Performer.Value, ent.Comp.Stamina, visual: false); } }