Port boxer from Nyano (#9080)
This commit is contained in:
23
Content.Server/Abilities/Boxer/Boxer/BoxerComponent.cs
Normal file
23
Content.Server/Abilities/Boxer/Boxer/BoxerComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Abilities.Boxer
|
||||
{
|
||||
/// <summary>
|
||||
/// Added to the boxer on spawn.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class BoxerComponent : Component
|
||||
{
|
||||
[DataField("modifiers", required: true)]
|
||||
public DamageModifierSet UnarmedModifiers = default!;
|
||||
|
||||
[DataField("rangeBonus")]
|
||||
public float RangeBonus = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Damage modifier with boxing glove stam damage.
|
||||
/// </summary>
|
||||
[DataField("boxingGlovesModifier")]
|
||||
public float BoxingGlovesModifier = 1.75f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Abilities.Boxer
|
||||
{
|
||||
/// <summary>
|
||||
/// Boxer gets a bonus for these, and their fists, but not other unarmed weapons.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class BoxingGlovesComponent : Component
|
||||
{}
|
||||
}
|
||||
51
Content.Server/Abilities/Boxer/BoxingSystem.cs
Normal file
51
Content.Server/Abilities/Boxer/BoxingSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Server.Weapon.Melee.Components;
|
||||
using Content.Server.Clothing.Components;
|
||||
using Content.Server.Damage.Components;
|
||||
using Content.Server.Damage.Events;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Abilities.Boxer
|
||||
{
|
||||
public sealed class BoxingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<BoxerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<BoxerComponent, MeleeHitEvent>(ApplyBoxerModifiers);
|
||||
SubscribeLocalEvent<BoxingGlovesComponent, StaminaMeleeHitEvent>(OnStamHit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, BoxerComponent boxer, ComponentInit args)
|
||||
{
|
||||
if (TryComp<MeleeWeaponComponent>(uid, out var meleeComp))
|
||||
meleeComp.Range *= boxer.RangeBonus;
|
||||
}
|
||||
private void ApplyBoxerModifiers(EntityUid uid, BoxerComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (component.UnarmedModifiers == default!)
|
||||
{
|
||||
Logger.Warning("BoxerComponent on " + uid + " couldn't get damage modifiers. Know that adding components with damage modifiers through VV or similar is unsupported.");
|
||||
return;
|
||||
}
|
||||
|
||||
args.ModifiersList.Add(component.UnarmedModifiers);
|
||||
}
|
||||
private void OnStamHit(EntityUid uid, BoxingGlovesComponent component, StaminaMeleeHitEvent args)
|
||||
{
|
||||
_containerSystem.TryGetContainingContainer(uid, out var equipee);
|
||||
if (TryComp<BoxerComponent>(equipee?.Owner, out var boxer))
|
||||
args.Multiplier *= boxer.BoxingGlovesModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Sound;
|
||||
|
||||
namespace Content.Server.Damage.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
@@ -5,4 +7,10 @@ public sealed class StaminaDamageOnHitComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
||||
public float Damage = 30f;
|
||||
|
||||
/// <summary>
|
||||
/// Play a sound when this knocks down an entity.
|
||||
/// </summary>
|
||||
[DataField("knockdownSound")]
|
||||
public SoundSpecifier? KnockdownSound;
|
||||
}
|
||||
|
||||
30
Content.Server/Damage/Events/StaminaMeleeHitEvent.cs
Normal file
30
Content.Server/Damage/Events/StaminaMeleeHitEvent.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Robust.Shared.Collections;
|
||||
using Content.Server.Damage.Components;
|
||||
|
||||
namespace Content.Server.Damage.Events;
|
||||
|
||||
/// <summary>
|
||||
/// The components in the list are going to be hit,
|
||||
/// give opportunities to change the damage or other stuff.
|
||||
/// </summary>
|
||||
public sealed class StaminaMeleeHitEvent : HandledEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// List of hit stamina components.
|
||||
public ValueList<StaminaComponent> HitList;
|
||||
|
||||
/// <summmary>
|
||||
/// The multiplier. Generally, try to use *= or /= instead of overwriting.
|
||||
/// </summary>
|
||||
public float Multiplier = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The flat modifier. Generally, try to use += or -= instead of overwriting.
|
||||
/// </summary>
|
||||
public float FlatModifier = 0;
|
||||
|
||||
public StaminaMeleeHitEvent(ValueList<StaminaComponent> hitList)
|
||||
{
|
||||
HitList = hitList;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,13 @@ using Content.Server.Weapon.Melee;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Rounding;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
|
||||
namespace Content.Server.Damage.Systems;
|
||||
|
||||
@@ -70,10 +73,22 @@ public sealed class StaminaSystem : EntitySystem
|
||||
toHit.Add(stam);
|
||||
}
|
||||
|
||||
var hitEvent = new StaminaMeleeHitEvent(toHit);
|
||||
RaiseLocalEvent(uid, hitEvent, false);
|
||||
|
||||
if (hitEvent.Handled)
|
||||
return;
|
||||
|
||||
var damage = component.Damage;
|
||||
|
||||
damage *= hitEvent.Multiplier;
|
||||
|
||||
damage += hitEvent.FlatModifier;
|
||||
|
||||
foreach (var comp in toHit)
|
||||
{
|
||||
var oldDamage = comp.StaminaDamage;
|
||||
TakeStaminaDamage(comp.Owner, component.Damage / toHit.Count, comp);
|
||||
TakeStaminaDamage(comp.Owner, damage / toHit.Count, comp, component.KnockdownSound);
|
||||
if (comp.StaminaDamage.Equals(oldDamage))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("stamina-resist"), comp.Owner, Filter.Entities(args.User));
|
||||
@@ -100,7 +115,7 @@ public sealed class StaminaSystem : EntitySystem
|
||||
_alerts.ShowAlert(uid, AlertType.Stamina, (short) severity);
|
||||
}
|
||||
|
||||
public void TakeStaminaDamage(EntityUid uid, float value, StaminaComponent? component = null)
|
||||
public void TakeStaminaDamage(EntityUid uid, float value, StaminaComponent? component = null, SoundSpecifier? knockdownSound = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false) || component.Critical) return;
|
||||
|
||||
@@ -131,6 +146,8 @@ public sealed class StaminaSystem : EntitySystem
|
||||
{
|
||||
if (component.StaminaDamage >= component.CritThreshold)
|
||||
{
|
||||
if (knockdownSound != null)
|
||||
SoundSystem.Play(knockdownSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, knockdownSound.Params);
|
||||
EnterStamCrit(uid, component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.CombatMode;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Pulling;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Server.Weapon.Melee.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
@@ -32,6 +33,8 @@ namespace Content.Server.Interaction
|
||||
[Dependency] private readonly PullingSystem _pullSystem = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -259,17 +262,23 @@ namespace Content.Server.Interaction
|
||||
|
||||
// TODO: Make this saner?
|
||||
// Attempt to do unarmed combat. We don't check for handled just because at this point it doesn't matter.
|
||||
|
||||
var used = user;
|
||||
|
||||
if (_inventory.TryGetSlotEntity(user, "gloves", out var gloves) && HasComp<MeleeWeaponComponent>(gloves))
|
||||
used = (EntityUid) gloves;
|
||||
|
||||
if (wideAttack)
|
||||
{
|
||||
var ev = new WideAttackEvent(user, user, coordinates);
|
||||
RaiseLocalEvent(user, ev, false);
|
||||
var ev = new WideAttackEvent(used, user, coordinates);
|
||||
RaiseLocalEvent(used, ev, false);
|
||||
if (ev.Handled)
|
||||
_adminLogger.Add(LogType.AttackUnarmedWide, LogImpact.Low, $"{ToPrettyString(user):user} wide attacked at {coordinates}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var ev = new ClickAttackEvent(user, user, coordinates, target);
|
||||
RaiseLocalEvent(user, ev, false);
|
||||
var ev = new ClickAttackEvent(used, user, coordinates, target);
|
||||
RaiseLocalEvent(used, ev, false);
|
||||
if (ev.Handled)
|
||||
{
|
||||
if (target != null)
|
||||
|
||||
Reference in New Issue
Block a user