Adds disarm action (#2950)
* Adds disarming * Disarm acts * yaml * much better icon for disarm * Apply Remie's suggestions, improve code!
This commit is contained in:
committed by
GitHub
parent
1fe25049a3
commit
d81a5faac4
@@ -28,6 +28,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeNetworkEvent<PlayMeleeWeaponAnimationMessage>(PlayWeaponArc);
|
SubscribeNetworkEvent<PlayMeleeWeaponAnimationMessage>(PlayWeaponArc);
|
||||||
|
SubscribeNetworkEvent<PlayLungeAnimationMessage>(PlayLunge);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FrameUpdate(float frameTime)
|
public override void FrameUpdate(float frameTime)
|
||||||
@@ -106,5 +107,13 @@ namespace Content.Client.GameObjects.EntitySystems
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PlayLunge(PlayLungeAnimationMessage msg)
|
||||||
|
{
|
||||||
|
EntityManager
|
||||||
|
.GetEntity(msg.Source)
|
||||||
|
.EnsureComponent<MeleeLungeComponent>()
|
||||||
|
.SetData(msg.Angle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
89
Content.Server/Actions/DisarmAction.cs
Normal file
89
Content.Server/Actions/DisarmAction.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.Components.GUI;
|
||||||
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
|
using Content.Server.GameObjects.Components.Pulling;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Interfaces.GameObjects;
|
||||||
|
using Content.Server.Utility;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Audio;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
|
using Content.Shared.GameObjects.Components.Pulling;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Content.Shared.Utility;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Random;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Server.Actions
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class DisarmAction : ITargetEntityAction
|
||||||
|
{
|
||||||
|
private float _failProb;
|
||||||
|
private float _pushProb;
|
||||||
|
private float _cooldown;
|
||||||
|
|
||||||
|
public void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
serializer.DataField(ref _failProb, "failProb", 0.4f);
|
||||||
|
serializer.DataField(ref _pushProb, "pushProb", 0.4f);
|
||||||
|
serializer.DataField(ref _cooldown, "cooldown", 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoTargetEntityAction(TargetEntityActionEventArgs args)
|
||||||
|
{
|
||||||
|
var disarmedActs = args.Target.GetAllComponents<IDisarmedAct>().ToArray();
|
||||||
|
|
||||||
|
if (disarmedActs.Length == 0 || !args.Performer.InRangeUnobstructed(args.Target)) return;
|
||||||
|
if (!args.Performer.TryGetComponent<SharedActionsComponent>(out var actions)) return;
|
||||||
|
if (args.Target == args.Performer || !args.Performer.CanAttack()) return;
|
||||||
|
|
||||||
|
var random = IoCManager.Resolve<IRobustRandom>();
|
||||||
|
var audio = EntitySystem.Get<AudioSystem>();
|
||||||
|
var system = EntitySystem.Get<MeleeWeaponSystem>();
|
||||||
|
|
||||||
|
var angle = new Angle(args.Target.Transform.MapPosition.Position - args.Performer.Transform.MapPosition.Position);
|
||||||
|
|
||||||
|
actions.Cooldown(ActionType.Disarm, Cooldowns.SecondsFromNow(_cooldown));
|
||||||
|
|
||||||
|
if (random.Prob(_failProb))
|
||||||
|
{
|
||||||
|
audio.PlayFromEntity("/Audio/Weapons/punchmiss.ogg", args.Performer,
|
||||||
|
AudioHelpers.WithVariation(0.025f));
|
||||||
|
args.Performer.PopupMessageOtherClients(Loc.GetString("{0} fails to disarm {1}!", args.Performer.Name, args.Target.Name));
|
||||||
|
args.Performer.PopupMessageCursor(Loc.GetString("You fail to disarm {0}!", args.Target.Name));
|
||||||
|
system.SendLunge(angle, args.Performer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
system.SendAnimation("disarm", angle, args.Performer, args.Performer, new []{ args.Target });
|
||||||
|
|
||||||
|
var eventArgs = new DisarmedActEventArgs() {Target = args.Target, Source = args.Performer, PushProbability = _pushProb};
|
||||||
|
|
||||||
|
// Sort by priority.
|
||||||
|
Array.Sort(disarmedActs, (a, b) => a.Priority.CompareTo(b.Priority));
|
||||||
|
|
||||||
|
foreach (var disarmedAct in disarmedActs)
|
||||||
|
{
|
||||||
|
if (disarmedAct.Disarmed(eventArgs))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.PlayFromEntity("/Audio/Effects/thudswoosh.ogg", args.Performer,
|
||||||
|
AudioHelpers.WithVariation(0.025f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,22 +4,31 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.GameObjects.Components.Items.Storage;
|
using Content.Server.GameObjects.Components.Items.Storage;
|
||||||
|
using Content.Server.GameObjects.Components.Pulling;
|
||||||
using Content.Server.GameObjects.EntitySystems.Click;
|
using Content.Server.GameObjects.EntitySystems.Click;
|
||||||
|
using Content.Server.Interfaces.GameObjects;
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||||
|
using Content.Server.Utility;
|
||||||
|
using Content.Shared.Audio;
|
||||||
using Content.Shared.GameObjects.Components.Body.Part;
|
using Content.Shared.GameObjects.Components.Body.Part;
|
||||||
using Content.Shared.GameObjects.Components.Items;
|
using Content.Shared.GameObjects.Components.Items;
|
||||||
|
using Content.Shared.GameObjects.Components.Pulling;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
using Content.Shared.Physics.Pull;
|
using Content.Shared.Physics.Pull;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.GameObjects.Components.Container;
|
using Robust.Server.GameObjects.Components.Container;
|
||||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||||
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameObjects.Components;
|
using Robust.Shared.GameObjects.Components;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Interfaces.Network;
|
using Robust.Shared.Interfaces.Network;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Players;
|
||||||
@@ -32,7 +41,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
|||||||
[ComponentReference(typeof(IHandsComponent))]
|
[ComponentReference(typeof(IHandsComponent))]
|
||||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||||
[ComponentReference(typeof(SharedHandsComponent))]
|
[ComponentReference(typeof(SharedHandsComponent))]
|
||||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved, IDisarmedAct
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||||
|
|
||||||
@@ -718,6 +727,43 @@ namespace Content.Server.GameObjects.Components.GUI
|
|||||||
|
|
||||||
RemoveHand(args.Slot);
|
RemoveHand(args.Slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IDisarmedAct.Disarmed(DisarmedActEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (BreakPulls())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var source = eventArgs.Source;
|
||||||
|
|
||||||
|
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Effects/thudswoosh.ogg", source,
|
||||||
|
AudioHelpers.WithVariation(0.025f));
|
||||||
|
|
||||||
|
if (ActiveHand != null && Drop(ActiveHand, false))
|
||||||
|
{
|
||||||
|
source.PopupMessageOtherClients(Loc.GetString("{0} disarms {1}!", source.Name, eventArgs.Target.Name));
|
||||||
|
source.PopupMessageCursor(Loc.GetString("You disarm {0}!", eventArgs.Target.Name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source.PopupMessageOtherClients(Loc.GetString("{0} shoves {1}!", source.Name, eventArgs.Target.Name));
|
||||||
|
source.PopupMessageCursor(Loc.GetString("You shove {0}!", eventArgs.Target.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want this to be the last disarm act to run.
|
||||||
|
int IDisarmedAct.Priority => int.MaxValue;
|
||||||
|
|
||||||
|
private bool BreakPulls()
|
||||||
|
{
|
||||||
|
// What is this API??
|
||||||
|
if (!Owner.TryGetComponent(out SharedPullerComponent? puller)
|
||||||
|
|| puller.Pulling == null || !puller.Pulling.TryGetComponent(out PullableComponent? pullable))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return pullable.TryStopPull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Hand : IDisposable
|
public class Hand : IDisposable
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
using Content.Server.GameObjects.EntitySystems;
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Interfaces.GameObjects;
|
||||||
|
using Content.Server.Utility;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Audio;
|
||||||
using Content.Shared.Chemistry;
|
using Content.Shared.Chemistry;
|
||||||
using Content.Shared.GameObjects.Components.Mobs;
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using Content.Shared.GameObjects.Components.Movement;
|
using Content.Shared.GameObjects.Components.Movement;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
using NFluidsynth;
|
using NFluidsynth;
|
||||||
using Robust.Server.GameObjects.EntitySystems;
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameObjects.Components.Timers;
|
using Robust.Shared.GameObjects.Components.Timers;
|
||||||
using Robust.Shared.GameObjects.Systems;
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.Random;
|
||||||
using Robust.Shared.Interfaces.Timing;
|
using Robust.Shared.Interfaces.Timing;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timers;
|
using Robust.Shared.Timers;
|
||||||
using Logger = Robust.Shared.Log.Logger;
|
using Logger = Robust.Shared.Log.Logger;
|
||||||
|
|
||||||
@@ -19,7 +25,7 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedStunnableComponent))]
|
[ComponentReference(typeof(SharedStunnableComponent))]
|
||||||
public class StunnableComponent : SharedStunnableComponent
|
public class StunnableComponent : SharedStunnableComponent, IDisarmedAct
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
@@ -121,5 +127,23 @@ namespace Content.Server.GameObjects.Components.Mobs
|
|||||||
return new StunnableComponentState(StunnedTimer, KnockdownTimer, SlowdownTimer, WalkModifierOverride,
|
return new StunnableComponentState(StunnedTimer, KnockdownTimer, SlowdownTimer, WalkModifierOverride,
|
||||||
RunModifierOverride);
|
RunModifierOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IDisarmedAct.Disarmed(DisarmedActEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (!IoCManager.Resolve<IRobustRandom>().Prob(eventArgs.PushProbability))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Paralyze(4f);
|
||||||
|
|
||||||
|
var source = eventArgs.Source;
|
||||||
|
|
||||||
|
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Effects/thudswoosh.ogg", source,
|
||||||
|
AudioHelpers.WithVariation(0.025f));
|
||||||
|
|
||||||
|
source.PopupMessageOtherClients(Loc.GetString("{0} pushes {1}!", source, eventArgs.Target.Name));
|
||||||
|
source.PopupMessageCursor(Loc.GetString("You push {0}!", eventArgs.Target.Name));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,10 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
RaiseNetworkEvent(new MeleeWeaponSystemMessages.PlayMeleeWeaponAnimationMessage(arc, angle, attacker.Uid, source.Uid,
|
RaiseNetworkEvent(new MeleeWeaponSystemMessages.PlayMeleeWeaponAnimationMessage(arc, angle, attacker.Uid, source.Uid,
|
||||||
hits.Select(e => e.Uid).ToList(), textureEffect, arcFollowAttacker));
|
hits.Select(e => e.Uid).ToList(), textureEffect, arcFollowAttacker));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SendLunge(Angle angle, IEntity source)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new MeleeWeaponSystemMessages.PlayLungeAnimationMessage(angle, source.Uid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
Content.Server/Interfaces/GameObjects/IDisarmedAct.cs
Normal file
42
Content.Server/Interfaces/GameObjects/IDisarmedAct.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.Interfaces.GameObjects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements behavior when an entity is disarmed.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDisarmedAct
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior when the entity is disarmed.
|
||||||
|
/// Return true to prevent the default disarm behavior,
|
||||||
|
/// or rest of IDisarmAct behaviors that come after this one from happening.
|
||||||
|
/// </summary>
|
||||||
|
bool Disarmed(DisarmedActEventArgs eventArgs);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Priority for this disarm act.
|
||||||
|
/// Used to determine act execution order.
|
||||||
|
/// </summary>
|
||||||
|
int Priority => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DisarmedActEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The entity being disarmed.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Target { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The entity performing the disarm.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Source { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Probability for push/knockdown.
|
||||||
|
/// </summary>
|
||||||
|
public float PushProbability { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
{
|
{
|
||||||
Error,
|
Error,
|
||||||
HumanScream,
|
HumanScream,
|
||||||
|
Disarm,
|
||||||
DebugInstant,
|
DebugInstant,
|
||||||
DebugToggle,
|
DebugToggle,
|
||||||
DebugTargetPoint,
|
DebugTargetPoint,
|
||||||
|
|||||||
@@ -30,5 +30,18 @@ namespace Content.Shared.GameObjects.EntitySystemMessages
|
|||||||
public bool TextureEffect { get; }
|
public bool TextureEffect { get; }
|
||||||
public bool ArcFollowAttacker { get; }
|
public bool ArcFollowAttacker { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class PlayLungeAnimationMessage : EntitySystemMessage
|
||||||
|
{
|
||||||
|
public Angle Angle { get; }
|
||||||
|
public EntityUid Source { get; }
|
||||||
|
|
||||||
|
public PlayLungeAnimationMessage(Angle angle, EntityUid source)
|
||||||
|
{
|
||||||
|
Angle = angle;
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,17 @@
|
|||||||
- /Audio/Voice/Human/femalescream_5.ogg
|
- /Audio/Voice/Human/femalescream_5.ogg
|
||||||
wilhelm: /Audio/Voice/Human/wilhelm_scream.ogg
|
wilhelm: /Audio/Voice/Human/wilhelm_scream.ogg
|
||||||
|
|
||||||
|
- type: action
|
||||||
|
actionType: Disarm
|
||||||
|
icon: Interface/Actions/disarm.png
|
||||||
|
name: "[color=red]Disarm[/color]"
|
||||||
|
description: "Attempt to [color=red]disarm[/color] someone."
|
||||||
|
filters:
|
||||||
|
- human
|
||||||
|
behaviorType: TargetEntity
|
||||||
|
repeat: true
|
||||||
|
behavior: !type:DisarmAction { }
|
||||||
|
|
||||||
- type: action
|
- type: action
|
||||||
actionType: DebugInstant
|
actionType: DebugInstant
|
||||||
icon: Interface/Alerts/Human/human1.png
|
icon: Interface/Alerts/Human/human1.png
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
- type: Actions
|
- type: Actions
|
||||||
innateActions:
|
innateActions:
|
||||||
- HumanScream
|
- HumanScream
|
||||||
|
- Disarm
|
||||||
- type: OverlayEffectsUI
|
- type: OverlayEffectsUI
|
||||||
- type: Eye
|
- type: Eye
|
||||||
zoom: 0.5, 0.5
|
zoom: 0.5, 0.5
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/Actions/disarm.png
Normal file
BIN
Resources/Textures/Interface/Actions/disarm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -18,6 +18,10 @@
|
|||||||
{
|
{
|
||||||
"name": "scream",
|
"name": "scream",
|
||||||
"directions": 1
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disarm",
|
||||||
|
"directions": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user