* DoAfter support for Actions (#38253) * Adds Action DoAfter Events * Adds DoAfterArgs fields to DoAfterComp * Adds a base doafter action * Adds Attempt action doafter logic * Adds doafter logic to actions * Changes Action Attempt Doafter and action doafter to take in Performer and the original use delay. Use delay now triggers when a repeated action is cancelled. * Readds the TryPerformAction method and readds request perform action into the action doafter events * Adds a force skip to DoAfter Cancel so we can skip the complete check * Adds a Delay Reduction field to the comp and to the comp state * Fixes doafter mispredict, changes doafter comp check to a guard clause, sets delay reduction if it exists. * Cancels ActionDoAfter if charges is 0 * Serializes Attempt Frequency * Comment for rework * Changes todo into a comment * Moves doafterargs to doafterargscomp * Adds DoAfterArgs comp to BaseDoAfterAction * Removes unused trycomp with actionDoAfter * Replaces DoAfterRepateUseDelay const with timespan.zero * Removes unused usings * Makes SharedActionsSystem partial, adds DoAfter partial class to ActionSystem, moves ActionDoAfter logic to the SharedActionsSystem.DoAfter class * Cleanup and prediction * Renames OnActionDoAfterAttempt to OnActionDoAfter, moves both to Shared Action DoAfter * Removes ActionAttemptDoAfterEvent and moves its summaries to ActionDoAfterEvent. Converts OnActionDoAfterAttempt into TryStartActionDoAfter * Removes Extra check for charges and actiondoafters * Sloptimization * Cleanup * Cleanup * Adds param descs --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Refactor CP14 action emote and speech handling Moved emote and speech logic from magic spell components to dedicated CP14 action components and systems. Removed CP14MagicEffectEmotingComponent and related event handling, introducing CP14ActionEmotingComponent and updating event subscriptions. Updated resource consumption and performed logic to use new action-based components. Adjusted affected prototypes and removed obsolete code. * kicking in * Refactor athletic spell actions and remove mana cost calc Reworked dash and sprint spell YAMLs to use modular effect events and updated their action properties. Removed the unused CalculateManacost method from CP14SharedMagicSystem.cs. Also removed the startDelay property from the kick action. * fix cooldown and resource cost * casting visuals adapt * telegraphy adapt * slowdown adaption * Remove Lumera and Merkas demigod spells and skills Deleted all spell and skill prototypes related to the Lumera and Merkas demigods, including their actions, effects, and skill trees. Updated athletic sprint and second wind spells, and refactored portal_to_city spell to use modular effects. Also added a debug category to the admin skill reset verb. * fuck... * done * some refactor hell * light + lurker process * meta process * mobs process * vampire * finish * no clientside * Update water_creation.yml * fixes * Update ice_ghost.yml * а * viator review * fix lurker --------- Co-authored-by: keronshb <54602815+keronshb@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
272 lines
10 KiB
C#
272 lines
10 KiB
C#
using System.Linq;
|
|
using Content.Shared._CP14.Actions.Components;
|
|
using Content.Shared._CP14.MagicEnergy.Components;
|
|
using Content.Shared._CP14.MagicSpell.Events;
|
|
using Content.Shared._CP14.Religion.Components;
|
|
using Content.Shared._CP14.Skill.Components;
|
|
using Content.Shared.Actions.Components;
|
|
using Content.Shared.Actions.Events;
|
|
using Content.Shared.CombatMode.Pacification;
|
|
using Content.Shared.Damage.Components;
|
|
using Content.Shared.Hands.Components;
|
|
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.Actions;
|
|
|
|
public abstract partial class CP14SharedActionSystem
|
|
{
|
|
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;
|
|
|
|
if (!TryComp<ActionComponent>(ent, out var action))
|
|
return;
|
|
|
|
//Total mana required
|
|
var requiredMana = ent.Comp.ManaCost;
|
|
|
|
if (ent.Comp.CanModifyManacost)
|
|
{
|
|
var manaEv = new CP14CalculateManacostEvent(args.User, ent.Comp.ManaCost);
|
|
|
|
RaiseLocalEvent(args.User, manaEv);
|
|
|
|
if (action.Container is not null)
|
|
RaiseLocalEvent(action.Container.Value, manaEv);
|
|
|
|
requiredMana = manaEv.GetManacost();
|
|
}
|
|
|
|
//First - trying get mana from item
|
|
if (action.Container is not null &&
|
|
TryComp<CP14MagicEnergyContainerComponent>(action.Container, 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;
|
|
}
|
|
}
|
|
}
|
|
}
|