Files
wwdpublic/Content.Shared/Contests/ContestsSystem.cs
VMSolidus 806dc0107b Mass Contests Remake (#480)
# Description

Part of Issue #467 

This is a complete re imagining of the Nyanotrasen Mass Contest
System(Long since removed from the game). This system adds a highly
flexible function that outputs a ratio of a given entity's mass, that is
optionally relative to another entity. I've primarily written this
system to be used in conjunction with PR #458 , as it adds several new
implementations of variable player mass to the game.

How this differs from the original Mass Contest system is that it is
configured via hotloaded CVars, and is inherently clamped so that
character mass only modifies functions by a finite amount rather than
providing infinite scaling. This essentially means that while an Oni is
25% better at shoving a Felinid to the floor thanks to their different
masses, a 2000kg Lamia is also only 25% better at shoving a Felinid to
the floor, rather than 50000% better. The inverse is also true, a small
player character can only be 25% better or worse at a given
implementation. These implementations are not handled directly by the
ContestSystem, and are instead handled directly by other systems that
call upon it.

This percentage limit can be modified by a new CVar at any time.
Additionally, the entire MassContest system can be optionally toggled
off completely at any time via CVar, without needing to modify any code
that calls upon it.

At this time, I have included three different implementations to serve
as suitable examples for how MassContest can be used.

1. Weapon recoil is now modified by an entity's mass relative to the
human average baseline. Smaller characters experience more recoil,
larger characters experience less recoil
2. Disarm/Shove is now modified by Mass Contests. Entities that are
sized differently from their target have their shove/disarm chance
modified based on the ratio of performer and target mass.
3. Certain types of handcuffs(such as Cablecuffs and zipties) are now
faster to slip out of if you are smaller than the average.

# Changelog

🆑
- add: Mass Contests have returned in a new reworked form. 
- add: Weapon Recoil is now resisted by character mass. More massive
characters take less recoil, less massive characters take more recoil.
- add: Disarm and Shove actions are now modified by relative character
mass. It is easier to shove people around if you're bigger than them.
- add: Cablecuffs and Zipties are now easier to escape out of if you're
smaller.

---------

Signed-off-by: VMSolidus <evilexecutive@gmail.com>
Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com>
Co-authored-by: Danger Revolution! <142105406+DangerRevolution@users.noreply.github.com>
2024-07-13 09:05:51 +01:00

117 lines
5.1 KiB
C#

using Content.Shared.CCVar;
using Robust.Shared.Configuration;
using Robust.Shared.Physics.Components;
namespace Content.Shared.Contests
{
public sealed partial class ContestsSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
/// <summary>
/// The presumed average mass of a player entity
/// Defaulted to the average mass of an adult human
/// </summary>
private const float AverageMass = 71f;
#region Mass Contests
/// <summary>
/// Outputs the ratio of mass between a performer and the average human mass
/// </summary>
/// <param name="performerUid">Uid of Performer</param>
public float MassContest(EntityUid performerUid, float otherMass = AverageMass)
{
if (_cfg.GetCVar(CCVars.DoMassContests)
&& TryComp<PhysicsComponent>(performerUid, out var performerPhysics)
&& performerPhysics.Mass != 0)
return Math.Clamp(performerPhysics.Mass / otherMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage));
return 1f;
}
/// <inheritdoc cref="MassContest(EntityUid, float)"/>
/// <remarks>
/// MaybeMassContest, in case your entity doesn't exist
/// </remarks>
public float MassContest(EntityUid? performerUid, float otherMass = AverageMass)
{
if (_cfg.GetCVar(CCVars.DoMassContests))
{
var ratio = performerUid is { } uid ? MassContest(uid, otherMass) : 1f;
return ratio;
}
return 1f;
}
/// <summary>
/// Outputs the ratio of mass between a performer and the average human mass
/// If a function already has the performer's physics component, this is faster
/// </summary>
/// <param name="performerPhysics"></param>
public float MassContest(PhysicsComponent performerPhysics, float otherMass = AverageMass)
{
if (_cfg.GetCVar(CCVars.DoMassContests)
&& performerPhysics.Mass != 0)
return Math.Clamp(performerPhysics.Mass / otherMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage));
return 1f;
}
/// <summary>
/// Outputs the ratio of mass between a performer and a target, accepts either EntityUids or PhysicsComponents in any combination
/// If you have physics components already in your function, use <see cref="MassContest(PhysicsComponent, float)" /> instead
/// </summary>
/// <param name="performerUid"></param>
/// <param name="targetUid"></param>
public float MassContest(EntityUid performerUid, EntityUid targetUid)
{
if (_cfg.GetCVar(CCVars.DoMassContests)
&& TryComp<PhysicsComponent>(performerUid, out var performerPhysics)
&& TryComp<PhysicsComponent>(targetUid, out var targetPhysics)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage));
return 1f;
}
/// <inheritdoc cref="MassContest(EntityUid, EntityUid)"/>
public float MassContest(EntityUid performerUid, PhysicsComponent targetPhysics)
{
if (_cfg.GetCVar(CCVars.DoMassContests)
&& TryComp<PhysicsComponent>(performerUid, out var performerPhysics)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage));
return 1f;
}
/// <inheritdoc cref="MassContest(EntityUid, EntityUid)"/>
public float MassContest(PhysicsComponent performerPhysics, EntityUid targetUid)
{
if (_cfg.GetCVar(CCVars.DoMassContests)
&& TryComp<PhysicsComponent>(targetUid, out var targetPhysics)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage));
return 1f;
}
/// <inheritdoc cref="MassContest(EntityUid, EntityUid)"/>
public float MassContest(PhysicsComponent performerPhysics, PhysicsComponent targetPhysics)
{
if (_cfg.GetCVar(CCVars.DoMassContests)
&& performerPhysics.Mass != 0
&& targetPhysics.InvMass != 0)
return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage));
return 1f;
}
#endregion
}
}