@@ -1,134 +0,0 @@
|
|||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Damage.Components;
|
|
||||||
using Content.Shared.Mobs;
|
|
||||||
using Content.Shared.Mobs.Systems;
|
|
||||||
using Robust.Shared.Physics.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 MobThresholdSystem _mobThresholdSystem = 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, false) || !Resolve(target, ref targetPhysics, false))
|
|
||||||
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, false) || !Resolve(target, ref targetDamage, false))
|
|
||||||
return 1f;
|
|
||||||
|
|
||||||
// First, we'll see what health they go into crit at.
|
|
||||||
//TODO: refactor this entire system.... Why does this exist, this shouldn't be calculated off health
|
|
||||||
var rollerThreshold = 100f;
|
|
||||||
if (!_mobThresholdSystem.TryGetThresholdForState(roller, MobState.Critical, out var rollerCritThreshold))
|
|
||||||
{
|
|
||||||
if (_mobThresholdSystem.TryGetThresholdForState(roller, MobState.Critical,
|
|
||||||
out var rollerdeadThreshold))
|
|
||||||
{
|
|
||||||
rollerThreshold = rollerdeadThreshold.Value.Float();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rollerThreshold = rollerCritThreshold.Value.Float();
|
|
||||||
}
|
|
||||||
var targetThreshold = 100f;
|
|
||||||
if (!_mobThresholdSystem.TryGetThresholdForState(roller, MobState.Critical, out var targetCritThreshold))
|
|
||||||
{
|
|
||||||
if (_mobThresholdSystem.TryGetThresholdForState(roller, MobState.Critical,
|
|
||||||
out var targetdeadThreshold))
|
|
||||||
{
|
|
||||||
targetThreshold = targetdeadThreshold.Value.Float();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
targetThreshold = targetCritThreshold.Value.Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, we'll see how their damage compares
|
|
||||||
var rollerDamageScore = (float) rollerDamage.TotalDamage / rollerThreshold;
|
|
||||||
var 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, false) || !Resolve(target, ref targetStamina, false))
|
|
||||||
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
|
|
||||||
{
|
|
||||||
// TODO: Should just be a curve
|
|
||||||
<= 0 => 1f,
|
|
||||||
<= 0.25f => 0.9f,
|
|
||||||
<= 0.5f => 0.75f,
|
|
||||||
<= 0.75f => 0.6f,
|
|
||||||
<= 0.95f => 0.45f,
|
|
||||||
_ => 0.05f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using Content.Server.Contests;
|
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Storage.Components;
|
using Content.Server.Storage.Components;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
@@ -7,7 +6,6 @@ using Content.Shared.Hands.EntitySystems;
|
|||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
using Content.Shared.Movement.Systems;
|
|
||||||
using Content.Shared.Resist;
|
using Content.Shared.Resist;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -21,12 +19,6 @@ public sealed class EscapeInventorySystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||||
[Dependency] private readonly ContestsSystem _contests = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// You can't escape the hands of an entity this many times more massive than you.
|
|
||||||
/// </summary>
|
|
||||||
public const float MaximumMassDisadvantage = 6f;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -53,23 +45,9 @@ public sealed class EscapeInventorySystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Contested
|
// Contested
|
||||||
if (_handsSystem.IsHolding(container.Owner, uid, out var inHand))
|
if (_handsSystem.IsHolding(container.Owner, uid, out _))
|
||||||
{
|
{
|
||||||
var contestResults = _contests.MassContest(uid, container.Owner);
|
AttemptEscape(uid, container.Owner, component);
|
||||||
|
|
||||||
// Inverse if we aren't going to divide by 0, otherwise just use a default multiplier of 1.
|
|
||||||
if (contestResults != 0)
|
|
||||||
contestResults = 1 / contestResults;
|
|
||||||
else
|
|
||||||
contestResults = 1;
|
|
||||||
|
|
||||||
if (contestResults >= MaximumMassDisadvantage)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-failed-resisting"), uid, uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AttemptEscape(uid, container.Owner, component, contestResults);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Content.Server.Chat.Systems;
|
|||||||
using Content.Server.Chemistry.Components;
|
using Content.Server.Chemistry.Components;
|
||||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||||
using Content.Server.CombatMode.Disarm;
|
using Content.Server.CombatMode.Disarm;
|
||||||
using Content.Server.Contests;
|
|
||||||
using Content.Server.Movement.Systems;
|
using Content.Server.Movement.Systems;
|
||||||
using Content.Shared.Actions.Events;
|
using Content.Shared.Actions.Events;
|
||||||
using Content.Shared.Administration.Components;
|
using Content.Shared.Administration.Components;
|
||||||
@@ -37,7 +36,6 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
[Dependency] private readonly ContestsSystem _contests = default!;
|
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
[Dependency] private readonly LagCompensationSystem _lag = default!;
|
[Dependency] private readonly LagCompensationSystem _lag = default!;
|
||||||
@@ -223,9 +221,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||||||
if (HasComp<DisarmProneComponent>(disarmed))
|
if (HasComp<DisarmProneComponent>(disarmed))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
var contestResults = 1 - _contests.OverallStrengthContest(disarmer, disarmed);
|
var chance = disarmerComp.BaseDisarmFailChance;
|
||||||
|
|
||||||
float chance = (disarmerComp.BaseDisarmFailChance + contestResults);
|
|
||||||
|
|
||||||
if (inTargetHand != null && TryComp<DisarmMalusComponent>(inTargetHand, out var malus))
|
if (inTargetHand != null && TryComp<DisarmMalusComponent>(inTargetHand, out var malus))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user