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.