Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Balance] Surgery healing tweaks #911

Merged
merged 2 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Content.Server/EntityEffects/Effects/HealthChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Robust.Shared.Prototypes;
using System.Linq;
using System.Text.Json.Serialization;
using Content.Shared.Backmen.Targeting;

namespace Content.Server.EntityEffects.Effects
{
Expand Down Expand Up @@ -124,7 +125,13 @@ public override void Effect(EntityEffectBaseArgs args)
args.TargetEntity,
Damage * scale,
IgnoreResistances,
interruptsDoAfters: false);
interruptsDoAfters: false,
// start-backmen: surgery
targetPart: TargetBodyPart.All,
partMultiplier: 0.5f,
canSever: false,
evade: true);
// end-backmen: surgery
}
}
}
44 changes: 33 additions & 11 deletions Content.Server/Medical/HealingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
using System.Linq;
using Content.Shared.Backmen.Targeting;

namespace Content.Server.Medical;

Expand Down Expand Up @@ -86,27 +87,48 @@ entity.Comp.DamageContainerID is not null &&
if (healing.ModifyBloodLevel != 0)
_bloodstreamSystem.TryModifyBloodLevel(entity.Owner, healing.ModifyBloodLevel);

var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage, true, origin: args.Args.User);
var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage, true, origin: args.Args.User, partMultiplier: 0f); // No parts healing now!

if (healed == null && healing.BloodlossModifier != 0)
return;

var total = healed?.GetTotal() ?? FixedPoint2.Zero;

/* This is rather shitcodey. Problem is that right now damage is coupled to integrity.
If the body is fully healed, all of the checks on TryChangeDamage stop us from actually healing.
So in this case we add a special check to heal anyway if TryChangeDamage returns null.
*/
if (total == 0)
// start-backmen: surgery
// Try to heal some body part.
if (total == 0 && ArePartsDamaged(entity))
{
var partHealing = healing.Damage;

var parts = _bodySystem.GetBodyChildren(args.Target).ToList();
// We fetch the most damaged body part
var mostDamaged = parts.MinBy(x => x.Component.TotalDamage);
var targetBodyPart = _bodySystem.GetTargetBodyPart(mostDamaged);
var damagedParts = new List<(Entity<BodyPartComponent>, FixedPoint2)>();
foreach (var part in parts)
{
// Get all body parts that are damaged with *partHealing* types of damage.
var possibleHealing = part.Component.Damage;
possibleHealing.ExclusiveAdd(partHealing);
possibleHealing.ClampMin(part.Component.MinIntegrity);

// Remove all other types of damage that can't be healed right now,
// so we can prioritize the target part correctly.
foreach (var (damage, value) in possibleHealing.DamageDict)
{
if (!partHealing.DamageDict.ContainsKey(damage))
possibleHealing.DamageDict.Remove(damage);
}

if (possibleHealing.GetTotal() == FixedPoint2.Zero)
continue;

damagedParts.Add((part, possibleHealing.GetTotal()));
}

if (targetBodyPart != null)
_bodySystem.TryChangeIntegrity(mostDamaged, healing.Damage, false, targetBodyPart.Value, out _);
// Fetch the most damaged body part
var mostDamaged = damagedParts.MaxBy(x => x.Item2).Item1;
var targetPart = _bodySystem.GetTargetBodyPart(mostDamaged);
_bodySystem.TryChangeIntegrity(mostDamaged, healing.Damage, false, targetPart, out _);
}
// end-backmen: surgery

// Re-verify that we can heal the damage.

Expand Down
23 changes: 11 additions & 12 deletions Content.Shared/Backmen/Surgery/Body/SharedBodySystem.Integrity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public partial class SharedBodySystem
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly string[] _severingDamageTypes = { "Slash", "Pierce", "Blunt" };

private const double IntegrityJobTime = 0.005;
private readonly JobQueue _integrityJobQueue = new(IntegrityJobTime);

Expand Down Expand Up @@ -117,7 +117,7 @@ public override void Update(float frameTime)
/// <summary>
/// Propagates damage to the specified part of the entity.
/// </summary>
private void ApplyPartDamage(
public void ApplyPartDamage(
Entity<BodyPartComponent> partEnt,
DamageSpecifier damage,
BodyPartType targetType,
Expand All @@ -126,17 +126,15 @@ private void ApplyPartDamage(
bool evade,
float partMultiplier)
{
if (partEnt.Comp.Body is not {} body)
if (partEnt.Comp.Body == null)
return;

_proto.TryIndex<DamageGroupPrototype>("Brute", out var proto);

if (!TryEvadeDamage(partEnt.Comp.Body.Value, GetEvadeChance(targetType)) || evade)
{
TryChangeIntegrity(partEnt,
damage * partMultiplier,
damage * partMultiplier * GetPartDamageModifier(targetType),
// This is true when damage contains at least one of the brute damage types
canSever && damage.TryGetDamageInGroup(proto!, out var dmg) && dmg > FixedPoint2.Zero,
canSever,
targetPart,
out _);
}
Expand All @@ -162,10 +160,13 @@ public void TryChangeIntegrity(
partEnt.Comp.Damage.ExclusiveAdd(damage);
partEnt.Comp.Damage.ClampMin(partEnt.Comp.MinIntegrity); // No over-healing!

_proto.TryIndex<DamageGroupPrototype>("Brute", out var proto);

if (canSever
&& !HasComp<BodyPartReattachedComponent>(partEnt)
&& !partEnt.Comp.Enabled
&& integrity >= partEnt.Comp.SeverIntegrity
&& partEnt.Comp.Damage.TryGetDamageInGroup(proto!, out var dmg)
&& dmg > partEnt.Comp.SeverIntegrity
&& partIdSlot is not null)
severed = true;

Expand Down Expand Up @@ -330,8 +331,8 @@ public static float GetPartDamageModifier(BodyPartType partType)
{
BodyPartType.Head => 0.5f, // 50% damage, necks are hard to cut
BodyPartType.Torso => 1.0f, // 100% damage
BodyPartType.Arm => 0.7f, // 70% damage
BodyPartType.Leg => 0.7f, // 70% damage
BodyPartType.Arm => 0.8f, // 80% damage
BodyPartType.Leg => 0.8f, // 80% damage
_ => 0.5f
};
}
Expand Down Expand Up @@ -376,8 +377,6 @@ public static float GetEvadeChance(BodyPartType partType)
};
}



public bool CanEvadeDamage(Entity<MobStateComponent?> uid)
{
if (!Resolve(uid, ref uid.Comp))
Expand Down
12 changes: 6 additions & 6 deletions Content.Shared/Body/Part/BodyPartComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public sealed partial class BodyPartComponent : Component, ISurgeryToolComponent
/// to make possible severing it.
/// </summary>
[DataField, AutoNetworkedField]
public float SeverIntegrity = 70;
public float SeverIntegrity = 100;

/// <summary>
/// On what TargetIntegrity we should re-enable the part.
Expand All @@ -111,12 +111,12 @@ public sealed partial class BodyPartComponent : Component, ISurgeryToolComponent
[DataField, AutoNetworkedField]
public Dictionary<TargetIntegrity, float> IntegrityThresholds = new()
{
{ TargetIntegrity.CriticallyWounded, 70 },
{ TargetIntegrity.HeavilyWounded, 56 },
{ TargetIntegrity.ModeratelyWounded, 42 },
{ TargetIntegrity.SomewhatWounded, 28},
{ TargetIntegrity.CriticallyWounded, 80 },
{ TargetIntegrity.HeavilyWounded, 65 },
{ TargetIntegrity.ModeratelyWounded, 48 },
{ TargetIntegrity.SomewhatWounded, 32 },
{ TargetIntegrity.LightlyWounded, 17 },
{ TargetIntegrity.Healthy, 7 },
{ TargetIntegrity.Healthy, 6 },
};

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Content.Shared/Damage/Systems/DamageableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSp
targetPart = targeter.Target;
// ...Or do we?
// If the target is Torso then have a 33% chance to hit another part
if (targetPart.Value.HasFlag(TargetBodyPart.Torso))
if (targetPart.Value == TargetBodyPart.Torso)
targetPart = GetRandomPartSpread(_random, 10);
}
else
Expand Down
Loading