Skip to content
205 changes: 205 additions & 0 deletions Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Content.Shared.UserInterface;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
Expand Down Expand Up @@ -156,6 +157,210 @@ public sealed partial class MeleeWeaponComponent : Component
/// </summary>
[DataField, AutoNetworkedField]
public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit");

#region Melee Contests Controller

/// <summary>
/// Controls whether this melee weapon allows for mass to factor into damage.
/// </summary>
[DataField]
public bool DoMassInteraction;

/// <summary>
/// When true, mass provides a disadvantage.
/// </summary>
[DataField]
public bool MassDisadvantage;

/// <summary>
/// When true, mass contests ignore clamp limitations for a melee weapon.
/// </summary>
[DataField]
public bool MassBypassClamp;

/// <summary>
/// Multiplies the acceptable range of outputs provided by mass contests for melee.
/// </summary>
[DataField]
public float MassRangeModifier = 1;

/// <summary>
/// The output of a mass contest is increased by this amount.
/// </summary>
[DataField]
public float MassOffset;

/// <summary>
/// Controls whether this melee weapon allows for stamina to factor into damage.
/// </summary>
[DataField]
public bool DoStaminaInteraction = true;

/// <summary>
/// When true, stamina provides a disadvantage.
/// </summary>
[DataField]
public bool StaminaDisadvantage = true;

/// <summary>
/// When true, stamina contests ignore clamp limitations for a melee weapon.
/// </summary>
[DataField]
public bool StaminaBypassClamp;

/// <summary>
/// Multiplies the acceptable range of outputs provided by mass contests for melee.
/// </summary>
[DataField]
public float StaminaRangeModifier = 2;

/// <summary>
/// The output of a stamina contest is increased by this amount.
/// </summary>
[DataField]
public float StaminaOffset = 0.25f;

/// <summary>
/// Controls whether this melee weapon allows for health to factor into damage.
/// </summary>
[DataField]
public bool DoHealthInteraction = true;

/// <summary>
/// When true, health contests provide a disadvantage.
/// </summary>
[DataField]
public bool HealthDisadvantage;

/// <summary>
/// When true, health contests ignore clamp limitations for a melee weapon.
/// </summary>
[DataField]
public bool HealthBypassClamp;

/// <summary>
/// Multiplies the acceptable range of outputs provided by mass contests for melee.
/// </summary>
[DataField]
public float HealthRangeModifier = 2;

/// <summary>
/// The output of health contests is increased by this amount.
/// </summary>
[DataField]
public float HealthOffset;

/// <summary>
/// Controls whether this melee weapon allows for psychic casting stats to factor into damage.
/// </summary>
[DataField]
public bool DoMindInteraction;

/// <summary>
/// When true, high psychic casting stats provide a disadvantage.
/// </summary>
[DataField]
public bool MindDisadvantage;

/// <summary>
/// When true, mind contests ignore clamp limitations for a melee weapon.
/// </summary>
[DataField]
public bool MindBypassClamp;

/// <summary>
/// Multiplies the acceptable range of outputs provided by mind contests for melee.
/// </summary>
[DataField]
public float MindRangeModifier = 1;

/// <summary>
/// The output of a mind contest is increased by this amount.
/// </summary>
[DataField]
public float MindOffset;

/// <summary>
/// Controls whether this melee weapon allows mood to factor into damage.
/// </summary>
[DataField]
public bool DoMoodInteraction;

/// <summary>
/// When true, mood provides a disadvantage.
/// </summary>
[DataField]
public bool MoodDisadvantage;

/// <summary>
/// When true, mood contests ignore clamp limitations for a melee weapon.
/// </summary>
[DataField]
public bool MoodBypassClamp;

/// <summary>
/// Multiplies the acceptable range of outputs provided by mood contests for melee.
/// </summary>
[DataField]
public float MoodRangeModifier = 1;

/// <summary>
/// The output of mood contests is increased by this amount.
/// </summary>
[DataField]
public float MoodOffset;

/// <summary>
/// Enables the EveryContest interaction for a melee weapon.
/// IF YOU PUT THIS ON ANY WEAPON OTHER THAN AN ADMEME, I WILL COME TO YOUR HOUSE AND SEND YOU TO MEET YOUR CREATOR WHEN THE PLAYERS COMPLAIN.
/// </summary>
[DataField]
public bool DoEveryInteraction;

/// <summary>
/// When true, EveryContest provides a disadvantage.
/// </summary>
[DataField]
public bool EveryDisadvantage;

/// <summary>
/// How much Mass is considered for an EveryContest.
/// </summary>
[DataField]
public float EveryMassWeight = 1;

/// <summary>
/// How much Stamina is considered for an EveryContest.
/// </summary>
[DataField]
public float EveryStaminaWeight = 1;

/// <summary>
/// How much Health is considered for an EveryContest.
/// </summary>
[DataField]
public float EveryHealthWeight = 1;

/// <summary>
/// How much psychic casting stats are considered for an EveryContest.
/// </summary>
[DataField]
public float EveryMindWeight = 1;

/// <summary>
/// How much mood is considered for an EveryContest.
/// </summary>
[DataField]
public float EveryMoodWeight = 1;

/// <summary>
/// When true, the EveryContest sums the results of all contests rather than multiplying them,
/// probably giving you a very, very, very large multiplier...
/// </summary>
[DataField]
public bool EveryInteractionSumOrMultiply;

#endregion
}

/// <summary>
Expand Down
79 changes: 79 additions & 0 deletions Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.Utility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Content.Shared.CCVar;

namespace Content.Shared.Weapons.Melee;

public abstract partial class SharedMeleeWeaponSystem : EntitySystem
{
/// <summary>
/// Constructor for feeding options from a given MeleeWeaponComponent into the ContestsSystem.
/// Just multiply by this and give it a user EntityUid and a MeleeWeapon component. That's all you need to know.
/// </summary>
public float MeleeContestInteractions(EntityUid user, MeleeWeaponComponent component)
{
if (!_config.GetCVar(CCVars.DoContestsSystem))
return 1;

return 1
* (component.DoMassInteraction ? ((!component.MassDisadvantage
? _contests.MassContest(user, component.MassBypassClamp, component.MassRangeModifier)
: 1 / _contests.MassContest(user, component.MassBypassClamp, component.MassRangeModifier))
+ component.MassOffset)
: 1)
* (component.DoStaminaInteraction ? ((!component.StaminaDisadvantage
? _contests.StaminaContest(user, component.StaminaBypassClamp, component.StaminaRangeModifier)
: 1 / _contests.StaminaContest(user, component.StaminaBypassClamp, component.StaminaRangeModifier))
+ component.StaminaOffset)
: 1)
* (component.DoHealthInteraction ? ((!component.HealthDisadvantage
? _contests.HealthContest(user, component.HealthBypassClamp, component.HealthRangeModifier)
: 1 / _contests.HealthContest(user, component.HealthBypassClamp, component.HealthRangeModifier))
+ component.HealthOffset)
: 1)
* (component.DoMindInteraction ? ((!component.MindDisadvantage
? _contests.MindContest(user, component.MindBypassClamp, component.MindRangeModifier)
: 1 / _contests.MindContest(user, component.MindBypassClamp, component.MindRangeModifier))
+ component.MindOffset)
: 1)
* (component.DoMoodInteraction ? ((!component.MoodDisadvantage
? _contests.MoodContest(user, component.MoodBypassClamp, component.MoodRangeModifier)
: 1 / _contests.MoodContest(user, component.MoodBypassClamp, component.MoodRangeModifier))
+ component.MoodOffset)
: 1)
* (component.DoEveryInteraction ? (!component.EveryDisadvantage
? _contests.EveryContest(user,
component.MassBypassClamp,
component.StaminaBypassClamp,
component.HealthBypassClamp,
component.MindBypassClamp,
component.MoodBypassClamp,
component.MassRangeModifier,
component.StaminaRangeModifier,
component.HealthRangeModifier,
component.MindRangeModifier,
component.MoodRangeModifier,
component.EveryMassWeight,
component.EveryStaminaWeight,
component.EveryHealthWeight,
component.EveryMindWeight,
component.EveryMoodWeight,
component.EveryInteractionSumOrMultiply)
: 1 / _contests.EveryContest(user,
component.MassBypassClamp,
component.StaminaBypassClamp,
component.HealthBypassClamp,
component.MindBypassClamp,
component.MoodBypassClamp,
component.MassRangeModifier,
component.StaminaRangeModifier,
component.HealthRangeModifier,
component.MindRangeModifier,
component.MoodRangeModifier,
component.EveryMassWeight,
component.EveryStaminaWeight,
component.EveryHealthWeight,
component.EveryMindWeight,
component.EveryMoodWeight,
component.EveryInteractionSumOrMultiply))
: 1);
}
}
14 changes: 6 additions & 8 deletions Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Systems;
Expand All @@ -32,7 +33,7 @@

namespace Content.Shared.Weapons.Melee;

public abstract class SharedMeleeWeaponSystem : EntitySystem
public abstract partial class SharedMeleeWeaponSystem : EntitySystem
{
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
[Dependency] protected readonly ActionBlockerSystem Blocker = default!;
Expand All @@ -49,6 +50,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly ContestsSystem _contests = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this added if not used, just asking.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a leftover from when the Contest Constructor lived in a Partial of SharedMeleeWeaponSystem.


private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);

Expand Down Expand Up @@ -225,7 +227,7 @@ public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponCompo
var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user);
RaiseLocalEvent(uid, ref ev);

return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers);
return DamageSpecifier.ApplyModifierSets(ev.Damage * MeleeContestInteractions(user, component), ev.Modifiers);
}

public float GetAttackRate(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null)
Expand All @@ -249,9 +251,7 @@ public FixedPoint2 GetHeavyDamageModifier(EntityUid uid, EntityUid user, MeleeWe

return ev.DamageModifier
* ev.Multipliers
* component.HeavyDamageBaseModifier
* _contests.StaminaContest(user, false, 2f) //Taking stamina damage reduces wide swing damage by up to 50%
/ _contests.HealthContest(user, false, 0.8f); //Being injured grants up to 20% more wide swing damage
* component.HeavyDamageBaseModifier;
}

public bool TryGetWeapon(EntityUid entity, out EntityUid weaponUid, [NotNullWhen(true)] out MeleeWeaponComponent? melee)
Expand Down Expand Up @@ -440,9 +440,7 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo

protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
{
var damage = GetDamage(meleeUid, user, component)
* _contests.StaminaContest(user) //Taking stamina damage reduces light attack damage by up to 25%
/ _contests.HealthContest(user, false, 0.8f); //Being injured grants up to 20% more damage;
var damage = GetDamage(meleeUid, user, component);
var target = GetEntity(ev.Target);

// For consistency with wide attacks stuff needs damageable.
Expand Down