diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
index c242d448f27..b646c18c014 100644
--- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
+++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
@@ -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;
@@ -156,6 +157,177 @@ public sealed partial class MeleeWeaponComponent : Component
///
[DataField, AutoNetworkedField]
public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit");
+
+ #region Melee Contests Controller
+
+ ///
+ /// Controls whether this melee weapon allows for mass to factor into damage.
+ ///
+ public bool DoMassInteraction;
+
+ ///
+ /// When true, mass provides a disadvantage.
+ ///
+ public bool MassDisadvantage;
+
+ ///
+ /// When true, mass contests ignore clamp limitations for a melee weapon.
+ ///
+ public bool MassBypassClamp;
+
+ ///
+ /// Multiplies the acceptable range of outputs provided by mass contests for melee.
+ ///
+ public float MassRangeModifier = 1;
+
+ ///
+ /// The output of a mass contest is increased by this amount.
+ ///
+ public float MassOffset;
+
+ ///
+ /// Controls whether this melee weapon allows for stamina to factor into damage.
+ ///
+ public bool DoStaminaInteraction = true;
+
+ ///
+ /// When true, stamina provides a disadvantage.
+ ///
+ public bool StaminaDisadvantage = true;
+
+ ///
+ /// When true, stamina contests ignore clamp limitations for a melee weapon.
+ ///
+ public bool StaminaBypassClamp;
+
+ ///
+ /// Multiplies the acceptable range of outputs provided by mass contests for melee.
+ ///
+ public float StaminaRangeModifier = 2;
+
+ ///
+ /// The output of a stamina contest is increased by this amount.
+ ///
+ public float StaminaOffset = 0.25f;
+
+ ///
+ /// Controls whether this melee weapon allows for health to factor into damage.
+ ///
+ public bool DoHealthInteraction = true;
+
+ ///
+ /// When true, health contests provide a disadvantage.
+ ///
+ public bool HealthDisadvantage;
+
+ ///
+ /// When true, health contests ignore clamp limitations for a melee weapon.
+ ///
+ public bool HealthBypassClamp;
+
+ ///
+ /// Multiplies the acceptable range of outputs provided by mass contests for melee.
+ ///
+ public float HealthRangeModifier = 2;
+
+ ///
+ /// The output of health contests is increased by this amount.
+ ///
+ public float HealthOffset;
+
+ ///
+ /// Controls whether this melee weapon allows for psychic casting stats to factor into damage.
+ ///
+ public bool DoMindInteraction;
+
+ ///
+ /// When true, high psychic casting stats provide a disadvantage.
+ ///
+ public bool MindDisadvantage;
+
+ ///
+ /// When true, mind contests ignore clamp limitations for a melee weapon.
+ ///
+ public bool MindBypassClamp;
+
+ ///
+ /// Multiplies the acceptable range of outputs provided by mind contests for melee.
+ ///
+ public float MindRangeModifier = 1;
+
+ ///
+ /// The output of a mind contest is increased by this amount.
+ ///
+ public float MindOffset;
+
+ ///
+ /// Controls whether this melee weapon allows mood to factor into damage.
+ ///
+ public bool DoMoodInteraction;
+
+ ///
+ /// When true, mood provides a disadvantage.
+ ///
+ public bool MoodDisadvantage;
+
+ ///
+ /// When true, mood contests ignore clamp limitations for a melee weapon.
+ ///
+ public bool MoodBypassClamp;
+
+ ///
+ /// Multiplies the acceptable range of outputs provided by mood contests for melee.
+ ///
+ public float MoodRangeModifier = 1;
+
+ ///
+ /// The output of mood contests is increased by this amount.
+ ///
+ public float MoodOffset;
+
+ ///
+ /// 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.
+ ///
+ public bool DoEveryInteraction;
+
+ ///
+ /// When true, EveryContest provides a disadvantage.
+ ///
+ public bool EveryDisadvantage;
+
+ ///
+ /// How much Mass is considered for an EveryContest.
+ ///
+ public float EveryMassWeight = 1;
+
+ ///
+ /// How much Stamina is considered for an EveryContest.
+ ///
+ public float EveryStaminaWeight = 1;
+
+ ///
+ /// How much Health is considered for an EveryContest.
+ ///
+ public float EveryHealthWeight = 1;
+
+ ///
+ /// How much psychic casting stats are considered for an EveryContest.
+ ///
+ public float EveryMindWeight = 1;
+
+ ///
+ /// How much mood is considered for an EveryContest.
+ ///
+ public float EveryMoodWeight = 1;
+
+ ///
+ /// When true, the EveryContest sums the results of all contests rather than multiplying them,
+ /// probably giving you a very, very, very large multiplier...
+ ///
+ public bool EveryInteractionSumOrMultiply;
+
+ #endregion
}
///
diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.Utility.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.Utility.cs
new file mode 100644
index 00000000000..a3fc1b7f5e5
--- /dev/null
+++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.Utility.cs
@@ -0,0 +1,79 @@
+using Content.Shared.CCVar;
+
+namespace Content.Shared.Weapons.Melee;
+
+public abstract partial class SharedMeleeWeaponSystem : EntitySystem
+{
+ ///
+ /// 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.
+ ///
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
index b5a537b7e15..687264c13d9 100644
--- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
+++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
@@ -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;
@@ -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!;
@@ -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!;
private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);
@@ -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)
@@ -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)
@@ -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.