Files
crystall-punk-14/Content.Shared/_CP14/Actions/CP14ActionSystem.Attempt.cs
Red 4debb114e5 Magic system refactor part 1 (#1709)
* Refactor pacified block logic to action system

Moved pacified block functionality from magic spell components to the new CP14ActionSystem and related components. Removed CP14MagicEffectPacifiedBlockComponent and its logic, and introduced CP14ActionDangerousComponent for handling pacified checks. Updated examine and checks logic to use the new action-based components, improving separation of concerns between magic and action systems.

* finish pacified

* mobtargetstate refactor

* skillpoint cost refactor

* somaticAspect refactor

* material cost refactor

* mana cost now

* stamina cost

* SSD + verbal aspect

* religion

* music tool refactor

* vampire

* get rid of this event

* manacost refac

* Remove magicType field from spell definitions

Eliminated the 'magicType' property from all CP14MagicEffect components in spell YAML files across Electric, Fire, Life, Light, and Water categories. This streamlines spell configuration and may reflect a change in how magic types are handled in the system.

* Remove mana cost reduction effects from skill tiers

Eliminated the ModifyManacost effects from tier 2 and tier 3 skills in electromancy, healing, hydrosophistry, illusion, and pyrokinetic. This change standardizes skill progression and may be part of a balance update to mana cost mechanics.

* comment out T3 tiers

* namespace refactor

* fix hands
2025-08-25 20:40:10 +03:00

281 lines
11 KiB
C#

using System.Linq;
using Content.Shared._CP14.Actions.Components;
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.Religion.Components;
using Content.Shared._CP14.Religion.Systems;
using Content.Shared._CP14.Skill.Components;
using Content.Shared.Actions.Events;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Damage.Components;
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;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared._CP14.Actions;
public abstract partial class CP14SharedActionSystem
{
[Dependency] private readonly SharedHandsSystem _hand = default!;
[Dependency] private readonly CP14SharedMagicEnergySystem _magicEnergy = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly CP14SharedReligionGodSystem _god = default!;
private void InitializeAttempts()
{
SubscribeLocalEvent<CP14ActionFreeHandsRequiredComponent, ActionAttemptEvent>(OnSomaticActionAttempt);
SubscribeLocalEvent<CP14ActionSpeakingComponent, ActionAttemptEvent>(OnVerbalActionAttempt);
SubscribeLocalEvent<CP14ActionMaterialCostComponent, ActionAttemptEvent>(OnMaterialActionAttempt);
SubscribeLocalEvent<CP14ActionManaCostComponent, ActionAttemptEvent>(OnManacostActionAttempt);
SubscribeLocalEvent<CP14ActionStaminaCostComponent, ActionAttemptEvent>(OnStaminaCostActionAttempt);
SubscribeLocalEvent<CP14ActionDangerousComponent, ActionAttemptEvent>(OnDangerousActionAttempt);
SubscribeLocalEvent<CP14ActionSkillPointCostComponent, ActionAttemptEvent>(OnSkillPointActionAttempt);
SubscribeLocalEvent<CP14ActionSSDBlockComponent, ActionValidateEvent>(OnActionSSDAttempt);
SubscribeLocalEvent<CP14ActionTargetMobStatusRequiredComponent, ActionValidateEvent>(OnTargetMobStatusRequiredValidate);
SubscribeLocalEvent<CP14ActionReligionRestrictedComponent, ActionValidateEvent>(OnReligionActionValidate);
}
/// <summary>
/// Before using a spell, a mana check is made for the amount of mana to show warnings.
/// </summary>
private void OnManacostActionAttempt(Entity<CP14ActionManaCostComponent> ent, ref ActionAttemptEvent args)
{
if (args.Cancelled)
return;
TryComp<CP14MagicEffectComponent>(ent, out var magicEffect);
//Total man required
var requiredMana = ent.Comp.ManaCost;
if (ent.Comp.CanModifyManacost)
{
var manaEv = new CP14CalculateManacostEvent(args.User, ent.Comp.ManaCost);
RaiseLocalEvent(args.User, manaEv);
if (magicEffect?.SpellStorage is not null)
RaiseLocalEvent(magicEffect.SpellStorage.Value, manaEv);
requiredMana = manaEv.GetManacost();
}
//First - trying get mana from item
if (magicEffect is not null && magicEffect.SpellStorage is not null &&
TryComp<CP14MagicEnergyContainerComponent>(magicEffect.SpellStorage, out var magicContainer))
requiredMana = MathF.Max(0, (float)(requiredMana - magicContainer.Energy));
if (requiredMana <= 0)
return;
//Second - trying get mana from performer
if (!TryComp<CP14MagicEnergyContainerComponent>(args.User, out var playerMana))
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-no-mana-component"), args.User, args.User);
args.Cancelled = true;
return;
}
if (!_magicEnergy.HasEnergy(args.User, requiredMana, playerMana, true) && _timing.IsFirstTimePredicted)
Popup.PopupClient(Loc.GetString($"cp14-magic-spell-not-enough-mana-cast-warning-{_random.Next(5)}"),
args.User,
args.User,
PopupType.SmallCaution);
}
private void OnStaminaCostActionAttempt(Entity<CP14ActionStaminaCostComponent> ent, ref ActionAttemptEvent args)
{
if (!TryComp<StaminaComponent>(args.User, out var staminaComp))
return;
if (!staminaComp.Critical)
return;
Popup.PopupClient(Loc.GetString("cp14-magic-spell-stamina-not-enough"), args.User, args.User);
args.Cancelled = true;
}
private void OnSomaticActionAttempt(Entity<CP14ActionFreeHandsRequiredComponent> ent, ref ActionAttemptEvent args)
{
if (args.Cancelled)
return;
if (TryComp<HandsComponent>(args.User, out var hands))
{
if (_hand.CountFreeHands((args.User, hands)) >= ent.Comp.FreeHandRequired)
return;
}
Popup.PopupClient(Loc.GetString("cp14-magic-spell-need-somatic-component"), args.User, args.User);
args.Cancelled = true;
}
private void OnVerbalActionAttempt(Entity<CP14ActionSpeakingComponent> ent, ref ActionAttemptEvent args)
{
if (!HasComp<MutedComponent>(args.User))
return;
Popup.PopupClient(Loc.GetString("cp14-magic-spell-need-verbal-component"), args.User, args.User);
args.Cancelled = true;
}
private void OnMaterialActionAttempt(Entity<CP14ActionMaterialCostComponent> ent, ref ActionAttemptEvent args)
{
if (args.Cancelled)
return;
if (ent.Comp.Requirement is null)
return;
HashSet<EntityUid> heldedItems = new();
foreach (var hand in _hand.EnumerateHands(args.User))
{
var helded = _hand.GetHeldItem(args.User, hand);
if (helded is not null)
heldedItems.Add(helded.Value);
}
if (!ent.Comp.Requirement.CheckRequirement(EntityManager, _proto, heldedItems))
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-need-material-component"), args.User, args.User);
args.Cancelled = true;
}
}
private void OnDangerousActionAttempt(Entity<CP14ActionDangerousComponent> ent, ref ActionAttemptEvent args)
{
if (args.Cancelled)
return;
if (HasComp<PacifiedComponent>(args.User))
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-pacified"), args.User, args.User);
args.Cancelled = true;
}
}
private void OnSkillPointActionAttempt(Entity<CP14ActionSkillPointCostComponent> ent, ref ActionAttemptEvent args)
{
if (!_proto.TryIndex(ent.Comp.SkillPoint, out var indexedSkillPoint) || ent.Comp.SkillPoint is null)
return;
if (!TryComp<CP14SkillStorageComponent>(args.User, out var skillStorage))
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-skillpoint-not-enough",
("name", Loc.GetString(indexedSkillPoint.Name)),
("count", ent.Comp.Count)),
args.User,
args.User);
args.Cancelled = true;
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;
Popup.PopupClient(Loc.GetString("cp14-magic-spell-skillpoint-not-enough",
("name", Loc.GetString(indexedSkillPoint.Name)),
("count", d)),
args.User,
args.User);
args.Cancelled = true;
}
}
}
private void OnTargetMobStatusRequiredValidate(Entity<CP14ActionTargetMobStatusRequiredComponent> ent,
ref ActionValidateEvent args)
{
if (args.Invalid)
return;
var target = GetEntity(args.Input.EntityTarget);
if (!TryComp<MobStateComponent>(target, out var mobStateComp))
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-target-not-mob"), args.User, args.User);
args.Invalid = true;
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")
}));
Popup.PopupClient(Loc.GetString("cp14-magic-spell-target-mob-state", ("state", states)),
args.User,
args.User);
args.Invalid = true;
}
}
private void OnActionSSDAttempt(Entity<CP14ActionSSDBlockComponent> ent, ref ActionValidateEvent args)
{
if (args.Invalid)
return;
if (!TryComp<SSDIndicatorComponent>(GetEntity(args.Input.EntityTarget), out var ssdIndication))
return;
if (ssdIndication.IsSSD)
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-ssd"), args.User, args.User);
args.Invalid = true;
}
}
private void OnReligionActionValidate(Entity<CP14ActionReligionRestrictedComponent> ent, ref ActionValidateEvent args)
{
if (args.Invalid)
return;
if (!TryComp<CP14ReligionEntityComponent>(args.User, out var religionComp))
return;
var position = GetCoordinates(args.Input.EntityCoordinatesTarget);
var target = GetEntity(args.Input.EntityTarget);
if (target is not null)
position ??= Transform(target.Value).Coordinates;
if (ent.Comp.OnlyInReligionZone)
{
if (position is null || !_god.InVision(position.Value, (args.User, religionComp)))
{
args.Invalid = true;
}
}
if (ent.Comp.OnlyOnFollowers)
{
if (target is null || !TryComp<CP14ReligionFollowerComponent>(target, out var follower) || follower.Religion != religionComp.Religion)
{
Popup.PopupClient(Loc.GetString("cp14-magic-spell-target-god-follower"), args.User, args.User);
args.Invalid = true;
}
}
}
}