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()
|
||||
{
|
||||
SubscribeNetworkEvent<PlayMeleeWeaponAnimationMessage>(PlayWeaponArc);
|
||||
SubscribeNetworkEvent<PlayLungeAnimationMessage>(PlayLunge);
|
||||
}
|
||||
|
||||
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.Linq;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Pulling;
|
||||
using Content.Server.GameObjects.EntitySystems.Click;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
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.Items;
|
||||
using Content.Shared.GameObjects.Components.Pulling;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
@@ -32,7 +41,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
[ComponentReference(typeof(IHandsComponent))]
|
||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||
[ComponentReference(typeof(SharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved, IDisarmedAct
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
|
||||
@@ -718,6 +727,43 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
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
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using NFluidsynth;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Timers;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timers;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
@@ -19,7 +25,7 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedStunnableComponent))]
|
||||
public class StunnableComponent : SharedStunnableComponent
|
||||
public class StunnableComponent : SharedStunnableComponent, IDisarmedAct
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
@@ -121,5 +127,23 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
return new StunnableComponentState(StunnedTimer, KnockdownTimer, SlowdownTimer, WalkModifierOverride,
|
||||
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,
|
||||
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,
|
||||
HumanScream,
|
||||
Disarm,
|
||||
DebugInstant,
|
||||
DebugToggle,
|
||||
DebugTargetPoint,
|
||||
|
||||
@@ -30,5 +30,18 @@ namespace Content.Shared.GameObjects.EntitySystemMessages
|
||||
public bool TextureEffect { 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
|
||||
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
|
||||
actionType: DebugInstant
|
||||
icon: Interface/Alerts/Human/human1.png
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
- type: Actions
|
||||
innateActions:
|
||||
- HumanScream
|
||||
- Disarm
|
||||
- type: OverlayEffectsUI
|
||||
- type: Eye
|
||||
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",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "disarm",
|
||||
"directions": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user