diff --git a/Content.Shared/Contests/ContestsSystem.Utilities.cs b/Content.Shared/Contests/ContestsSystem.Utilities.cs index 42a69bff478..0278d75e943 100644 --- a/Content.Shared/Contests/ContestsSystem.Utilities.cs +++ b/Content.Shared/Contests/ContestsSystem.Utilities.cs @@ -1,4 +1,5 @@ using Content.Shared.CCVar; +using Robust.Shared.Serialization; namespace Content.Shared.Contests; public sealed partial class ContestsSystem @@ -18,4 +19,265 @@ private bool ContestClampOverride(bool bypassClamp) { return _cfg.GetCVar(CCVars.AllowClampOverride) && bypassClamp; } + + /// + /// Constructor for feeding options from a given set of ContestArgs into the ContestsSystem. + /// Just multiply by this and give it a user EntityUid and a ContestArgs variable. That's all you need to know. + /// + public float ContestConstructor(EntityUid user, ContestArgs args) + { + if (!_cfg.GetCVar(CCVars.DoContestsSystem)) + return 1; + + if (!args.DoEveryInteraction) + return args.DoMassInteraction ? ((!args.MassDisadvantage + ? MassContest(user, args.MassBypassClamp, args.MassRangeModifier) + : 1 / MassContest(user, args.MassBypassClamp, args.MassRangeModifier)) + + args.MassOffset) + : 1 + * (args.DoStaminaInteraction ? ((!args.StaminaDisadvantage + ? StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier) + : 1 / StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier)) + + args.StaminaOffset) + : 1) + * (args.DoHealthInteraction ? ((!args.HealthDisadvantage + ? HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier) + : 1 / HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier)) + + args.HealthOffset) + : 1) + * (args.DoMindInteraction ? ((!args.MindDisadvantage + ? MindContest(user, args.MindBypassClamp, args.MindRangeModifier) + : 1 / MindContest(user, args.MindBypassClamp, args.MindRangeModifier)) + + args.MindOffset) + : 1) + * (args.DoMoodInteraction ? ((!args.MoodDisadvantage + ? MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier) + : 1 / MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier)) + + args.MoodOffset) + : 1); + + var everyContest = EveryContest(user, + args.MassBypassClamp, + args.StaminaBypassClamp, + args.HealthBypassClamp, + args.MindBypassClamp, + args.MoodBypassClamp, + args.MassRangeModifier, + args.StaminaRangeModifier, + args.HealthRangeModifier, + args.MindRangeModifier, + args.MoodRangeModifier, + args.EveryMassWeight, + args.EveryStaminaWeight, + args.EveryHealthWeight, + args.EveryMindWeight, + args.EveryMoodWeight, + args.EveryInteractionSumOrMultiply); + + return !args.EveryDisadvantage ? everyContest : 1 / everyContest; + } } + +[Serializable, NetSerializable, DataDefinition] +public sealed partial class ContestArgs +{ + /// + /// Controls whether this melee weapon allows for mass to factor into damage. + /// + [DataField] + public bool DoMassInteraction; + + /// + /// When true, mass provides a disadvantage. + /// + [DataField] + public bool MassDisadvantage; + + /// + /// When true, mass contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MassBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float MassRangeModifier = 1; + + /// + /// The output of a mass contest is increased by this amount. + /// + [DataField] + public float MassOffset; + + /// + /// Controls whether this melee weapon allows for stamina to factor into damage. + /// + [DataField] + public bool DoStaminaInteraction; + + /// + /// When true, stamina provides a disadvantage. + /// + [DataField] + public bool StaminaDisadvantage; + + /// + /// When true, stamina contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool StaminaBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float StaminaRangeModifier = 1; + + /// + /// The output of a stamina contest is increased by this amount. + /// + [DataField] + public float StaminaOffset; + + /// + /// Controls whether this melee weapon allows for health to factor into damage. + /// + [DataField] + public bool DoHealthInteraction; + + /// + /// When true, health contests provide a disadvantage. + /// + [DataField] + public bool HealthDisadvantage; + + /// + /// When true, health contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool HealthBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float HealthRangeModifier = 1; + + /// + /// The output of health contests is increased by this amount. + /// + [DataField] + public float HealthOffset; + + /// + /// Controls whether this melee weapon allows for psychic casting stats to factor into damage. + /// + [DataField] + public bool DoMindInteraction; + + /// + /// When true, high psychic casting stats provide a disadvantage. + /// + [DataField] + public bool MindDisadvantage; + + /// + /// When true, mind contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MindBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mind contests for melee. + /// + [DataField] + public float MindRangeModifier = 1; + + /// + /// The output of a mind contest is increased by this amount. + /// + [DataField] + public float MindOffset; + + /// + /// Controls whether this melee weapon allows mood to factor into damage. + /// + [DataField] + public bool DoMoodInteraction; + + /// + /// When true, mood provides a disadvantage. + /// + [DataField] + public bool MoodDisadvantage; + + /// + /// When true, mood contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MoodBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mood contests for melee. + /// + [DataField] + public float MoodRangeModifier = 1; + + /// + /// The output of mood contests is increased by this amount. + /// + [DataField] + 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. + /// + [DataField] + public bool DoEveryInteraction; + + /// + /// When true, EveryContest provides a disadvantage. + /// + [DataField] + public bool EveryDisadvantage; + + /// + /// How much Mass is considered for an EveryContest. + /// + [DataField] + public float EveryMassWeight = 1; + + /// + /// How much Stamina is considered for an EveryContest. + /// + [DataField] + public float EveryStaminaWeight = 1; + + /// + /// How much Health is considered for an EveryContest. + /// + [DataField] + public float EveryHealthWeight = 1; + + /// + /// How much psychic casting stats are considered for an EveryContest. + /// + [DataField] + public float EveryMindWeight = 1; + + /// + /// How much mood is considered for an EveryContest. + /// + [DataField] + 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... + /// + [DataField] + public bool EveryInteractionSumOrMultiply; +} \ No newline at end of file diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index c242d448f27..43e3096d158 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Contests; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Robust.Shared.Audio; @@ -156,6 +157,20 @@ public sealed partial class MeleeWeaponComponent : Component /// [DataField, AutoNetworkedField] public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit"); + + /// + /// Arguments for the MeleeContestInteractions constructor + /// + [DataField] + public ContestArgs ContestArgs = new ContestArgs + { + DoStaminaInteraction = true, + StaminaDisadvantage = true, + StaminaRangeModifier = 2, + StaminaOffset = 0.25f, + DoHealthInteraction = true, + HealthRangeModifier = 1.5f, + }; } /// diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index b5a537b7e15..fd77ad31a45 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -32,7 +32,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!; @@ -225,6 +225,9 @@ public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponCompo var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user); RaiseLocalEvent(uid, ref ev); + if (component.ContestArgs is not null) + ev.Damage *= _contests.ContestConstructor(user, component.ContestArgs); + return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers); } @@ -249,9 +252,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 +441,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.