Skip to content

Commit

Permalink
NyanoCombat Part 4: MeleeSystem Refactor Part 2, Electric Boogaloo (S…
Browse files Browse the repository at this point in the history
…imple-Station#934)

# Description

Surprise motherfucker, here's a massive fucking refactor of
SharedMeleeWeaponSystem.
The MeleeWeaponComponent now allows for melee weapons to directly
configure how and what members of the ContestsSystem they wish to
utilize for influencing damage. Additionally, the damage of a melee
weapon modified by Contests is now also visible when examined(Although
this only reflects damage as per the current condition of the user).

# Changelog

:cl:
- add: Melee Weapons can now individually define their interactions with
the ContestsSystem.
- add: Added the ContestArgs type, allowing arbitrarily any component to
contain a list of arguments for ContestSystems.
- add: Added the ContestConstructor, a new type of meta-Contest that
enables other systems to use components to define all possible contest
behaviors, rather than needing to hardcode specific interactions.
  • Loading branch information
VMSolidus committed Sep 20, 2024
1 parent 80daa94 commit 7119dd0
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 7 deletions.
262 changes: 262 additions & 0 deletions Content.Shared/Contests/ContestsSystem.Utilities.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Shared.CCVar;
using Robust.Shared.Serialization;

namespace Content.Shared.Contests;
public sealed partial class ContestsSystem
Expand All @@ -18,4 +19,265 @@ private bool ContestClampOverride(bool bypassClamp)
{
return _cfg.GetCVar(CCVars.AllowClampOverride) && bypassClamp;
}

/// <summary>
/// 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.
/// </summary>
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
{
/// <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;

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

/// <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 = 1;

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

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

/// <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 = 1;

/// <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;
}
15 changes: 15 additions & 0 deletions Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared.Contests;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;
Expand Down Expand Up @@ -156,6 +157,20 @@ public sealed partial class MeleeWeaponComponent : Component
/// </summary>
[DataField, AutoNetworkedField]
public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit");

/// <summary>
/// Arguments for the MeleeContestInteractions constructor
/// </summary>
[DataField]
public ContestArgs ContestArgs = new ContestArgs
{
DoStaminaInteraction = true,
StaminaDisadvantage = true,
StaminaRangeModifier = 2,
StaminaOffset = 0.25f,
DoHealthInteraction = true,
HealthRangeModifier = 1.5f,
};
}

/// <summary>
Expand Down
13 changes: 6 additions & 7 deletions Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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!;
Expand Down Expand Up @@ -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);
}

Expand All @@ -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)
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 7119dd0

Please sign in to comment.