using System.Linq; using Content.Shared._CP14.MagicEnergy.Components; using Content.Shared._CP14.MagicSpell.Components; using Content.Shared._CP14.MagicSpell.Events; using Content.Shared._CP14.Religion.Components; using Content.Shared._CP14.Religion.Systems; using Content.Shared._CP14.Skill; using Content.Shared._CP14.Skill.Components; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage.Components; using Content.Shared.FixedPoint; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Popups; using Content.Shared.Speech.Muting; using Content.Shared.SSDIndicator; namespace Content.Shared._CP14.MagicSpell; public abstract partial class CP14SharedMagicSystem { [Dependency] private readonly CP14SharedReligionGodSystem _god = default!; [Dependency] private readonly SharedHandsSystem _hand = default!; [Dependency] private readonly CP14SharedSkillSystem _skill = default!; private void InitializeChecks() { SubscribeLocalEvent(OnSomaticCheck); SubscribeLocalEvent(OnVerbalCheck); SubscribeLocalEvent(OnMaterialCheck); SubscribeLocalEvent(OnManaCheck); SubscribeLocalEvent(OnStaminaCheck); SubscribeLocalEvent(OnSkillPointCheck); SubscribeLocalEvent(OnPacifiedCheck); SubscribeLocalEvent(OnSSDCheck); SubscribeLocalEvent(OnMobStateCheck); SubscribeLocalEvent(OnReligionRestrictedCheck); //Verbal speaking SubscribeLocalEvent(OnVerbalAspectStartCast); SubscribeLocalEvent(OnVerbalAspectAfterCast); SubscribeLocalEvent(OnEmoteStartCast); SubscribeLocalEvent(OnEmoteEndCast); //Consuming resources SubscribeLocalEvent(OnMaterialAspectEndCast); SubscribeLocalEvent(OnStaminaConsume); SubscribeLocalEvent(OnManaConsume); SubscribeLocalEvent(OnSkillPointConsume); } /// /// Before using a spell, a mana check is made for the amount of mana to show warnings. /// private void OnManaCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { //Total man required var requiredMana = CalculateManacost(ent, args.Performer); //First - trying get mana from item if (_magicEffectQuery.TryComp(ent, out var magicEffect)) { if (magicEffect.SpellStorage is not null && _magicContainerQuery.TryComp(magicEffect.SpellStorage, out var magicContainer)) requiredMana = MathF.Max(0, (float)(requiredMana - magicContainer.Energy)); } if (requiredMana <= 0) return; //Second - trying get mana from performer if (!_magicContainerQuery.TryComp(args.Performer, out var playerMana)) { args.PushReason(Loc.GetString("cp14-magic-spell-no-mana-component")); args.Cancel(); return; } if (!_magicEnergy.HasEnergy(args.Performer, requiredMana, playerMana, true)) _popup.PopupEntity(Loc.GetString($"cp14-magic-spell-not-enough-mana-cast-warning-{_random.Next(5)}"), args.Performer, args.Performer, PopupType.SmallCaution); } private void OnStaminaCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (!TryComp(args.Performer, out var staminaComp)) return; if (!staminaComp.Critical) return; args.PushReason(Loc.GetString("cp14-magic-spell-stamina-not-enough")); args.Cancel(); } private void OnSkillPointCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (!_proto.TryIndex(ent.Comp.SkillPoint, out var indexedSkillPoint) || ent.Comp.SkillPoint is null) return; if (!TryComp(args.Performer, out var skillStorage)) { args.PushReason(Loc.GetString("cp14-magic-spell-skillpoint-not-enough", ("name", Loc.GetString(indexedSkillPoint.Name)), ("count", ent.Comp.Count))); args.Cancel(); return; } var points = skillStorage.SkillPoints; if (points.TryGetValue(ent.Comp.SkillPoint.Value, out var currentPoints)) { var freePoints = currentPoints.Max - currentPoints.Sum; if (freePoints < ent.Comp.Count) { var d = ent.Comp.Count - freePoints; args.PushReason(Loc.GetString("cp14-magic-spell-skillpoint-not-enough", ("name", Loc.GetString(indexedSkillPoint.Name)), ("count", d))); args.Cancel(); } } } private void OnSomaticCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (TryComp(args.Performer, out var hands) || hands is not null) { if (_hand.CountFreeableHands((args.Performer, hands)) >= ent.Comp.FreeHandRequired) return; } args.PushReason(Loc.GetString("cp14-magic-spell-need-somatic-component")); args.Cancel(); } private void OnVerbalCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (!HasComp(args.Performer)) return; args.PushReason(Loc.GetString("cp14-magic-spell-need-verbal-component")); args.Cancel(); } private void OnMaterialCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (ent.Comp.Requirement is null) return; HashSet heldedItems = new(); foreach (var hand in _hand.EnumerateHands(args.Performer)) { var helded = _hand.GetHeldItem(args.Performer, hand); if (helded is not null) heldedItems.Add(helded.Value); } if (!ent.Comp.Requirement.CheckRequirement(EntityManager, _proto, heldedItems)) { args.PushReason(Loc.GetString("cp14-magic-spell-need-material-component")); args.Cancel(); } } private void OnPacifiedCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (!HasComp(args.Performer)) return; args.PushReason(Loc.GetString("cp14-magic-spell-pacified")); args.Cancel(); } private void OnSSDCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (args.Target is null) return; if (!TryComp(args.Target.Value, out var ssdIndication)) return; if (ssdIndication.IsSSD) { args.PushReason(Loc.GetString("cp14-magic-spell-ssd")); args.Cancel(); } } private void OnMobStateCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (args.Target is not { } target) return; if (!TryComp(target, out var mobStateComp)) { args.PushReason(Loc.GetString("cp14-magic-spell-target-not-mob")); args.Cancel(); return; } if (!ent.Comp.AllowedStates.Contains(mobStateComp.CurrentState)) { var states = string.Join(", ", ent.Comp.AllowedStates.Select(state => state switch { MobState.Alive => Loc.GetString("cp14-magic-spell-target-mob-state-live"), MobState.Dead => Loc.GetString("cp14-magic-spell-target-mob-state-dead"), MobState.Critical => Loc.GetString("cp14-magic-spell-target-mob-state-critical") })); args.PushReason(Loc.GetString("cp14-magic-spell-target-mob-state", ("state", states))); args.Cancel(); } } private void OnReligionRestrictedCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) { if (!TryComp(args.Performer, out var religionComp)) return; var position = args.Position; if (args.Target is not null) position ??= Transform(args.Target.Value).Coordinates; if (ent.Comp.OnlyInReligionZone) { if (position is null || !_god.InVision(position.Value, (args.Performer, religionComp))) { args.Cancel(); } } if (ent.Comp.OnlyOnFollowers) { if (args.Target is null || !TryComp(args.Target, out var follower) || follower.Religion != religionComp.Religion) { args.PushReason(Loc.GetString("cp14-magic-spell-target-god-follower")); args.Cancel(); } } } private void OnVerbalAspectStartCast(Entity ent, ref CP14StartCastMagicEffectEvent args) { var ev = new CP14SpellSpeechEvent { Performer = args.Performer, Speech = Loc.GetString(ent.Comp.StartSpeech), Emote = false, }; RaiseLocalEvent(ent, ref ev); } private void OnVerbalAspectAfterCast(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { var ev = new CP14SpellSpeechEvent { Performer = args.Performer, Speech = Loc.GetString(ent.Comp.EndSpeech), Emote = false }; RaiseLocalEvent(ent, ref ev); } private void OnEmoteStartCast(Entity ent, ref CP14StartCastMagicEffectEvent args) { var ev = new CP14SpellSpeechEvent { Performer = args.Performer, Speech = Loc.GetString(ent.Comp.StartEmote), Emote = true, }; RaiseLocalEvent(ent, ref ev); } private void OnEmoteEndCast(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { var ev = new CP14SpellSpeechEvent { Performer = args.Performer, Speech = Loc.GetString(ent.Comp.EndEmote), Emote = true }; RaiseLocalEvent(ent, ref ev); } private void OnMaterialAspectEndCast(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { if (ent.Comp.Requirement is null || args.Performer is null) return; HashSet heldedItems = new(); foreach (var hand in _hand.EnumerateHands(args.Performer.Value)) { var helded = _hand.GetHeldItem(args.Performer.Value, hand); if (helded is not null) heldedItems.Add(helded.Value); } ent.Comp.Requirement.PostCraft(EntityManager, _proto, heldedItems); } private void OnStaminaConsume(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { if (args.Performer is null) return; _stamina.TakeStaminaDamage(args.Performer.Value, ent.Comp.Stamina, visual: false); } private void OnManaConsume(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { if (!TryComp(ent, out var magicEffect)) return; var requiredMana = CalculateManacost(ent, args.Performer); //First - used object if (magicEffect.SpellStorage is not null && TryComp(magicEffect.SpellStorage, out var magicStorage)) { var spellEv = new CP14SpellFromSpellStorageUsedEvent(args.Performer, (ent, magicEffect), requiredMana); RaiseLocalEvent(magicEffect.SpellStorage.Value, ref spellEv); _magicEnergy.ChangeEnergy((magicEffect.SpellStorage.Value, magicStorage), -requiredMana, out var changedEnergy, out var overloadedEnergy, safe: false); requiredMana -= FixedPoint2.Abs(changedEnergy + overloadedEnergy); } //Second - action user if (requiredMana > 0 && TryComp(args.Performer, out var playerMana)) _magicEnergy.ChangeEnergy((args.Performer.Value, playerMana), -requiredMana, out _, out _, safe: false); } private void OnSkillPointConsume(Entity ent, ref CP14MagicEffectConsumeResourceEvent args) { if (!_proto.TryIndex(ent.Comp.SkillPoint, out var indexedSkillPoint) || ent.Comp.SkillPoint is null || args.Performer is null) return; _skill.RemoveSkillPoints(args.Performer.Value, ent.Comp.SkillPoint.Value, ent.Comp.Count); } }