Skip to content

Commit

Permalink
Martial Artist Rework + Fix MeleeWeapon Wizmerge Bugs (#1560)
Browse files Browse the repository at this point in the history
# Description

Reworks the Martial Artist trait, making it a more impactful and
visually distinct trait.
- Left-clicks are now single-target power attacks, requiring less aim
than before.
- **50%** damage bonus reduced to **20%** damage bonus (same overall DPS
with next change).
- Gain **25%** attack rate bonus.
- The attack rate bonus helps make the trait feel and look distinct from
non-Martial Artist melee attacks.
- **50%** range bonus reduced to **10%** range bonus.
- The damage bonus is now also applied to Asphyxiation and Poison
damage, which Lamia unarmed attacks deal.
- Trait cost increased from **-3** points to **-5** points.
- Striking Calluses (which requires Martial Artist) cost reduced from
**-4** points to **-3** points to prevent the Martial Artist/Striking
Calluses combo from being too expensive. The combo used to cost **-7**
points, now it is **-8** points.

The reworked Martial Artist trait is also given to the Boxers, Martial
Artists and Gladiators.

Also reverted some wizmerge messery that messed up the melee attack rate
**again**, and messed up pistol whipping by making the cooldowns of
gunshots and melee attacks intertwined.

Also while we're at it, Natural Weapons Removal has been disabled for
all species whose damage is pure Blunt, including Diona, Dwarf, Arachne
and IPC. (IPC have 6 blunt so the trait would literally be a 0-point
negative trait for them)

## Technical Details

A new trait function has been added for Martial Artist:
`TraitModifyUnarmed`, which modifies the player entity's
`MeleeWeaponComponent`.

The Claws, Talons, Natural Weapon Removal, and Striking Calluses traits
have also been refactored under the hood to use `TraitModifyUnarmed`,
instead of replacing `MeleeWeaponComponent` which would wipe out all the
changes made by the Martial Artist trait.

## Media

### New Description

![martialartist](https://github.com/user-attachments/assets/de51530f-5f2a-41a8-b686-c12f746b5e4a)

### Martial Artist In Action

https://github.com/user-attachments/assets/7890aac2-2c74-4abd-be9f-b28c2922c865

### Striking Calluses New Description

![strikingcalluses](https://github.com/user-attachments/assets/bc15425e-3460-49f2-88f5-840c686e0053)

### Natural Weapons Removal New Description

![naturalweaponsremoval](https://github.com/user-attachments/assets/583adaa4-48c0-46e3-882b-1bb0f470018c)

# Changelog

:cl: Skubman
- add: Martial Artist Rework: Martial Artist now costs 5 points, but it
turns all unarmed melee attacks into single-target power attacks, with
20% bonus damage, 25% bonus attack rate and 10% bonus attack range.
- tweak: The reworked Martial Artist trait is now given for free to
Boxers, Martial Artists, and Gladiators.
- tweak: Martial Artists (the job) and Gladiators can now select the
Striking Calluses trait.
- tweak: The Martial Artist trait now applies bonus damage to the
Lamiae's unarmed Asphyxiation and Poison damage.
- tweak: The cost of Striking Calluses has been reduced from 4 points to
3 points.
- fix: Fixed a bug where slow weapons were fast and fast weapons were
slow.
- fix: You can pistol whip (right-click melee) immediately after firing
a gun again, and the cooldown on firing the gun after pistol whipping is
always 0.528 seconds again.
- fix: Prevented Dionas, Arachnae and IPCs, who all have pure Blunt
damage from selecting the redundant Natural Weapons Removal trait.
  • Loading branch information
angelofallars authored Jan 17, 2025
1 parent d185170 commit 6c43d00
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 102 deletions.
9 changes: 9 additions & 0 deletions Content.Client/Weapons/Melee/MeleeWeaponSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ public override void Update(float frameTime)
if (mousePos.MapId != attackerPos.MapId ||
(attackerPos.Position - mousePos.Position).Length() > weapon.Range)
{
if (weapon.HeavyOnLightMiss)
ClientHeavyAttack(entity, coordinates, weaponUid, weapon);

return;
}

Expand All @@ -165,6 +168,12 @@ public override void Update(float frameTime)
if (Interaction.CombatModeCanHandInteract(entity, target))
return;

if (weapon.HeavyOnLightMiss && !CanDoLightAttack(entity, target, weapon, out _))
{
ClientHeavyAttack(entity, coordinates, weaponUid, weapon);
return;
}

RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates)));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed partial class BoxerComponent : Component
public DamageModifierSet UnarmedModifiers = default!;

[DataField("rangeBonus")]
public float RangeBonus = 1.5f;
public float RangeBonus = 1.0f;

/// <summary>
/// Damage modifier with boxing glove stam damage.
Expand Down
90 changes: 90 additions & 0 deletions Content.Server/Traits/TraitSystem.Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
using Content.Shared.Mobs;
using Content.Shared.Damage.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Weapons.Melee;
using Robust.Shared.Audio;

namespace Content.Server.Traits;

Expand Down Expand Up @@ -585,3 +587,91 @@ public override void OnPlayerSpawn(EntityUid uid,
slowOnDamage.SpeedModifierThresholds = newSpeedModifierThresholds;
}
}

/// <summary>
/// Used for traits that modify unarmed damage on MeleeWeaponComponent.
/// </summary>
[UsedImplicitly]
public sealed partial class TraitModifyUnarmed : TraitFunction
{
// <summary>
// The sound played on hitting targets.
// </summary>
[DataField, AlwaysPushInheritance]
public SoundSpecifier? SoundHit;

// <summary>
// The animation to play on hit, for both light and power attacks.
// </summary>
[DataField, AlwaysPushInheritance]
public EntProtoId? Animation;

// <summary>
// Whether to set the power attack animation to be the same as the light attack.
// </summary>
[DataField, AlwaysPushInheritance]
public bool HeavyAnimationFromLight = true;

// <summary>
// The damage values of unarmed damage.
// </summary>
[DataField, AlwaysPushInheritance]
public DamageSpecifier? Damage;

// <summary>
// Additional damage added to the existing damage.
// </summary>
[DataField, AlwaysPushInheritance]
public DamageSpecifier? FlatDamageIncrease;

/// <summary>
/// Turns the left click into a power attack when the light attack misses.
/// </summary>
[DataField]
public bool? HeavyOnLightMiss;

// <summary>
// What to multiply the melee weapon range by.
// </summary>
[DataField, AlwaysPushInheritance]
public float? RangeModifier;

// <summary>
// What to multiply the attack rate by.
// </summary>
[DataField, AlwaysPushInheritance]
public float? AttackRateModifier;

public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
if (!entityManager.TryGetComponent<MeleeWeaponComponent>(uid, out var melee))
return;

if (SoundHit != null)
melee.SoundHit = SoundHit;

if (Animation != null)
melee.Animation = Animation.Value;

if (HeavyAnimationFromLight)
melee.WideAnimation = melee.Animation;

if (Damage != null)
melee.Damage = Damage;

if (FlatDamageIncrease != null)
melee.Damage += FlatDamageIncrease;

if (HeavyOnLightMiss != null)
melee.HeavyOnLightMiss = HeavyOnLightMiss.Value;

if (RangeModifier != null)
melee.Range *= RangeModifier.Value;

if (AttackRateModifier != null)
melee.AttackRate *= AttackRateModifier.Value;
}
}
6 changes: 6 additions & 0 deletions Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public sealed partial class MeleeWeaponComponent : Component
[DataField]
public bool DisableClick = false;

/// <summary>
/// If true, when a light attack misses, the weapon will perform a power attack instead.
/// </summary>
[DataField, AutoNetworkedField]
public bool HeavyOnLightMiss = false;

/*
* Melee combat works based around 2 types of attacks:
* 1. Click attacks with left-click. This attacks whatever is under your mnouse
Expand Down
43 changes: 17 additions & 26 deletions Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ public override void Initialize()
base.Initialize();

SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
SubscribeLocalEvent<MeleeWeaponComponent, ShotAttemptedEvent>(OnMeleeShotAttempted);
SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot);
SubscribeLocalEvent<BonusMeleeDamageComponent, GetMeleeDamageEvent>(OnGetBonusMeleeDamage);
SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier);
SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate);
Expand All @@ -93,24 +91,6 @@ private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEve
#endif
}

private void OnMeleeShotAttempted(EntityUid uid, MeleeWeaponComponent comp, ref ShotAttemptedEvent args)
{
if (comp.NextAttack > Timing.CurTime)
args.Cancel();
}

private void OnMeleeShot(EntityUid uid, MeleeWeaponComponent component, ref GunShotEvent args)
{
if (!TryComp<GunComponent>(uid, out var gun))
return;

if (gun.NextFire > component.NextAttack)
{
component.NextAttack = gun.NextFire;
DirtyField(uid, component, nameof(MeleeWeaponComponent.NextAttack));
}
}

private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args)
{
var attackRate = GetAttackRate(uid, args.User, component);
Expand Down Expand Up @@ -351,6 +331,8 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo
if (!CombatMode.IsInCombatMode(user))
return false;

var fireRateSwingModifier = 1f;

EntityUid? target = null;
switch (attack)
{
Expand All @@ -368,6 +350,9 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo
if (weaponUid == target)
return false;

break;
case HeavyAttackEvent:
fireRateSwingModifier = weapon.HeavyRateModifier;
break;
case DisarmAttackEvent disarm:
if (disarm.Target != null && !TryGetEntity(disarm.Target, out target))
Expand All @@ -386,7 +371,7 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo
}

// Windup time checked elsewhere.
var fireRate = TimeSpan.FromSeconds(1f / GetAttackRate(weaponUid, user, weapon));
var fireRate = TimeSpan.FromSeconds(GetAttackRate(weaponUid, user, weapon) * fireRateSwingModifier);
var swings = 0;

// TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly.
Expand Down Expand Up @@ -457,6 +442,16 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo

protected abstract bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session);

protected bool CanDoLightAttack(EntityUid user, [NotNullWhen(true)] EntityUid? target, MeleeWeaponComponent component, [NotNullWhen(true)] out TransformComponent? targetXform, ICommonSession? session = null)
{
targetXform = null;
return !Deleted(target) &&
HasComp<DamageableComponent>(target) &&
TryComp<TransformComponent>(target, out targetXform) &&
// Not in LOS.
InRange(user, target.Value, component.Range, session);
}

protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
{
// If I do not come back later to fix Light Attacks being Heavy Attacks you can throw me in the spider pit -Errant
Expand All @@ -465,11 +460,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity
var resistanceBypass = GetResistanceBypass(meleeUid, user, component);

// For consistency with wide attacks stuff needs damageable.
if (Deleted(target) ||
!HasComp<DamageableComponent>(target) ||
!TryComp(target, out TransformComponent? targetXform) ||
// Not in LOS.
!InRange(user, target.Value, component.Range, session))
if (!CanDoLightAttack(user, target, component, out var targetXform, session))
{
// Leave IsHit set to true, because the only time it's set to false
// is when a melee weapon is examined. Misses are inferred from an
Expand Down
8 changes: 5 additions & 3 deletions Resources/Locale/en-US/traits/traits.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,11 @@ trait-description-Feeble =
trait-name-MartialArtist = Martial Artist
trait-description-MartialArtist =
You have received formal training in unarmed combat, whether with Fists, Feet, or Claws.
Your unarmed melee attacks have a small range increase, and deal 50% more damage.
This does not apply to any form of armed melee, only the weapons you were naturally born with.
You have received formal training in unarmed combat, whether with fists, claws, feet, or teeth.
Your unarmed melee attack is now considered a single-target [color=orange]Power Attack[/color], requiring less precision.
Additionally, your unarmed melee attacks deal [color=yellow]20%[/color] more damage, attack [color=yellow]25%[/color] faster, and have [color=yellow]10%[/color] increased range.
This has no effect on damage dealt with any form of armed melee.
The [color=#9FED58]Boxer[/color], [color=#9FED58]Martial Artist[/color], and [color=#9FED58]Gladiator[/color] jobs start with this trait by default.
trait-name-Vigor = Vigor
trait-description-Vigor =
Expand Down
5 changes: 5 additions & 0 deletions Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@
damage:
types:
Blunt: 6 # It's tough.
heavyRateModifier: 1
heavyDamageBaseModifier: 1
heavyPartDamageMultiplier: 1
heavyStaminaCost: 0
maxTargets: 1
- type: MobPrice
price: 1500 # Kidnapping a living person and selling them for cred is a good move.
deathPenalty: 0.01 # However they really ought to be living and intact, otherwise they're worth 100x less.
Expand Down
5 changes: 5 additions & 0 deletions Resources/Prototypes/Entities/Mobs/Species/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@
damage:
types:
Blunt: 5
heavyRateModifier: 1
heavyDamageBaseModifier: 1
heavyPartDamageMultiplier: 1
heavyStaminaCost: 0
maxTargets: 1
- type: SleepEmitSound
- type: SSDIndicator
- type: StandingState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@
department: Security
min: 21600
special:
- !type:AddComponentSpecial
components:
- type: Boxer
modifiers:
coefficients: # These only apply to unarmed
Blunt: 1.5
Slash: 1.5
Piercing: 1.5
- !type:AddTraitSpecial
traits:
- MartialArtist

- type: startingGear
id: NyanoGladiatorGear
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,9 @@
- Theatre # DeltaV - Add Theatre access
- Boxer # DeltaV - Add Boxer access
special:
- !type:AddComponentSpecial
components:
- type: Boxer
modifiers:
coefficients: # These only apply to unarmed
Blunt: 1.5
Slash: 1.5
Piercing: 1.5
- !type:AddTraitSpecial
traits:
- MartialArtist

- type: startingGear
id: MartialArtistGear
Expand Down
11 changes: 3 additions & 8 deletions Resources/Prototypes/Roles/Jobs/Wildcards/boxer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,9 @@
- Theatre # DeltaV - Add Theatre access
- Boxer # DeltaV - Add Boxer access
special: # Nyanotrasen - BoxerComponent, see Content.Server/Nyanotrasen/Abilities/Boxer/Boxer/BoxerComponent.cs
- !type:AddComponentSpecial
components:
- type: Boxer
modifiers:
coefficients: # These only apply to unarmed
Blunt: 1.5
Slash: 1.5
Piercing: 1.5
- !type:AddTraitSpecial
traits:
- MartialArtist

- type: startingGear
id: BoxerGear
Expand Down
Loading

0 comments on commit 6c43d00

Please sign in to comment.