diff --git a/Content.Server/Stories/Abilities/AbilitiesSystem.cs b/Content.Server/Stories/Abilities/AbilitiesSystem.cs new file mode 100644 index 0000000000..7366c834b7 --- /dev/null +++ b/Content.Server/Stories/Abilities/AbilitiesSystem.cs @@ -0,0 +1,114 @@ +using Content.Server.Administration.Systems; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Emp; +using Content.Server.Flash; +using Content.Server.Lightning; +using Content.Shared.Speech.Muting; +using Content.Shared.StatusEffect; +using Content.Shared.Stories.Abilities; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Server.Stories.Abilities; + +public sealed partial class AbilitiesSystem : SharedAbilitiesSystem +{ + [Dependency] private readonly LightningSystem _lightning = default!; + [Dependency] private readonly FlammableSystem _flammable = default!; + [Dependency] private readonly EmpSystem _emp = default!; + [Dependency] private readonly RejuvenateSystem _rejuvenate = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; + [Dependency] private readonly FlashSystem _flash = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + + public readonly ProtoId MutedStatusEffect = "Muted"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRejuvenate); + SubscribeLocalEvent(OnEmp); + SubscribeLocalEvent(OnIgnite); + SubscribeLocalEvent(OnLightning); + SubscribeLocalEvent(OnFlashAreaEvent); + SubscribeLocalEvent(OnRangedGlare); + } + + private void OnRangedGlare(RangedGlareEvent args) + { + if (args.Handled) + return; + + var modifier = args.RequiredRange / (_xform.GetMapCoordinates(args.Performer).Position - _xform.GetMapCoordinates(args.Target).Position).Length(); + modifier = modifier < 1 ? modifier : 1; + + _flash.Flash(args.Target, args.Performer, null, args.Duration * modifier, args.SlowTo * modifier, false); + _statusEffects.TryAddStatusEffect(args.Target, MutedStatusEffect, TimeSpan.FromSeconds(args.Duration * modifier / 1000f), true); + + args.Handled = true; + } + + private void OnFlashAreaEvent(FlashAreaEvent args) + { + if (args.Handled) + return; + + _flash.FlashArea(args.Performer, args.Performer, args.Range, args.FlashDuration, slowTo: args.SlowTo, sound: args.Sound); + + args.Handled = true; + } + + private void OnLightning(ShootLightningsTargetEvent args) + { + if (args.Handled) + return; + + var performerCoords = _xform.GetMapCoordinates(args.Performer); + HashSet lightningsCoords = new(); + + foreach (var vector in args.Vectors) + { + lightningsCoords.Add(new MapCoordinates(performerCoords.Position + vector, performerCoords.MapId)); + } + + foreach (var coordinates in lightningsCoords) + { + var user = Spawn(null, coordinates); + _lightning.ShootLightning(user, args.Target, args.LightningPrototype, args.TriggerLightningEvents); + } + + args.Handled = true; + } + + private void OnRejuvenate(RejuvenateActionEvent args) + { + if (args.Handled) + return; + + _rejuvenate.PerformRejuvenate(args.Performer); + + args.Handled = true; + } + + private void OnIgnite(IgniteTargetActionEvent args) + { + if (args.Handled) + return; + + _flammable.AdjustFireStacks(args.Target, args.StackAmount); + _flammable.Ignite(args.Target, args.Performer); + + args.Handled = true; + } + + private void OnEmp(EmpActionEvent args) + { + if (args.Handled) + return; + + _emp.EmpPulse(_xform.GetMapCoordinates(args.Performer), args.Range, args.EnergyConsumption, args.DisableDuration); + + args.Handled = true; + } +} diff --git a/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.Lightning.cs b/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.Lightning.cs index 176e53a6a9..6edee76bc9 100644 --- a/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.Lightning.cs +++ b/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.Lightning.cs @@ -5,32 +5,5 @@ namespace Content.Server.Stories.ForceUser; public sealed partial class ForceUserSystem { - public void InitializeLightning() - { - SubscribeLocalEvent(OnLightning); - } - private void OnLightning(LightningStrikeEvent args) - { - if (args.Handled) - return; - var xform = Transform(args.Performer); - - var coord = _xform.GetMapCoordinates(xform); - - HashSet coords = new() - { - new MapCoordinates(coord.Position + new Vector2(0, 1), coord.MapId), - new MapCoordinates(coord.Position + new Vector2(1, -1), coord.MapId), - new MapCoordinates(coord.Position + new Vector2(-1, -1), coord.MapId) - }; - - foreach (var coordinates in coords) - { - var user = Spawn(null, coordinates); - _lightning.ShootLightning(user, args.Target); - } - - args.Handled = true; - } } diff --git a/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.cs b/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.cs index b325c8041f..3511c0457d 100644 --- a/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.cs +++ b/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.Actions.cs @@ -15,15 +15,6 @@ public sealed partial class ForceUserSystem { public void InitializeSimpleActions() { - SubscribeLocalEvent(OnFlashAreaEvent); - SubscribeLocalEvent(OnEmp); - SubscribeLocalEvent(OnRejuvenate); - SubscribeLocalEvent(OnFreedom); - // SubscribeLocalEvent(OnStrike); - SubscribeLocalEvent(OnIgnite); - SubscribeLocalEvent(OnPulseEvent); - SubscribeLocalEvent(OnDash); - SubscribeLocalEvent(OnHypnosis); // FIXME: Тут не должно быть этого - start SubscribeLocalEvent(OnFrozeBullets); SubscribeLocalEvent(OnShop); @@ -58,84 +49,10 @@ private void OnShop(EntityUid uid, ForceUserComponent component, ForceShopAction return; _store.ToggleUi(uid, uid, store); } - private void OnRejuvenate(RejuvenateActionEvent args) - { - if (args.Handled) return; - _rejuvenate.PerformRejuvenate(args.Performer); - args.Handled = true; - } - private void OnPulseEvent(RecliningPulseEvent args) - { - if (args.Handled) return; - - var xform = Transform(args.Performer); - var range = args.Range; - var strength = args.Strength; - var lookup = _lookup.GetEntitiesInRange(args.Performer, range, LookupFlags.Dynamic | LookupFlags.Sundries); - var xformQuery = GetEntityQuery(); - var worldPos = _xform.GetWorldPosition(xform, xformQuery); - var physQuery = GetEntityQuery(); - - foreach (var ent in lookup) - { - if (physQuery.TryGetComponent(ent, out var phys) - && (phys.CollisionMask & (int) CollisionGroup.GhostImpassable) != 0) - continue; - - var foo = _xform.GetWorldPosition(ent, xformQuery) - worldPos; - _throwing.TryThrow(ent, foo * 10, strength, args.Performer, 0); - - if (_force.TryRemoveVolume(ent, _random.Next(10, 30))) - _popup.PopupEntity("Устоял!", ent); - else - _stun.TryParalyze(ent, TimeSpan.FromSeconds(args.StunTime), true); - } - - args.Handled = true; - } - private void OnFlashAreaEvent(FlashAreaEvent args) - { - if (args.Handled) return; - _flashSystem.FlashArea(args.Performer, args.Performer, args.Range, args.FlashDuration, slowTo: args.SlowTo, sound: args.Sound); - args.Handled = true; - } - private void OnDash(ForceDashActionEvent args) - { - if (args.Handled) return; - _throwing.TryThrow(args.Performer, args.Target, args.Strength); - args.Handled = true; - } private void OnHypnosis(HypnosisTargetActionEvent args) { if (args.Handled || _mobState.IsIncapacitated(args.Target) || HasComp(args.Target)) return; _conversion.TryConvert(args.Target, "HypnotizedEmpire", args.Performer); // FIXME: Hardcode. Исправим в обновлении инквизитора. args.Handled = true; } - private void OnIgnite(IgniteTargetActionEvent args) - { - if (args.Handled || _mobState.IsIncapacitated(args.Target) || HasComp(args.Target)) return; // FIXME: Hardcode - - _flammable.AdjustFireStacks(args.Target, args.StackAmount); - _flammable.Ignite(args.Target, args.Performer); - - args.Handled = true; - } - private void OnStrike(LightningStrikeEvent args) - { - if (args.Handled) return; - _lightning.ShootLightning(args.Performer, args.Target); - args.Handled = true; - } - private void OnFreedom(FreedomActionEvent args) - { - if (!TryComp(args.Performer, out var cuffs) || cuffs.Container.ContainedEntities.Count < 1) return; - _cuffable.Uncuff(args.Performer, cuffs.LastAddedCuffs, cuffs.LastAddedCuffs); - args.Handled = true; - } - private void OnEmp(EmpActionEvent args) - { - if (args.Handled) return; - _emp.EmpPulse(_xform.GetMapCoordinates(args.Performer), args.Range, args.EnergyConsumption, args.DisableDuration); - args.Handled = true; - } } diff --git a/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.cs b/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.cs index a98e5d7464..46c7baa2c4 100644 --- a/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.cs +++ b/Content.Server/Stories/ForceUser/Systems/Actions/ForceUserSystem.cs @@ -90,7 +90,7 @@ public override void Initialize() InitializePolymorph(); InitializeProtectiveBubble(); InitializeLightsaber(); - InitializeLightning(); + // InitializeLightning(); InitializeSteal(); } public override void Update(float frameTime) diff --git a/Content.Shared/DoAfter/DoAfterArgs.cs b/Content.Shared/DoAfter/DoAfterArgs.cs index ac66278538..a1252089b3 100644 --- a/Content.Shared/DoAfter/DoAfterArgs.cs +++ b/Content.Shared/DoAfter/DoAfterArgs.cs @@ -11,7 +11,7 @@ public sealed partial class DoAfterArgs /// The entity invoking do_after /// [NonSerialized] - [DataField("user", required: true)] + [DataField("user")] public EntityUid User; public NetEntity NetUser; @@ -19,7 +19,7 @@ public sealed partial class DoAfterArgs /// /// How long does the do_after require to complete /// - [DataField(required: true)] + [DataField] public TimeSpan Delay; /// @@ -50,7 +50,7 @@ public sealed partial class DoAfterArgs /// /// The event that will get raised when the DoAfter has finished. If null, this will simply raise a /// - [DataField(required: true)] + [DataField] public DoAfterEvent Event = default!; /// diff --git a/Content.Shared/Stories/Abilities/AbilitiesActions.DoAfter.cs b/Content.Shared/Stories/Abilities/AbilitiesActions.DoAfter.cs new file mode 100644 index 0000000000..f859fc07d4 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesActions.DoAfter.cs @@ -0,0 +1,189 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared.Stories.Abilities; + +public sealed partial class StartDoAfterWithTargetEvent : EntityTargetActionEvent +{ + [DataField(required: true)] + public DoAfterData DoAfterArgs; + + [DataField(required: true)] + public EntityTargetActionEvent Event = default!; +} + +[Serializable, NetSerializable] +public sealed partial class EntityTargetActionDoAfterEvent : SimpleDoAfterEvent +{ + [DataField] + [NonSerialized] + public EntityTargetActionEvent? Event; +} + +public sealed partial class StartDoAfterEvent : InstantActionEvent +{ + [DataField(required: true)] + public DoAfterData DoAfterArgs; + + [DataField(required: true)] + public InstantActionEvent Event = default!; +} + +[Serializable, NetSerializable] +public sealed partial class InstantActionDoAfterEvent : SimpleDoAfterEvent +{ + [DataField] + [NonSerialized] + public InstantActionEvent? Event; +} + +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class DoAfterData +{ + /// + /// How long does the do_after require to complete + /// + [DataField] + public TimeSpan Delay; + + /// + /// Whether the progress bar for this DoAfter should be hidden from other players. + /// + [DataField] + public bool Hidden; + + #region Event options + + /// + /// This option determines how frequently the DoAfterAttempt event will get raised. Defaults to never raising the + /// event. + /// + [DataField("attemptEventFrequency")] + public AttemptFrequency AttemptFrequency; + #endregion + + #region Break/Cancellation Options + // Break the chains + + /// + /// Whether or not this do after requires the user to have hands. + /// + [DataField] + public bool NeedHand; + + /// + /// Whether we need to keep our active hand as is (i.e. can't change hand or change item). This also covers + /// requiring the hand to be free (if applicable). This does nothing if is false. + /// + [DataField] + public bool BreakOnHandChange = true; + + /// + /// Whether the do-after should get interrupted if we drop the + /// active item we started the do-after with + /// This does nothing if is false. + /// + [DataField] + public bool BreakOnDropItem = true; + + /// + /// If do_after stops when the user or target moves + /// + [DataField] + public bool BreakOnMove; + + /// + /// Whether to break on movement when the user is weightless. + /// This does nothing if is false. + /// + [DataField] + public bool BreakOnWeightlessMove = true; + + /// + /// Threshold for user and target movement + /// + [DataField] + public float MovementThreshold = 0.3f; + + /// + /// Threshold for distance user from the used OR target entities. + /// + [DataField] + public float? DistanceThreshold; + + /// + /// Whether damage will cancel the DoAfter. See also . + /// + [DataField] + public bool BreakOnDamage; + + /// + /// Threshold for user damage. This damage has to be dealt in a single event, not over time. + /// + [DataField] + public FixedPoint2 DamageThreshold = 1; + + /// + /// If true, this DoAfter will be canceled if the user can no longer interact with the target. + /// + [DataField] + public bool RequireCanInteract = true; + #endregion + + #region Duplicates + /// + /// If true, this will prevent duplicate DoAfters from being started See also . + /// + /// + /// Note that this will block even if the duplicate is cancelled because either DoAfter had + /// enabled. + /// + [DataField] + public bool BlockDuplicate = true; + + //TODO: User pref to not cancel on second use on specific doafters + /// + /// If true, this will cancel any duplicate DoAfters when attempting to add a new DoAfter. See also + /// . + /// + [DataField] + public bool CancelDuplicate = true; + + /// + /// These flags determine what DoAfter properties are used to determine whether one DoAfter is a duplicate of + /// another. + /// + /// + /// Note that both DoAfters may have their own conditions, and they will be considered duplicated if either set + /// of conditions is satisfied. + /// + [DataField] + public DuplicateConditions DuplicateCondition = DuplicateConditions.All; + #endregion + + public DoAfterArgs Apply(DoAfterArgs args) + { + return new DoAfterArgs(args) + { + Delay = Delay, + Hidden = Hidden, + AttemptFrequency = AttemptFrequency, + NeedHand = NeedHand, + BreakOnHandChange = BreakOnHandChange, + BreakOnDropItem = BreakOnDropItem, + BreakOnMove = BreakOnMove, + BreakOnWeightlessMove = BreakOnWeightlessMove, + MovementThreshold = MovementThreshold, + DistanceThreshold = DistanceThreshold, + BreakOnDamage = BreakOnDamage, + DamageThreshold = DamageThreshold, + RequireCanInteract = RequireCanInteract, + BlockDuplicate = BlockDuplicate, + CancelDuplicate = CancelDuplicate, + DuplicateCondition = DuplicateCondition, + }; + } +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesActions.InRange.cs b/Content.Shared/Stories/Abilities/AbilitiesActions.InRange.cs new file mode 100644 index 0000000000..0690450841 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesActions.InRange.cs @@ -0,0 +1,28 @@ +using Content.Shared.Actions; +using Content.Shared.Whitelist; + +namespace Content.Shared.Stories.Abilities; + +public sealed partial class ApplyInRangeEvent : InstantActionEvent +{ + [DataField] + public bool IncludePerformer { get; set; } = false; + + [DataField] + public float Range { get; set; } = 7.5f; + + [DataField] + public int MaxTargets { get; set; } = 5; + + [DataField] + public bool CheckCanAccess { get; set; } = true; + + [DataField] + public EntityWhitelist? Whitelist { get; set; } + + [DataField] + public EntityWhitelist? Blacklist { get; set; } + + [DataField(required: true)] + public EntityTargetActionEvent Event = default!; +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesActions.Reagent.cs b/Content.Shared/Stories/Abilities/AbilitiesActions.Reagent.cs new file mode 100644 index 0000000000..476b6300d1 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesActions.Reagent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Actions; +using Content.Shared.Chemistry.Components; +using Content.Shared.Whitelist; + +namespace Content.Shared.Stories.Abilities; + +public sealed partial class InjectSolutionEvent : InstantActionEvent +{ + [DataField] + public Solution Solution { get; set; } = new(); + + [DataField] + public string TargetSolution { get; set; } = "chemicals"; +} + +public sealed partial class InjectSolutionToTargetEvent : EntityTargetActionEvent +{ + [DataField] + public Solution Solution { get; set; } = new(); + + [DataField] + public string TargetSolution { get; set; } = "chemicals"; +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesActions.cs b/Content.Shared/Stories/Abilities/AbilitiesActions.cs new file mode 100644 index 0000000000..cff3f853a5 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesActions.cs @@ -0,0 +1,101 @@ +using System.Numerics; +using Content.Shared.Actions; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +/// +/// Abilities - это большой набор способностей, которые не требуют наличия особых компонентов для активации. +/// Конструктор с кучей вложенностей... +/// + +namespace Content.Shared.Stories.Abilities; + +public sealed partial class ShootLightningsTargetEvent : EntityTargetActionEvent +{ + [DataField] + public List Vectors = new() + { + new(0, 1), + new(1, -1), + new(-1, -1), + }; + + [DataField] + public EntProtoId LightningPrototype = "Lightning"; + + [DataField] + public bool TriggerLightningEvents = true; +} + +public sealed partial class EmpActionEvent : InstantActionEvent +{ + [DataField] + public float Range = 1.0f; + + [DataField] + public float EnergyConsumption; + + [DataField] + public float DisableDuration = 60f; +} + +public sealed partial class FreedomActionEvent : InstantActionEvent { } + +public sealed partial class RejuvenateActionEvent : InstantActionEvent { } + +public sealed partial class FlashAreaEvent : InstantActionEvent +{ + [DataField("duration")] + public int FlashDuration { get; set; } = 8000; + + [DataField] + public float Range { get; set; } = 7f; + + [DataField] + public float SlowTo { get; set; } = 0.5f; + + [DataField] + public SoundSpecifier Sound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/flash.ogg"); +} + +public sealed partial class PushTargetEvent : EntityTargetActionEvent +{ + [DataField] + public float ParalyzeTime { get; set; } = 3f; + + [DataField] + public int Strength { get; set; } = 10; + + [DataField] + public float DistanceModifier { get; set; } = 2f; + + [DataField] + public SoundSpecifier? Sound; +} + +public sealed partial class IgniteTargetActionEvent : EntityTargetActionEvent +{ + [DataField] + public float StackAmount = 1f; +} + +public sealed partial class ThrownDashActionEvent : WorldTargetActionEvent +{ + [DataField] + public float Strength = 10; +} + +public sealed partial class RangedGlareEvent : EntityTargetActionEvent +{ + [DataField] + public int Duration { get; set; } = 15000; + + [DataField] + public float SlowTo { get; set; } = 0.8f; + + [DataField] + public SoundSpecifier Sound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/flash.ogg"); + + [DataField] + public float RequiredRange { get; set; } = 1.5f; +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesSystem.DoAfter.cs b/Content.Shared/Stories/Abilities/AbilitiesSystem.DoAfter.cs new file mode 100644 index 0000000000..2a17b729b0 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesSystem.DoAfter.cs @@ -0,0 +1,75 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; + +namespace Content.Shared.Stories.Abilities; + +public abstract partial class SharedAbilitiesSystem +{ + public void InitializeDoAfter() + { + SubscribeLocalEvent(OnStartDoAfterWithTargetEvent); + SubscribeLocalEvent(OnStartDoAfterEvent); + + SubscribeLocalEvent(OnEntityTargetActionDoAfterEvent); + SubscribeLocalEvent(OnInstantActionDoAfterEvent); + } + + private void OnInstantActionDoAfterEvent(EntityUid uid, MetaDataComponent component, InstantActionDoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Target == null || args.Event == null) + return; + + args.Event.Handled = false; + args.Event.Performer = args.User; + + RaiseLocalEvent(args.User, (object)args.Event, broadcast: true); + + args.Handled = true; + } + + private void OnEntityTargetActionDoAfterEvent(EntityUid uid, MetaDataComponent component, EntityTargetActionDoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Target == null || args.Event == null) + return; + + args.Event.Handled = false; + args.Event.Performer = args.User; + args.Event.Target = args.Target.Value; + + RaiseLocalEvent(args.User, (object)args.Event, broadcast: true); + + args.Handled = true; + } + + private void OnStartDoAfterWithTargetEvent(StartDoAfterWithTargetEvent args) + { + if (args.Handled) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, args.DoAfterArgs.Delay, new EntityTargetActionDoAfterEvent() { Event = args.Event }, args.Target, args.Target) + { + // Необходимо для EntityTargetActionDoAfterEvent + Broadcast = true, + }; + + doAfterEventArgs = args.DoAfterArgs.Apply(doAfterEventArgs); + + args.Handled = _doAfterSystem.TryStartDoAfter(doAfterEventArgs); + } + + private void OnStartDoAfterEvent(StartDoAfterEvent args) + { + if (args.Handled) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, args.DoAfterArgs.Delay, new InstantActionDoAfterEvent() { Event = args.Event }, args.Performer, args.Performer) + { + // Необходимо для InstantActionDoAfterEvent + Broadcast = true, + }; + + doAfterEventArgs = args.DoAfterArgs.Apply(doAfterEventArgs); + + args.Handled = _doAfterSystem.TryStartDoAfter(doAfterEventArgs); + } +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesSystem.InRange.cs b/Content.Shared/Stories/Abilities/AbilitiesSystem.InRange.cs new file mode 100644 index 0000000000..bb243816d9 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesSystem.InRange.cs @@ -0,0 +1,55 @@ + +using Content.Shared.Physics; + +/// +/// Abilities In Range - функционал для запуска по нескольким сущностям. +/// +namespace Content.Shared.Stories.Abilities; + +public abstract partial class SharedAbilitiesSystem +{ + public void InitializeInRange() + { + SubscribeLocalEvent(OnApplyInRangeEvent); + } + + private void OnApplyInRangeEvent(ApplyInRangeEvent args) + { + if (args.Handled) + return; + + var entities = _entityLookup.GetEntitiesInRange(Transform(args.Performer).Coordinates, args.Range); + + var appliedAmount = 0; + + foreach (var entity in entities) + { + if (entity != args.Performer) + { + + if (_whitelist.IsWhitelistFail(args.Whitelist, entity)) + continue; + + if (_whitelist.IsBlacklistPass(args.Blacklist, entity)) + continue; + + if (args.CheckCanAccess && !_interaction.InRangeUnobstructed(args.Performer, entity, range: args.Range, collisionMask: CollisionGroup.Opaque)) + continue; + + } + else if (!args.IncludePerformer) + continue; + + args.Event.Handled = false; + args.Event.Performer = args.Performer; + args.Event.Target = entity; + + RaiseLocalEvent(entity, (object)args.Event, broadcast: true); + + if (args.Event.Handled) + appliedAmount++; + } + + args.Handled = appliedAmount > 0; + } +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesSystem.Reagent.cs b/Content.Shared/Stories/Abilities/AbilitiesSystem.Reagent.cs new file mode 100644 index 0000000000..e12f4746f5 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesSystem.Reagent.cs @@ -0,0 +1,31 @@ +using Content.Shared.Chemistry.Components.SolutionManager; + +namespace Content.Shared.Stories.Abilities; + +public abstract partial class SharedAbilitiesSystem +{ + public void InitializeReagent() + { + SubscribeLocalEvent(OnInjectEvent); + SubscribeLocalEvent(OnIjectToTargetEvent); + } + + private void OnInjectEvent(InjectSolutionEvent args) + { + if (args.Handled || + !_solution.TryGetSolution(args.Performer, args.TargetSolution, out var solution)) + return; + + args.Handled = _solution.TryAddSolution(solution.Value, args.Solution); + } + + private void OnIjectToTargetEvent(InjectSolutionToTargetEvent args) + { + if (args.Handled || + !_solution.TryGetSolution(args.Target, args.TargetSolution, out var solution)) + return; + + args.Handled = _solution.TryAddSolution(solution.Value, args.Solution); + } + +} diff --git a/Content.Shared/Stories/Abilities/AbilitiesSystem.cs b/Content.Shared/Stories/Abilities/AbilitiesSystem.cs new file mode 100644 index 0000000000..f224724380 --- /dev/null +++ b/Content.Shared/Stories/Abilities/AbilitiesSystem.cs @@ -0,0 +1,79 @@ +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Cuffs; +using Content.Shared.Cuffs.Components; +using Content.Shared.DoAfter; +using Content.Shared.Emp; +using Content.Shared.Flash; +using Content.Shared.Interaction; +using Content.Shared.Rejuvenate; +using Content.Shared.Stunnable; +using Content.Shared.Throwing; +using Content.Shared.Whitelist; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Map; + +namespace Content.Shared.Stories.Abilities; + +public abstract partial class SharedAbilitiesSystem : EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly SharedCuffableSystem _cuffable = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + + public override void Initialize() + { + base.Initialize(); + InitializeDoAfter(); + InitializeReagent(); + InitializeInRange(); + + SubscribeLocalEvent(OnFreedom); + SubscribeLocalEvent(OnPushTargetEvent); + SubscribeLocalEvent(OnDash); + } + + private void OnPushTargetEvent(PushTargetEvent args) + { + if (args.Handled) + return; + + var performer = args.Performer; + var target = args.Target; + var strength = args.Strength; + + _stun.TryParalyze(target, TimeSpan.FromSeconds(args.ParalyzeTime), true); + _throwing.TryThrow(target, (_xform.GetWorldPosition(target) - _xform.GetWorldPosition(performer)) * args.DistanceModifier, strength, args.Performer, 0, playSound: args.Sound != null); + + _audio.PlayPvs(args.Sound, performer); + + args.Handled = true; + } + + private void OnDash(ThrownDashActionEvent args) + { + if (args.Handled) + return; + + _throwing.TryThrow(args.Performer, args.Target, args.Strength); + + args.Handled = true; + } + + private void OnFreedom(FreedomActionEvent args) + { + if (!TryComp(args.Performer, out var cuffs) || cuffs.Container.ContainedEntities.Count < 1) + return; + + _cuffable.Uncuff(args.Performer, cuffs.LastAddedCuffs, cuffs.LastAddedCuffs); + + args.Handled = true; + } + +} diff --git a/Content.Shared/Stories/DoAfter/SharedDoAfterTargetSystem.cs b/Content.Shared/Stories/DoAfter/SharedDoAfterTargetSystem.cs deleted file mode 100644 index c2c5342aa9..0000000000 --- a/Content.Shared/Stories/DoAfter/SharedDoAfterTargetSystem.cs +++ /dev/null @@ -1,324 +0,0 @@ -using Content.Shared.DoAfter; -using Content.Shared.Actions; -using Robust.Shared.Serialization; -using Content.Shared.FixedPoint; - -namespace Content.Shared.Stories.DoAfter; -public sealed partial class SharedDoAfterTargetSystem : EntitySystem -{ - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - public override void Initialize() - { - SubscribeLocalEvent(OnDoAfterTargetEvent); - SubscribeLocalEvent(OnEntityTargetActionDoAfterEvent); - SubscribeLocalEvent(OnDoAfterUserEvent); - SubscribeLocalEvent(OnInstantActionDoAfterEvent); - } - private void OnInstantActionDoAfterEvent(EntityUid uid, MetaDataComponent component, InstantActionDoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Target == null || args.Event == null) - return; - - args.Event.Handled = false; - args.Event.Performer = args.User; - - RaiseLocalEvent(args.User, (object) args.Event, broadcast: true); - - args.Handled = true; - } - private void OnEntityTargetActionDoAfterEvent(EntityUid uid, MetaDataComponent component, EntityTargetActionDoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Target == null || args.Event == null) - return; - - args.Event.Handled = false; - args.Event.Performer = args.User; - args.Event.Target = args.Target.Value; - - RaiseLocalEvent(args.User, (object) args.Event, broadcast: true); - - args.Handled = true; - } - private void OnDoAfterTargetEvent(DoAfterTargetEvent args) - { - if (args.Handled) - return; - - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, args.Delay, new EntityTargetActionDoAfterEvent() { Event = (EntityTargetActionEvent) args.Event }, args.Target, args.Target) - { - NeedHand = args.NeedHand, - Hidden = args.Hidden, - AttemptFrequency = args.AttemptFrequency, - Broadcast = args.Broadcast, - BreakOnHandChange = args.BreakOnHandChange, - BreakOnMove = args.BreakOnMove, - BreakOnWeightlessMove = args.BreakOnWeightlessMove, - BreakOnDamage = args.BreakOnDamage, - DamageThreshold = args.DamageThreshold, - BlockDuplicate = args.BlockDuplicate, - CancelDuplicate = args.CancelDuplicate, - DistanceThreshold = args.DistanceThreshold, - DuplicateCondition = args.DuplicateCondition, - MovementThreshold = args.MovementThreshold, - RequireCanInteract = args.RequireCanInteract, - EventTarget = args.Target, - }; - - if (_doAfterSystem.TryStartDoAfter(doAfterEventArgs)) - args.Handled = true; - } - private void OnDoAfterUserEvent(DoAfterUserEvent args) - { - if (args.Handled) - return; - - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.Performer, args.Delay, new InstantActionDoAfterEvent() { Event = (InstantActionEvent) args.Event }, args.Performer, args.Performer) - { - NeedHand = args.NeedHand, - Hidden = args.Hidden, - AttemptFrequency = args.AttemptFrequency, - Broadcast = args.Broadcast, - BreakOnHandChange = args.BreakOnHandChange, - BreakOnMove = args.BreakOnMove, - BreakOnWeightlessMove = args.BreakOnWeightlessMove, - BreakOnDamage = args.BreakOnDamage, - DamageThreshold = args.DamageThreshold, - BlockDuplicate = args.BlockDuplicate, - CancelDuplicate = args.CancelDuplicate, - DistanceThreshold = args.DistanceThreshold, - DuplicateCondition = args.DuplicateCondition, - MovementThreshold = args.MovementThreshold, - RequireCanInteract = args.RequireCanInteract, - EventTarget = args.Performer, - }; - - if (_doAfterSystem.TryStartDoAfter(doAfterEventArgs)) - args.Handled = true; - } -} - -[Serializable, NetSerializable] -public sealed partial class InstantActionDoAfterEvent : SimpleDoAfterEvent -{ - [DataField("event")] - [NonSerialized] - public InstantActionEvent? Event; -} -[Serializable, NetSerializable] -public sealed partial class EntityTargetActionDoAfterEvent : SimpleDoAfterEvent -{ - [DataField("event")] - [NonSerialized] - public EntityTargetActionEvent? Event; -} -public sealed partial class DoAfterTargetEvent : EntityTargetActionEvent -{ - /// - /// How long does the do_after require to complete - /// - [DataField("delay", required: true)] - public float Delay; - - /// - /// Whether the progress bar for this DoAfter should be hidden from other players. - /// - [DataField("hidden")] - public bool Hidden; - - [DataField("event", required: true)] - public EntityTargetActionEvent Event = default!; - - [DataField("attemptEventFrequency")] - public AttemptFrequency AttemptFrequency; - - /// - /// Should the DoAfter event broadcast? If this is false, then should be a valid entity. - /// - [DataField("broadcast")] - public bool Broadcast; - - // Break the chains - /// - /// Whether or not this do after requires the user to have hands. - /// - [DataField("needHand")] - public bool NeedHand; - - /// - /// Whether we need to keep our active hand as is (i.e. can't change hand or change item). This also covers - /// requiring the hand to be free (if applicable). This does nothing if is false. - /// - [DataField("breakOnHandChange")] - public bool BreakOnHandChange = true; - - /// - /// If do_after stops when the user moves - /// - [DataField("breakOnMove")] - public bool BreakOnMove; - - /// - /// If this is true then any movement, even when weightless, will break the doafter. - /// When there is no gravity, BreakOnUserMove is ignored. If it is false to begin with nothing will change. - /// - [DataField("breakOnWeightlessMove")] - public bool BreakOnWeightlessMove; - - /// - /// Threshold for user and target movement - /// - [DataField("movementThreshold")] - public float MovementThreshold = 0.1f; - - /// - /// Threshold for distance user from the used OR target entities. - /// - [DataField("distanceThreshold")] - public float? DistanceThreshold; - - /// - /// Whether damage will cancel the DoAfter. See also . - /// - [DataField("breakOnDamage")] - public bool BreakOnDamage; - - /// - /// Threshold for user damage. This damage has to be dealt in a single event, not over time. - /// - [DataField("damageThreshold")] - public FixedPoint2 DamageThreshold = 1; - - /// - /// If true, this DoAfter will be canceled if the user can no longer interact with the target. - /// - [DataField("requireCanInteract")] - public bool RequireCanInteract = true; - - [DataField("blockDuplicate")] - public bool BlockDuplicate = true; - - //TODO: User pref to not cancel on second use on specific doafters - /// - /// If true, this will cancel any duplicate DoAfters when attempting to add a new DoAfter. See also - /// . - /// - [DataField("cancelDuplicate")] - public bool CancelDuplicate = true; - - /// - /// These flags determine what DoAfter properties are used to determine whether one DoAfter is a duplicate of - /// another. - /// - /// - /// Note that both DoAfters may have their own conditions, and they will be considered duplicated if either set - /// of conditions is satisfied. - /// - [DataField("duplicateCondition")] - public DuplicateConditions DuplicateCondition = DuplicateConditions.All; -} - -public sealed partial class DoAfterUserEvent : InstantActionEvent -{ - /// - /// How long does the do_after require to complete - /// - [DataField("delay", required: true)] - public float Delay; - - /// - /// Whether the progress bar for this DoAfter should be hidden from other players. - /// - [DataField("hidden")] - public bool Hidden; - - [DataField("event", required: true)] - public InstantActionEvent Event = default!; - - [DataField("attemptEventFrequency")] - public AttemptFrequency AttemptFrequency; - - /// - /// Should the DoAfter event broadcast? If this is false, then should be a valid entity. - /// - [DataField("broadcast")] - public bool Broadcast; - - // Break the chains - /// - /// Whether or not this do after requires the user to have hands. - /// - [DataField("needHand")] - public bool NeedHand; - - /// - /// Whether we need to keep our active hand as is (i.e. can't change hand or change item). This also covers - /// requiring the hand to be free (if applicable). This does nothing if is false. - /// - [DataField("breakOnHandChange")] - public bool BreakOnHandChange = true; - - /// - /// If do_after stops when the user moves - /// - [DataField("breakOnMove")] - public bool BreakOnMove; - - /// - /// If this is true then any movement, even when weightless, will break the doafter. - /// When there is no gravity, BreakOnUserMove is ignored. If it is false to begin with nothing will change. - /// - [DataField("breakOnWeightlessMove")] - public bool BreakOnWeightlessMove; - - /// - /// Threshold for user and target movement - /// - [DataField("movementThreshold")] - public float MovementThreshold = 0.1f; - - /// - /// Threshold for distance user from the used OR target entities. - /// - [DataField("distanceThreshold")] - public float? DistanceThreshold; - - /// - /// Whether damage will cancel the DoAfter. See also . - /// - [DataField("breakOnDamage")] - public bool BreakOnDamage; - - /// - /// Threshold for user damage. This damage has to be dealt in a single event, not over time. - /// - [DataField("damageThreshold")] - public FixedPoint2 DamageThreshold = 1; - - /// - /// If true, this DoAfter will be canceled if the user can no longer interact with the target. - /// - [DataField("requireCanInteract")] - public bool RequireCanInteract = true; - - [DataField("blockDuplicate")] - public bool BlockDuplicate = true; - - //TODO: User pref to not cancel on second use on specific doafters - /// - /// If true, this will cancel any duplicate DoAfters when attempting to add a new DoAfter. See also - /// . - /// - [DataField("cancelDuplicate")] - public bool CancelDuplicate = true; - - /// - /// These flags determine what DoAfter properties are used to determine whether one DoAfter is a duplicate of - /// another. - /// - /// - /// Note that both DoAfters may have their own conditions, and they will be considered duplicated if either set - /// of conditions is satisfied. - /// - [DataField("duplicateCondition")] - public DuplicateConditions DuplicateCondition = DuplicateConditions.All; -} diff --git a/Content.Shared/Stories/ForceUser/Systems/ForceUserSystem.Actions.Events.cs b/Content.Shared/Stories/ForceUser/Systems/ForceUserSystem.Actions.Events.cs index 48e2b84f03..56a127be67 100644 --- a/Content.Shared/Stories/ForceUser/Systems/ForceUserSystem.Actions.Events.cs +++ b/Content.Shared/Stories/ForceUser/Systems/ForceUserSystem.Actions.Events.cs @@ -11,71 +11,7 @@ namespace Content.Shared.Stories.ForceUser.Actions.Events; -// "Простые" события, которые не нуждаются в Content.Shared.Stories.ForceUser.Components -// ? Возможно стоит их убрать отсюда? -#region Simple -public sealed partial class LightningStrikeEvent : EntityTargetActionEvent { } -public sealed partial class EmpActionEvent : InstantActionEvent -{ - [DataField("range"), ViewVariables(VVAccess.ReadWrite)] - public float Range = 1.0f; - - /// - /// How much energy will be consumed per battery in range - /// - [DataField("energyConsumption"), ViewVariables(VVAccess.ReadWrite)] - public float EnergyConsumption; - - /// - /// How long it disables targets in seconds - /// - [DataField("disableDuration"), ViewVariables(VVAccess.ReadWrite)] - public float DisableDuration = 60f; -} -public sealed partial class FreedomActionEvent : InstantActionEvent { } -public sealed partial class RejuvenateActionEvent : InstantActionEvent { } -public sealed partial class FlashAreaEvent : InstantActionEvent -{ - [DataField("duration")] - [ViewVariables(VVAccess.ReadWrite)] - public int FlashDuration { get; set; } = 8000; - - [DataField("range")] - [ViewVariables(VVAccess.ReadWrite)] - public float Range { get; set; } = 7f; - - [DataField("slowTo")] - [ViewVariables(VVAccess.ReadWrite)] - public float SlowTo { get; set; } = 0.5f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("sound")] - public SoundSpecifier Sound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/flash.ogg"); -} -public sealed partial class RecliningPulseEvent : InstantActionEvent -{ - [DataField("stun")] - public float StunTime { get; set; } = 3f; - - [DataField("strength")] - public int Strength { get; set; } = 10; - - [DataField("range")] - public float Range { get; set; } = 10f; -} public sealed partial class HypnosisTargetActionEvent : EntityTargetActionEvent { } -public sealed partial class IgniteTargetActionEvent : EntityTargetActionEvent -{ - [ViewVariables(VVAccess.ReadWrite)] - [DataField("stackAmount")] - public float StackAmount = 1f; -} -public sealed partial class ForceDashActionEvent : WorldTargetActionEvent -{ - [DataField("strength")] - public float Strength = 10; -} -#endregion #region ForceProtectiveBubble public sealed partial class CreateProtectiveBubbleEvent : InstantActionEvent diff --git a/Content.Shared/Stories/InjectReagents/InjectReagentsSystem.cs b/Content.Shared/Stories/InjectReagents/InjectReagentsSystem.cs deleted file mode 100644 index 20832372b0..0000000000 --- a/Content.Shared/Stories/InjectReagents/InjectReagentsSystem.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.Actions; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Whitelist; -using Content.Shared.Chemistry.Components.SolutionManager; - -namespace Content.Shared.Stories.InjectReagents; -public sealed partial class InjectReagentsSystem : EntitySystem -{ - [Dependency] private readonly SharedSolutionContainerSystem _solutions = default!; - [Dependency] private readonly EntityLookupSystem _entityLookup = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; - public override void Initialize() - { - SubscribeLocalEvent(OnInjectReagentsEvent); - SubscribeLocalEvent(OnIjectReagentsToTargetEvent); - SubscribeLocalEvent(OnInjectReagentsInRangeEvent); - } - private void OnInjectReagentsEvent(InjectReagentsEvent args) - { - if (args.Handled || !_solutions.TryGetSolution(args.Performer, args.SolutionTarget, out var solution)) return; - _solutions.TryAddSolution(solution.Value, args.Solution); - args.Handled = true; - } - private void OnIjectReagentsToTargetEvent(InjectReagentsToTargetEvent args) - { - if (args.Handled || !_solutions.TryGetSolution(args.Target, args.SolutionTarget, out var solution)) return; - _solutions.TryAddSolution(solution.Value, args.Solution); - args.Handled = true; - } - private void OnInjectReagentsInRangeEvent(InjectReagentsInRangeEvent args) - { - if (args.Handled) - return; - - var entitis = _entityLookup.GetEntitiesInRange(Transform(args.Performer).Coordinates, args.Range); - foreach (var (entity, component) in entitis) - { - - if (entity == args.Performer && !args.InjectToPerformer) - continue; - - if (_whitelist.IsWhitelistFail(args.Whitelist, entity)) - continue; - - if (_whitelist.IsBlacklistPass(args.Blacklist, entity)) - continue; - - if (!_solutions.TryGetSolution((entity, component), args.SolutionTarget, out var solution)) - continue; - - _solutions.TryAddSolution(solution.Value, args.Solution); - } - - args.Handled = true; - } -} -public sealed partial class InjectReagentsEvent : InstantActionEvent -{ - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solution")] - public Solution Solution { get; set; } = new(); - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solutionTarget")] - public string SolutionTarget { get; set; } = "chemicals"; -} -public sealed partial class InjectReagentsToTargetEvent : EntityTargetActionEvent -{ - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solution")] - public Solution Solution { get; set; } = new(); - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solutionTarget")] - public string SolutionTarget { get; set; } = "chemicals"; -} -public sealed partial class InjectReagentsInRangeEvent : InstantActionEvent -{ - [DataField] - public bool InjectToPerformer { get; set; } = false; - - [DataField] - public float Range { get; set; } = 7.5f; - - [DataField] - public EntityWhitelist? Whitelist { get; set; } - - [DataField] - public EntityWhitelist? Blacklist { get; set; } - - [DataField] - public Solution Solution { get; set; } = new(); - - [DataField] - public string SolutionTarget { get; set; } = "chemicals"; -} diff --git a/Resources/Prototypes/Stories/Actions/force/darkside.yml b/Resources/Prototypes/Stories/Actions/force/darkside.yml index c99f1844dd..ccfd9ea345 100644 --- a/Resources/Prototypes/Stories/Actions/force/darkside.yml +++ b/Resources/Prototypes/Stories/Actions/force/darkside.yml @@ -48,12 +48,13 @@ useDelay: 5 itemIconStyle: BigAction icon: { sprite: Stories/Actions/force.rsi, state: sith } - event: !type:DoAfterUserEvent - hidden: false - needHand: false - delay: 3 - breakOnDamage: true - breakOnMove: true + event: !type:StartDoAfterEvent + doAfterArgs: + hidden: false + needHand: false + delay: 3 + breakOnDamage: true + breakOnMove: true event: !type:InstantForceUserActionEvent volume: 100 event: !type:SithPolymorphEvent @@ -97,10 +98,10 @@ icon: { sprite: Stories/Actions/force.rsi, state: pulse } event: !type:InstantForceUserActionEvent volume: 100 - event: !type:RecliningPulseEvent - strength: 15 - stun: 3 + event: !type:ApplyInRangeEvent range: 5 + event: !type:PushTargetEvent + strength: 15 - type: entity id: ActionStrangle @@ -144,7 +145,7 @@ state: lightning event: !type:EntityTargetForceUserActionEvent volume: 50 - event: !type:LightningStrikeEvent + event: !type:ShootLightningsTargetEvent - type: entity id: ActionIgniteTarget @@ -186,12 +187,13 @@ icon: sprite: Stories/Actions/force.rsi state: hypnosis - event: !type:DoAfterTargetEvent - hidden: true - needHand: true - delay: 10 - breakOnDamage: true - breakOnMove: true + event: !type:StartDoAfterWithTargetEvent + doAfterArgs: + hidden: true + needHand: true + delay: 10 + breakOnDamage: true + breakOnMove: true event: !type:EntityTargetForceUserActionEvent volume: 100 event: !type:HypnosisTargetActionEvent diff --git a/Resources/Prototypes/Stories/Actions/force/general.yml b/Resources/Prototypes/Stories/Actions/force/general.yml index a405e1cfa5..7bc455f3d0 100644 --- a/Resources/Prototypes/Stories/Actions/force/general.yml +++ b/Resources/Prototypes/Stories/Actions/force/general.yml @@ -109,7 +109,7 @@ checkCanInteract: false event: !type:InstantForceUserActionEvent volume: 100 - event: !type:InjectReagentsEvent + event: !type:InjectSolutionEvent solution: maxVol: 15 reagents: @@ -170,7 +170,7 @@ path: /Audio/Magic/blink.ogg event: !type:WorldTargetForceUserActionEvent volume: 50 - event: !type:ForceDashActionEvent + event: !type:ThrownDashActionEvent strength: 20 - type: entity diff --git a/Resources/Prototypes/Stories/Actions/force/lightside.yml b/Resources/Prototypes/Stories/Actions/force/lightside.yml index 7f826de7a8..7a2284f992 100644 --- a/Resources/Prototypes/Stories/Actions/force/lightside.yml +++ b/Resources/Prototypes/Stories/Actions/force/lightside.yml @@ -49,7 +49,7 @@ checkCanInteract: false event: !type:InstantForceUserActionEvent volume: 100 - event: !type:InjectReagentsEvent + event: !type:InjectSolutionEvent solution: maxVol: 150 reagents: @@ -86,14 +86,15 @@ icon: sprite: Stories/Actions/force.rsi state: healingtouch - event: !type:DoAfterTargetEvent - needHand: true - delay: 2 - breakOnDamage: true - breakOnMove: true + event: !type:StartDoAfterWithTargetEvent + doAfterArgs: + needHand: true + delay: 2 + breakOnDamage: true + breakOnMove: true event: !type:EntityTargetForceUserActionEvent volume: 25 - event: !type:InjectReagentsToTargetEvent + event: !type:InjectSolutionToTargetEvent solution: maxVol: 7.5 reagents: @@ -105,17 +106,29 @@ name: Силовой толчок description: Создайте сильный толчок, который может сбить человека с ног. Полностью ослабевает, пролетев 4 тайла. components: - - type: WorldTargetAction - useDelay: 10 + - type: EntityTargetAction itemIconStyle: BigAction checkCanAccess: false - range: 60 - sound: !type:SoundPathSpecifier - path: /Audio/Magic/forcewall.ogg + canTargetSelf: false + useDelay: 10 + range: 3 + whitelist: + components: + - Body icon: sprite: Stories/Actions/force.rsi state: push - event: !type:WorldTargetForceUserActionEvent - volume: 50 - event: !type:ProjectileSpellEvent - prototype: JediPushBall + event: !type:StartDoAfterWithTargetEvent + doAfterArgs: + needHand: true + delay: 5 + distanceThreshold: 3 + breakOnDamage: false + breakOnMove: false + breakOnHandChange: false + event: !type:EntityTargetForceUserActionEvent + volume: 50 + event: !type:PushTargetEvent + strength: 10 + sound: !type:SoundPathSpecifier + path: /Audio/Magic/forcewall.ogg diff --git a/Resources/Prototypes/Stories/Actions/shadowling.yml b/Resources/Prototypes/Stories/Actions/shadowling.yml index 3e5933ca33..650b41724a 100644 --- a/Resources/Prototypes/Stories/Actions/shadowling.yml +++ b/Resources/Prototypes/Stories/Actions/shadowling.yml @@ -20,12 +20,13 @@ icon: { sprite: Stories/Actions/shadowling.rsi, state: enthrall } itemIconStyle: NoItem priority: -11 - event: !type:DoAfterTargetEvent - hidden: false - needHand: true - delay: 5 - breakOnDamage: true - breakOnMove: true + event: !type:StartDoAfterWithTargetEvent + doAfterArgs: + hidden: false + needHand: true + delay: 5 + breakOnDamage: true + breakOnMove: true event: !type:ShadowlingEnthrallEvent {} - type: entity @@ -39,8 +40,12 @@ icon: { sprite: Stories/Actions/shadowling.rsi, state: glare } itemIconStyle: NoItem range: 10 + whitelist: + components: + - Body priority: -11 - event: !type:ShadowlingGlareEvent {} + canTargetSelf: false + event: !type:RangedGlareEvent {} - type: entity id: ActionShadowlingVeil @@ -81,14 +86,9 @@ icon: { sprite: Stories/Actions/shadowling.rsi, state: icy_veins } itemIconStyle: NoItem priority: -11 - event: !type:InjectReagentsInRangeEvent - injectToPerformer: false + event: !type:ApplyInRangeEvent + includePerformer: false range: 7.5 - solutionTarget: chemicals - solution: - reagents: - - ReagentId: IceOil - Quantity: 4 whitelist: components: - Body @@ -96,6 +96,11 @@ components: - Shadowling - ShadowlingThrall + event: !type:InjectSolutionToTargetEvent + solution: + reagents: + - ReagentId: IceOil + Quantity: 4 - type: entity id: ActionShadowlingCollectiveMind