Standard contests + disarm math tweak (#10087)

This commit is contained in:
Rane
2022-07-30 23:41:30 -04:00
committed by GitHub
parent 24d05aa5f8
commit de789ad5ea
2 changed files with 128 additions and 20 deletions

View File

@@ -4,6 +4,7 @@ using Content.Server.Administration.Logs;
using Content.Server.CombatMode.Disarm; using Content.Server.CombatMode.Disarm;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Contests;
using Content.Server.Weapon.Melee; using Content.Server.Weapon.Melee;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Audio; using Content.Shared.Audio;
@@ -29,6 +30,8 @@ namespace Content.Server.CombatMode
[Dependency] private readonly IAdminLogManager _adminLogger= default!; [Dependency] private readonly IAdminLogManager _adminLogger= default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ContestsSystem _contests = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -108,33 +111,15 @@ namespace Content.Server.CombatMode
private float CalculateDisarmChance(EntityUid disarmer, EntityUid disarmed, EntityUid? inTargetHand, SharedCombatModeComponent disarmerComp) private float CalculateDisarmChance(EntityUid disarmer, EntityUid disarmed, EntityUid? inTargetHand, SharedCombatModeComponent disarmerComp)
{ {
float healthMod = 0;
if (HasComp<DisarmProneComponent>(disarmer)) if (HasComp<DisarmProneComponent>(disarmer))
return 1.0f; return 1.0f;
if (HasComp<DisarmProneComponent>(disarmed)) if (HasComp<DisarmProneComponent>(disarmed))
return 0.0f; return 0.0f;
if (TryComp<DamageableComponent>(disarmer, out var disarmerDamage) && TryComp<DamageableComponent>(disarmed, out var disarmedDamage)) var contestResults = 1 - _contests.OverallStrengthContest(disarmer, disarmed);
{
// I wanted this to consider their mob state thresholds too but I'm not touching that shitcode after having a go at this.
healthMod = (((float) disarmedDamage.TotalDamage - (float) disarmerDamage.TotalDamage) / 200); // Ex. You have 0 damage, they have 90, you get a 45% chance increase
}
float massMod = 0; float chance = (disarmerComp.BaseDisarmFailChance + contestResults);
if (TryComp<PhysicsComponent>(disarmer, out var disarmerPhysics) && TryComp<PhysicsComponent>(disarmed, out var disarmedPhysics))
{
if (disarmerPhysics.FixturesMass != 0) // yeah this will never happen but let's not kill the server if it does
massMod = (((disarmedPhysics.FixturesMass / disarmerPhysics.FixturesMass - 1 ) / 2)); // Ex, you weigh 120, they weigh 70, you get a 29% bonus
}
float chance = (disarmerComp.BaseDisarmFailChance - healthMod - massMod);
if (HasComp<SlowedDownComponent>(disarmer)) // might need to revisit this part after stamina damage, right now this is basically "pre-stun"
chance += 0.35f;
if (HasComp<SlowedDownComponent>(disarmed))
chance -= 0.35f;
if (inTargetHand != null && TryComp<DisarmMalusComponent>(inTargetHand, out var malus)) if (inTargetHand != null && TryComp<DisarmMalusComponent>(inTargetHand, out var malus))
{ {

View File

@@ -0,0 +1,123 @@
using Content.Shared.Damage;
using Content.Shared.MobState.EntitySystems;
using Content.Shared.MobState.Components;
using Content.Server.Damage.Components;
namespace Content.Server.Contests
{
/// <summary>
/// Standardized contests.
/// A contest is figuring out, based on data in components on two entities,
/// which one has an advantage in a situation. The advantage is expressed by a multiplier.
/// 1 = No advantage to either party.
/// >1 = Advantage to roller
/// <1 = Advantage to target
/// Roller should be the entity with an advantage from being bigger/healthier/more skilled, etc.
/// <summary>
public sealed class ContestsSystem : EntitySystem
{
[Dependency] private readonly SharedMobStateSystem _mobStateSystem = default!;
/// <summary>
/// Returns the roller's mass divided by the target's.
/// </summary>
public float MassContest(EntityUid roller, EntityUid target, PhysicsComponent? rollerPhysics = null, PhysicsComponent? targetPhysics = null)
{
if (!Resolve(roller, ref rollerPhysics) || !Resolve(target, ref targetPhysics))
return 1f;
if (rollerPhysics == null || targetPhysics == null)
return 1f;
if (targetPhysics.FixturesMass == 0)
return 1f;
return (rollerPhysics.FixturesMass / targetPhysics.FixturesMass);
}
/// <summary>
/// Tries to compare both entities damage to the damage they will enter crit at.
/// After that, it runs the % towards crit through a converter that smooths out and makes
/// the the ratios less severe.
/// Returns the roller's adjusted damage value divided by the target's. Higher is better for the roller.
/// <summary>
public float DamageContest(EntityUid roller, EntityUid target, DamageableComponent? rollerDamage = null, DamageableComponent? targetDamage = null)
{
if (!Resolve(roller, ref rollerDamage) || !Resolve(target, ref targetDamage))
return 1f;
if (rollerDamage == null || targetDamage == null)
return 1f;
// First, we'll see what health they go into crit at.
float rollerThreshold = 100f;
float targetThreshold = 100f;
if (TryComp<MobStateComponent>(roller, out var rollerState) && rollerState != null &&
_mobStateSystem.TryGetEarliestIncapacitatedState(rollerState, 10000, out _, out var rollerCritThreshold))
rollerThreshold = (float) rollerCritThreshold;
if (TryComp<MobStateComponent>(target, out var targetState) && targetState != null &&
_mobStateSystem.TryGetEarliestIncapacitatedState(targetState, 10000, out _, out var targetCritThreshold))
targetThreshold = (float) targetCritThreshold;
// Next, we'll see how their damage compares
float rollerDamageScore = (float) rollerDamage.TotalDamage / rollerThreshold;
float targetDamageScore = (float) targetDamage.TotalDamage / targetThreshold;
return DamageThresholdConverter(rollerDamageScore) / DamageThresholdConverter(targetDamageScore);
}
/// <summary>
/// Finds the % of each entity's stamina damage towards its stam crit threshold.
/// It then runs the % towards the converter to smooth/soften it.
/// Returns the roller's decimal % divided by the target's. Higher value is better for the roller.
/// <summary>
public float StaminaContest(EntityUid roller, EntityUid target, StaminaComponent? rollerStamina = null, StaminaComponent? targetStamina = null)
{
if (!Resolve(roller, ref rollerStamina) || !Resolve(target, ref targetStamina))
return 1f;
if (rollerStamina == null || targetStamina == null)
return 1f;
var rollerDamageScore= rollerStamina.StaminaDamage / rollerStamina.CritThreshold;
var targetDamageScore = targetStamina.StaminaDamage / targetStamina.CritThreshold;
return DamageThresholdConverter(rollerDamageScore) / DamageThresholdConverter(targetDamageScore);
}
/// <summary>
/// This will compare the roller and target's damage, mass, and stamina. See the functions for what each one does.
/// You can change the 'weighting' to make the tests weigh more or less than the other tests.
/// Returns a weighted average of all 3 tests.
/// <summary>
public float OverallStrengthContest(EntityUid roller, EntityUid target, float damageWeight = 1f, float massWeight = 1f, float stamWeight = 1f)
{
var weightTotal = damageWeight + massWeight + stamWeight;
var damageMultiplier = damageWeight / weightTotal;
var massMultiplier = massWeight / weightTotal;
var stamMultiplier = stamWeight / weightTotal;
return ((DamageContest(roller, target) * damageMultiplier) + (MassContest(roller, target) * massMultiplier)
+ (StaminaContest(roller, target) * stamMultiplier));
}
/// <summary>
/// This softens out the huge advantages that damage contests would lead to otherwise.
/// Once you are crit or near crit, we just let the massive advantages roll with what could be a 20x.
/// </summary>
public float DamageThresholdConverter(float score)
{
return score switch
{
<= 0 => 1f,
<= 0.25f => 0.9f,
<= 0.5f => 0.75f,
<= 0.75f => 0.6f,
<= 0.95f => 0.45f,
_ => 0.05f
};
}
}
}