From 6b0677a86d8886c2170c19f5cb39eefb8dd401bb Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 5 Nov 2022 01:51:08 -0700 Subject: [PATCH 001/148] started work on wound prototypes --- Content.Shared/Content.Shared.csproj | 5 ++ .../Wounds/Components/WoundableComponent.cs | 10 +++ .../Wounds/Prototypes/WoundPrototype.cs | 15 +++++ .../Medical/Wounds/Systems/WoundSystem.cs | 67 +++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 Content.Shared/Medical/Wounds/Components/WoundableComponent.cs create mode 100644 Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs create mode 100644 Content.Shared/Medical/Wounds/Systems/WoundSystem.cs diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index b29b228b355bb5..4a7e144d576e27 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -30,6 +30,11 @@ + + + + + diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs new file mode 100644 index 00000000000000..a48d1b72cb8788 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -0,0 +1,10 @@ +using Content.Shared.Damage; +using Content.Shared.Medical.Wounds.Prototypes; + +namespace Content.Shared.Medical.Wounds.Components; + +[RegisterComponent] +public sealed class WoundableComponent : Component +{ + public Dictionary Wounds = new(); +} diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs new file mode 100644 index 00000000000000..9d108b4db13df6 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs @@ -0,0 +1,15 @@ +using Content.Shared.Damage; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +namespace Content.Shared.Medical.Wounds.Prototypes; + +[DataDefinition] +public sealed class WoundPrototype : IPrototype +{ + [IdDataField] public string ID { get; init; } = string.Empty; + + [DataField("damageToApply")] public DamageSpecifier DamageToApply { get; init; } = new(); +} + +[Serializable, NetSerializable, DataRecord] +public record struct WoundData (string WoundId, float Severity, float Healed, float Infected); diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs new file mode 100644 index 00000000000000..dc41b3d14d023f --- /dev/null +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -0,0 +1,67 @@ +using System.Linq; +using Content.Shared.Damage; +using Content.Shared.Medical.Wounds.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Medical.Wounds.Systems; + +public sealed class WoundSystem : EntitySystem +{ + [Dependency] private IPrototypeManager _prototypeManager = default!; + private Dictionary> _woundPrototypesIdCache = new(); + public override void Initialize() + { + CachePrototypes(null); + _prototypeManager.PrototypesReloaded += CachePrototypes; + } + private void CachePrototypes(PrototypesReloadedEventArgs? args) + { + _woundPrototypesIdCache.Clear(); //Prevent any hot-reload related fuckery + foreach (var woundProto in _prototypeManager.EnumeratePrototypes()) + { + var specifier = GetWoundPrototypeLookupId(woundProto.DamageToApply); + _woundPrototypesIdCache.TryAdd(specifier, new List()); + _woundPrototypesIdCache[specifier].Add(woundProto.ID); + } + } + private string GetWoundPrototypeLookupId(DamageSpecifier damageSpecifier) + { + var temp = ""; + foreach (var damageType in damageSpecifier.DamageDict.Keys) + { + temp += damageType; + } + return temp; + } + + public bool TryGetWoundPrototype(DamageSpecifier damage, out WoundPrototype? prototype) + { + + prototype = null; + var lookupId = GetWoundPrototypeLookupId(damage); + if (!_woundPrototypesIdCache.TryGetValue(lookupId, out var woundProtos)) + { + return false; + } + var valid = false; + foreach (var woundId in woundProtos) + { + valid = true; + var testProto = _prototypeManager.Index(woundId); + foreach (var protoDamageDef in testProto.DamageToApply.DamageDict) + { + valid = damage.DamageDict[protoDamageDef.Key] >= protoDamageDef.Value; + if (!valid) + break; + } + + if (valid) + prototype = testProto; + } + return valid; + } + public static class A + { + // IT LIVES ON! FOREVER IN OUR HEARTS! + } +} From 7f57303bb71809fd160b1640e62b83c8e698bd16 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 01:35:54 -0800 Subject: [PATCH 002/148] implemented wound application and stacking --- .../Wounds/Components/WoundableComponent.cs | 6 +- .../Wounds/Prototypes/WoundPrototype.cs | 2 +- .../Medical/Wounds/Systems/WoundSystem.cs | 108 +++++++++++++++++- Content.Shared/Medical/Wounds/WoundHandle.cs | 34 ++++++ 4 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/WoundHandle.cs diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index a48d1b72cb8788..59a9654d5a29b3 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -1,4 +1,6 @@ -using Content.Shared.Damage; +using System.Linq; +using System.Runtime.InteropServices; +using Content.Shared.Damage; using Content.Shared.Medical.Wounds.Prototypes; namespace Content.Shared.Medical.Wounds.Components; @@ -6,5 +8,5 @@ namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent] public sealed class WoundableComponent : Component { - public Dictionary Wounds = new(); + public Dictionary> Wounds = new(); } diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs index 9d108b4db13df6..216b175c97b076 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs @@ -12,4 +12,4 @@ public sealed class WoundPrototype : IPrototype } [Serializable, NetSerializable, DataRecord] -public record struct WoundData (string WoundId, float Severity, float Healed, float Infected); +public record struct WoundData (string WoundId, float Severity, float Tended, float Size, float Infected); diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index dc41b3d14d023f..07bc310fda3f2d 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,14 +1,24 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices; using Content.Shared.Damage; +using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Shared.Medical.Wounds.Systems; public sealed class WoundSystem : EntitySystem { [Dependency] private IPrototypeManager _prototypeManager = default!; + [Dependency] private RobustRandom _random = default!; private Dictionary> _woundPrototypesIdCache = new(); + //TODO: CVAR these + private int maxWoundOfTypeCount = 10; //maximum wounds of a single type allowed before the wounds always stack + private float woundStackChance = 0.5f; + private float severityModifier = 1.0f; + private float sizeModifier = 1.0f; public override void Initialize() { CachePrototypes(null); @@ -19,12 +29,12 @@ private void CachePrototypes(PrototypesReloadedEventArgs? args) _woundPrototypesIdCache.Clear(); //Prevent any hot-reload related fuckery foreach (var woundProto in _prototypeManager.EnumeratePrototypes()) { - var specifier = GetWoundPrototypeLookupId(woundProto.DamageToApply); + var specifier = GetDamageComboId(woundProto.DamageToApply); _woundPrototypesIdCache.TryAdd(specifier, new List()); _woundPrototypesIdCache[specifier].Add(woundProto.ID); } } - private string GetWoundPrototypeLookupId(DamageSpecifier damageSpecifier) + private string GetDamageComboId(DamageSpecifier damageSpecifier) { var temp = ""; foreach (var damageType in damageSpecifier.DamageDict.Keys) @@ -34,12 +44,98 @@ private string GetWoundPrototypeLookupId(DamageSpecifier damageSpecifier) return temp; } - public bool TryGetWoundPrototype(DamageSpecifier damage, out WoundPrototype? prototype) + public int WoundCount(EntityUid target, string woundProtoId) + { + return !EntityManager.TryGetComponent(target, out var woundContainer) ? 0 : WoundCount(woundContainer, woundProtoId); + } + + public int WoundCount(WoundableComponent woundContainer, string woundProtoId) + { + return woundContainer.Wounds.TryGetValue(woundProtoId, out var woundData) ? woundData.Count : 0; + } + + //Wound handles are only valid so long as the their their woundData list is not modified. + public WoundHandle CreateWoundHandle(EntityUid target, string woundProtoId, int woundIndex) + { + return !EntityManager.TryGetComponent(target, out var woundContainer) ? new WoundHandle() : new WoundHandle(woundContainer, woundProtoId, woundIndex); + } + + //Get a COPY of woundData from a WoundHandle + public WoundData GetWoundData(WoundHandle handle) + { + if (!handle.Valid) + throw new ArgumentException("Tried to get WoundData from a null handle"); + return handle.Parent.Wounds[handle.Prototype][handle.WoundIndex]; + } + //Gets a REF of woundData from a WoundHandle, this is only valid until the woundList of that woundtype is modified + public ref WoundData GetWoundDataRef(WoundHandle handle) { + if (!handle.Valid) + throw new ArgumentException("Tried to get WoundData from a null handle"); + return ref CollectionsMarshal.AsSpan(handle.Parent.Wounds[handle.Prototype])[handle.WoundIndex]; + } + private Span GetWoundDataListAsSpan(WoundableComponent woundable, string protoId) + { + if (!woundable.Wounds.ContainsKey(protoId)) + throw new ArgumentException("Cannot get WoundData, woundType not found in woundData"); + return CollectionsMarshal.AsSpan(woundable.Wounds[protoId]); + } + + private bool TryGetWoundDataListAsSpan(WoundableComponent woundable, string protoId, + out Span woundDataSpan) + { + woundDataSpan = null; + if (!woundable.Wounds.TryGetValue(protoId, out var woundDataList)) + return false; + woundDataSpan = CollectionsMarshal.AsSpan(woundDataList); + return true; + } + + private WoundHandle TryStackWound(WoundableComponent woundContainer, string woundProtoId, float size, float severity) + { + if (!TryGetWoundDataListAsSpan(woundContainer, woundProtoId, out var woundDataSpan)) + throw new ArgumentException("Invalid or non-initialized woundPrototypeId"); + var woundIndex = _random.Next(0, woundDataSpan.Length); + ref var baseWound = ref woundDataSpan[woundIndex]; + baseWound.Tended = 0; //reopen that wound :D + //always stack if we exceed the max number of wounds of this type + if (woundDataSpan.Length < maxWoundOfTypeCount && !_random.Prob(woundStackChance)) + return new WoundHandle(); //return an invalid handle + + var woundHandle = new WoundHandle(woundContainer, woundProtoId, woundIndex); + var newSeverity = Math.Clamp(severity * severityModifier, 0f, 1f); + var newSize = Math.Clamp(size * sizeModifier, 0f, 1f); + if (baseWound.Severity < newSeverity) + { + baseWound.Severity = newSeverity; + } + if (baseWound.Size < newSize) + { + baseWound.Size = newSize; + } + return woundHandle; + } + + private WoundHandle ApplyWound(WoundableComponent woundContainer, string woundProtoId, float size, float severity) + { + WoundHandle woundHandle; + if (woundContainer.Wounds.ContainsKey(woundProtoId)) + { + woundHandle = TryStackWound(woundContainer, woundProtoId, size, severity); + if (woundHandle.Valid) + return woundHandle; + } + woundContainer.Wounds[woundProtoId].Add(new WoundData(woundProtoId, severity, 0, size, 0)); + woundHandle = new WoundHandle(woundContainer, woundProtoId, woundContainer.Wounds[woundProtoId].Count-1); + return woundHandle; + } + + public bool TryGetWoundPrototype(DamageSpecifier damage, out WoundPrototype? prototype) + { prototype = null; - var lookupId = GetWoundPrototypeLookupId(damage); - if (!_woundPrototypesIdCache.TryGetValue(lookupId, out var woundProtos)) + var damageComboId = GetDamageComboId(damage); + if (!_woundPrototypesIdCache.TryGetValue(damageComboId, out var woundProtos)) { return false; } diff --git a/Content.Shared/Medical/Wounds/WoundHandle.cs b/Content.Shared/Medical/Wounds/WoundHandle.cs new file mode 100644 index 00000000000000..4977206d9b33e2 --- /dev/null +++ b/Content.Shared/Medical/Wounds/WoundHandle.cs @@ -0,0 +1,34 @@ +using System.Runtime.InteropServices; +using Content.Shared.Medical.Wounds.Components; +using Content.Shared.Medical.Wounds.Prototypes; +using Content.Shared.Medical.Wounds.Systems; + +namespace Content.Shared.Medical.Wounds; + +public readonly ref struct WoundHandle +{ + public readonly string Prototype = string.Empty; + public readonly int WoundIndex = -1; + private readonly WoundableComponent? parentContainer = null; + public WoundableComponent Parent + { + get + { + if (parentContainer == null) + throw new ArgumentException("Tried to get the parent of an invalid WoundId"); + return parentContainer; + } + } + + public bool Valid => parentContainer != null; + public WoundHandle(WoundableComponent woundable, string prototype, int woundIndex) + { + if (woundIndex < 0 || !woundable.Wounds.ContainsKey(Prototype) || + woundable.Wounds[Prototype].Count >= WoundIndex) + return; //Do not construct a valid woundId if any of the construction args are invalid + Prototype = prototype; + parentContainer = woundable; + WoundIndex = woundIndex; + + } +} From bb6d320b97ea9b8e365ef9e19012d324dd63284b Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 19:58:50 -0800 Subject: [PATCH 003/148] implemented SKIN and created Bones! --- Content.Shared/Body/Organ/OrganComponent.cs | 4 + .../Body/Part/BodyCoveringComponent.cs | 20 +++ .../Body/Part/BodyCoveringComponentState.cs | 20 +++ .../Body/Prototypes/BodyCoveringPrototype.cs | 27 +++ .../Body/Prototypes/BonePrototype.cs | 36 ++++ .../Body/Systems/SharedBodySystem.Parts.cs | 19 ++- .../Damage/Prototypes/DamageTypePrototype.cs | 23 +++ .../Wounds/Components/WoundableComponent.cs | 2 + .../Wounds/Prototypes/WoundGroupPrototype.cs | 33 ++++ .../Wounds/Prototypes/WoundPrototype.cs | 20 ++- .../Medical/Wounds/Systems/WoundSystem.cs | 156 ++---------------- Content.Shared/Medical/Wounds/WoundHandle.cs | 34 ---- Resources/Prototypes/Body/Organs/human.yml | 2 + 13 files changed, 217 insertions(+), 179 deletions(-) create mode 100644 Content.Shared/Body/Part/BodyCoveringComponent.cs create mode 100644 Content.Shared/Body/Part/BodyCoveringComponentState.cs create mode 100644 Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs create mode 100644 Content.Shared/Body/Prototypes/BonePrototype.cs create mode 100644 Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs delete mode 100644 Content.Shared/Medical/Wounds/WoundHandle.cs diff --git a/Content.Shared/Body/Organ/OrganComponent.cs b/Content.Shared/Body/Organ/OrganComponent.cs index a01c8d853765a8..1f87783ce2ce7d 100644 --- a/Content.Shared/Body/Organ/OrganComponent.cs +++ b/Content.Shared/Body/Organ/OrganComponent.cs @@ -11,6 +11,10 @@ public sealed class OrganComponent : Component [DataField("body")] public EntityUid? Body; + [ViewVariables] + [DataField("internal")] + public bool Internal = true; + [ViewVariables] [DataField("parent")] public OrganSlot? ParentSlot; diff --git a/Content.Shared/Body/Part/BodyCoveringComponent.cs b/Content.Shared/Body/Part/BodyCoveringComponent.cs new file mode 100644 index 00000000000000..01968f64ce6c2c --- /dev/null +++ b/Content.Shared/Body/Part/BodyCoveringComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.Body.Prototypes; +using Content.Shared.Body.Systems; + +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +namespace Content.Shared.Body.Part; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedBodySystem))] +public sealed class BodyCoveringComponent : Component +{ + [ViewVariables, DataField("primaryCoveringId", required:true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string PrimaryBodyCoveringId = string.Empty; + + [ViewVariables, DataField("secondaryCoveringId", required:false, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string SecondaryBodyCoveringId = string.Empty; + + [ViewVariables, DataField("secondaryCoveringPercentage")] + public float SecondaryCoveringPercentage = 0f; +} diff --git a/Content.Shared/Body/Part/BodyCoveringComponentState.cs b/Content.Shared/Body/Part/BodyCoveringComponentState.cs new file mode 100644 index 00000000000000..3a6a926b2bbe37 --- /dev/null +++ b/Content.Shared/Body/Part/BodyCoveringComponentState.cs @@ -0,0 +1,20 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Body.Part; + +[Serializable, NetSerializable] +public sealed class BodyCoveringComponentState : ComponentState +{ + public string PrimaryBodyCoveringId; + + public string SecondaryBodyCoveringId; + + public float SecondaryCoveringPercentage; + + public BodyCoveringComponentState(string primaryBodyCoveringId, string secondaryBodyCoveringId, float secondaryCoveringPercentage) + { + PrimaryBodyCoveringId = primaryBodyCoveringId; + SecondaryBodyCoveringId = secondaryBodyCoveringId; + SecondaryCoveringPercentage = secondaryCoveringPercentage; + } +} diff --git a/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs b/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs new file mode 100644 index 00000000000000..387a1fee5d6760 --- /dev/null +++ b/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs @@ -0,0 +1,27 @@ +using Robust.Shared.Prototypes; +using Content.Shared.Damage; +namespace Content.Shared.Body.Prototypes; + +[Prototype("bodyCovering")] +public sealed class BodyCoveringPrototype : IPrototype +{ + [IdDataField] public string ID { get; } = default!; + + private string _name = string.Empty; + + [DataField("name")] + public string Name + { + get => _name; + private set => _name = Loc.GetString(value); + } + + [DataField("description", required: false)] + public string Description = string.Empty; + + [DataField("damageResistance", required: false)] + public DamageSpecifier Resistance = new DamageSpecifier(); + + [DataField("hardened", required: false)] + public bool Hardened = false; +} diff --git a/Content.Shared/Body/Prototypes/BonePrototype.cs b/Content.Shared/Body/Prototypes/BonePrototype.cs new file mode 100644 index 00000000000000..8c01fa4cd28831 --- /dev/null +++ b/Content.Shared/Body/Prototypes/BonePrototype.cs @@ -0,0 +1,36 @@ +using Content.Shared.Damage; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Body.Prototypes; + +[Prototype("bone")] +public sealed class BonePrototype : IPrototype +{ + [IdDataField] public string ID { get; } = default!; + + private string _name = string.Empty; + + [DataField("name")] + public string Name + { + get => _name; + private set => _name = Loc.GetString(value); + } + + [DataField("description", required: false)] + public string Description = string.Empty; + + [DataField("damageResistance", required: false)] + public DamageSpecifier Resistance = new DamageSpecifier(); + + //support for exoskeletons because bugs and xenos are cool. Also crabs. Because everything returns to crab. + [DataField("internal", required: false)] + public bool Internal = true; + + [DataField("structural", required: false)] + public bool Structure = true; + + //this gets overidden if internal is false, because exoskeletons block access to organs + [DataField("blocksOrgans", required: false)] + public bool BlocksOrgans = false; +} diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index f92e3c1a5c8e39..b84cbdde350cad 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -20,6 +20,9 @@ private void InitializeParts() SubscribeLocalEvent(OnPartRemoved); SubscribeLocalEvent(OnPartGetState); SubscribeLocalEvent(OnPartHandleState); + //SKIIIIIIIINNNNN + SubscribeLocalEvent(OnCoveringGetState); + SubscribeLocalEvent(OnCoveringHandleState); } private void OnPartGetState(EntityUid uid, BodyPartComponent part, ref ComponentGetState args) @@ -34,7 +37,6 @@ private void OnPartGetState(EntityUid uid, BodyPartComponent part, ref Component part.Symmetry ); } - private void OnPartHandleState(EntityUid uid, BodyPartComponent part, ref ComponentHandleState args) { if (args.Current is not BodyPartComponentState state) @@ -63,6 +65,21 @@ private void OnPartRemoved(EntityUid uid, BodyPartComponent part, ComponentRemov } } + private void OnCoveringGetState(EntityUid uid, BodyCoveringComponent part, ref ComponentGetState args) + { + args.State = new BodyCoveringComponentState(part.PrimaryBodyCoveringId, part.SecondaryBodyCoveringId, + part.SecondaryCoveringPercentage); + } + + private void OnCoveringHandleState(EntityUid uid, BodyCoveringComponent part, ref ComponentHandleState args) + { + if (args.Current is not BodyCoveringComponentState state) + return; + part.PrimaryBodyCoveringId = state.SecondaryBodyCoveringId; + part.SecondaryBodyCoveringId = state.SecondaryBodyCoveringId; + part.SecondaryCoveringPercentage = state.SecondaryCoveringPercentage; + } + private BodyPartSlot? CreatePartSlot( string slotId, EntityUid parent, diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index dc8ceff4565078..5fddbda75edd53 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -1,5 +1,7 @@ +using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Damage.Prototypes { @@ -12,5 +14,26 @@ public sealed class DamageTypePrototype : IPrototype { [IdDataFieldAttribute] public string ID { get; } = default!; + + // --=== Wounding/Medical configuration ===--- + + //Note: these should be defined in order of severity! + //surface wounds are wounds on skin or exposed bodyparts + [DataField("surfaceWounds", required: false, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet SurfaceWounds { get; init; } = new(); + + //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton + [DataField("solidWounds", required: false, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet SolidWounds { get; init; } = new(); + + //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh + [DataField("internalWounds", required: false, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet InternalWounds { get; init; } = new(); + + //Modifier for adjusting how much penetration this damage type has to apply internal wounding + [DataField("penModifier", required: false)] + public float PenetrationModifier { get; init; } = 1.0f; + + } } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 59a9654d5a29b3..6e34ede9ac30af 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -2,11 +2,13 @@ using System.Runtime.InteropServices; using Content.Shared.Damage; using Content.Shared.Medical.Wounds.Prototypes; +using Content.Shared.Medical.Wounds.Systems; namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent] public sealed class WoundableComponent : Component { + [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] public Dictionary> Wounds = new(); } diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs new file mode 100644 index 00000000000000..00405f860eebd6 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs @@ -0,0 +1,33 @@ +using Content.Shared.Damage.Prototypes; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; + +namespace Content.Shared.Medical.Wounds.Prototypes; + +[DataDefinition] +public sealed class WoundGroupPrototype : IPrototype +{ + [IdDataField] public string ID { get; init; } = string.Empty; + + [DataField("damageType", required: true)] + public DamageTypePrototype DamageType { get; init; } = default!; + + [DataField("depthModifier", required: false)] + public float DepthModifier { get; init; } = 1.0f; + + //--- wound prototypes ordered in severity based on the initial --- + //surface wounds are wounds on skin or exposed bodyparts + [DataField("surfaceWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet SurfaceWoundProtos { get; init; } = new(); + + //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton + [DataField("solidWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet SolidWoundProtos { get; init; } = new(); + + //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh + [DataField("internalWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet InternalWoundProtos { get; init; } = new(); + + +} diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs index 216b175c97b076..9ed65fc8d60445 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Medical.Wounds.Prototypes; @@ -8,8 +9,19 @@ public sealed class WoundPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; - [DataField("damageToApply")] public DamageSpecifier DamageToApply { get; init; } = new(); -} + [DataField("allowStacking")] public bool AllowStacking { get; init; } = true; + + [DataField("name", required: true)] + public string DisplayName { get; init; } = string.Empty; + + [DataField("description", required: true)] + public string Description { get; init; } = string.Empty; + + [DataField("pain", required: true)] public float Pain { get; init; } -[Serializable, NetSerializable, DataRecord] -public record struct WoundData (string WoundId, float Severity, float Tended, float Size, float Infected); + [DataField("bleedRate", required: false)] public float Bleed { get; init; } +} +public static class A +{ + // IT LIVES ON! FOREVER IN OUR HEARTS! +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 07bc310fda3f2d..5a1e5836d6b9e4 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,11 +1,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; +using Content.Shared.Body.Organ; +using Content.Shared.Body.Part; using Content.Shared.Damage; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Serialization; namespace Content.Shared.Medical.Wounds.Systems; @@ -13,151 +16,24 @@ public sealed class WoundSystem : EntitySystem { [Dependency] private IPrototypeManager _prototypeManager = default!; [Dependency] private RobustRandom _random = default!; - private Dictionary> _woundPrototypesIdCache = new(); - //TODO: CVAR these - private int maxWoundOfTypeCount = 10; //maximum wounds of a single type allowed before the wounds always stack - private float woundStackChance = 0.5f; - private float severityModifier = 1.0f; - private float sizeModifier = 1.0f; - public override void Initialize() - { - CachePrototypes(null); - _prototypeManager.PrototypesReloaded += CachePrototypes; - } - private void CachePrototypes(PrototypesReloadedEventArgs? args) - { - _woundPrototypesIdCache.Clear(); //Prevent any hot-reload related fuckery - foreach (var woundProto in _prototypeManager.EnumeratePrototypes()) - { - var specifier = GetDamageComboId(woundProto.DamageToApply); - _woundPrototypesIdCache.TryAdd(specifier, new List()); - _woundPrototypesIdCache[specifier].Add(woundProto.ID); - } - } - private string GetDamageComboId(DamageSpecifier damageSpecifier) - { - var temp = ""; - foreach (var damageType in damageSpecifier.DamageDict.Keys) - { - temp += damageType; - } - return temp; - } - - public int WoundCount(EntityUid target, string woundProtoId) - { - return !EntityManager.TryGetComponent(target, out var woundContainer) ? 0 : WoundCount(woundContainer, woundProtoId); - } - public int WoundCount(WoundableComponent woundContainer, string woundProtoId) - { - return woundContainer.Wounds.TryGetValue(woundProtoId, out var woundData) ? woundData.Count : 0; - } - - //Wound handles are only valid so long as the their their woundData list is not modified. - public WoundHandle CreateWoundHandle(EntityUid target, string woundProtoId, int woundIndex) - { - return !EntityManager.TryGetComponent(target, out var woundContainer) ? new WoundHandle() : new WoundHandle(woundContainer, woundProtoId, woundIndex); - } - - //Get a COPY of woundData from a WoundHandle - public WoundData GetWoundData(WoundHandle handle) - { - if (!handle.Valid) - throw new ArgumentException("Tried to get WoundData from a null handle"); - return handle.Parent.Wounds[handle.Prototype][handle.WoundIndex]; - } - //Gets a REF of woundData from a WoundHandle, this is only valid until the woundList of that woundtype is modified - public ref WoundData GetWoundDataRef(WoundHandle handle) - { - if (!handle.Valid) - throw new ArgumentException("Tried to get WoundData from a null handle"); - return ref CollectionsMarshal.AsSpan(handle.Parent.Wounds[handle.Prototype])[handle.WoundIndex]; - } - - private Span GetWoundDataListAsSpan(WoundableComponent woundable, string protoId) - { - if (!woundable.Wounds.ContainsKey(protoId)) - throw new ArgumentException("Cannot get WoundData, woundType not found in woundData"); - return CollectionsMarshal.AsSpan(woundable.Wounds[protoId]); - } - - private bool TryGetWoundDataListAsSpan(WoundableComponent woundable, string protoId, - out Span woundDataSpan) + public override void Initialize() { - woundDataSpan = null; - if (!woundable.Wounds.TryGetValue(protoId, out var woundDataList)) - return false; - woundDataSpan = CollectionsMarshal.AsSpan(woundDataList); - return true; } - private WoundHandle TryStackWound(WoundableComponent woundContainer, string woundProtoId, float size, float severity) - { - if (!TryGetWoundDataListAsSpan(woundContainer, woundProtoId, out var woundDataSpan)) - throw new ArgumentException("Invalid or non-initialized woundPrototypeId"); - var woundIndex = _random.Next(0, woundDataSpan.Length); - ref var baseWound = ref woundDataSpan[woundIndex]; - baseWound.Tended = 0; //reopen that wound :D - //always stack if we exceed the max number of wounds of this type - if (woundDataSpan.Length < maxWoundOfTypeCount && !_random.Prob(woundStackChance)) - return new WoundHandle(); //return an invalid handle - - var woundHandle = new WoundHandle(woundContainer, woundProtoId, woundIndex); - var newSeverity = Math.Clamp(severity * severityModifier, 0f, 1f); - var newSize = Math.Clamp(size * sizeModifier, 0f, 1f); - if (baseWound.Severity < newSeverity) - { - baseWound.Severity = newSeverity; - } - if (baseWound.Size < newSize) - { - baseWound.Size = newSize; - } - return woundHandle; - } - private WoundHandle ApplyWound(WoundableComponent woundContainer, string woundProtoId, float size, float severity) - { - WoundHandle woundHandle; - if (woundContainer.Wounds.ContainsKey(woundProtoId)) - { - woundHandle = TryStackWound(woundContainer, woundProtoId, size, severity); - if (woundHandle.Valid) - return woundHandle; - } - woundContainer.Wounds[woundProtoId].Add(new WoundData(woundProtoId, severity, 0, size, 0)); - woundHandle = new WoundHandle(woundContainer, woundProtoId, woundContainer.Wounds[woundProtoId].Count-1); - return woundHandle; - } - public bool TryGetWoundPrototype(DamageSpecifier damage, out WoundPrototype? prototype) - { - prototype = null; - var damageComboId = GetDamageComboId(damage); - if (!_woundPrototypesIdCache.TryGetValue(damageComboId, out var woundProtos)) - { - return false; - } - var valid = false; - foreach (var woundId in woundProtos) - { - valid = true; - var testProto = _prototypeManager.Index(woundId); - foreach (var protoDamageDef in testProto.DamageToApply.DamageDict) - { - valid = damage.DamageDict[protoDamageDef.Key] >= protoDamageDef.Value; - if (!valid) - break; - } +} - if (valid) - prototype = testProto; - } - return valid; - } - public static class A - { - // IT LIVES ON! FOREVER IN OUR HEARTS! - } +[Serializable, NetSerializable] +[Flags] +public enum WoundDepth +{ + None = 0, + Surface = 1 <<1, + Internal = 1 << 2, + Solid = 1 << 3, } + +[Serializable, NetSerializable, DataRecord] +public record struct WoundData (string WoundId, float Severity, float Tended, float Size, float Infected); diff --git a/Content.Shared/Medical/Wounds/WoundHandle.cs b/Content.Shared/Medical/Wounds/WoundHandle.cs deleted file mode 100644 index 4977206d9b33e2..00000000000000 --- a/Content.Shared/Medical/Wounds/WoundHandle.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Runtime.InteropServices; -using Content.Shared.Medical.Wounds.Components; -using Content.Shared.Medical.Wounds.Prototypes; -using Content.Shared.Medical.Wounds.Systems; - -namespace Content.Shared.Medical.Wounds; - -public readonly ref struct WoundHandle -{ - public readonly string Prototype = string.Empty; - public readonly int WoundIndex = -1; - private readonly WoundableComponent? parentContainer = null; - public WoundableComponent Parent - { - get - { - if (parentContainer == null) - throw new ArgumentException("Tried to get the parent of an invalid WoundId"); - return parentContainer; - } - } - - public bool Valid => parentContainer != null; - public WoundHandle(WoundableComponent woundable, string prototype, int woundIndex) - { - if (woundIndex < 0 || !woundable.Wounds.ContainsKey(Prototype) || - woundable.Wounds[Prototype].Count >= WoundIndex) - return; //Do not construct a valid woundId if any of the construction args are invalid - Prototype = prototype; - parentContainer = woundable; - WoundIndex = woundIndex; - - } -} diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 6c10aa20026e43..57ce26d24beadb 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -39,6 +39,7 @@ id: OrganHumanEyes parent: BaseHumanOrgan name: eyes + internal: false description: "I see you!" components: - type: Sprite @@ -73,6 +74,7 @@ id: OrganHumanEars parent: BaseHumanOrgan name: ears + internal: false description: "There are three parts to the ear. Inner, middle and outer. Only one of these parts should normally be visible." components: - type: Sprite From d0957669b3c0f1464c383825afe78a258e6e511b Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 20:27:45 -0800 Subject: [PATCH 004/148] implemented bone typing --- Content.Shared/Body/Prototypes/BonePrototype.cs | 5 +++++ Content.Shared/Body/Prototypes/BoneTypePrototype.cs | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 Content.Shared/Body/Prototypes/BoneTypePrototype.cs diff --git a/Content.Shared/Body/Prototypes/BonePrototype.cs b/Content.Shared/Body/Prototypes/BonePrototype.cs index 8c01fa4cd28831..17065f632d9b9c 100644 --- a/Content.Shared/Body/Prototypes/BonePrototype.cs +++ b/Content.Shared/Body/Prototypes/BonePrototype.cs @@ -1,5 +1,7 @@ using Content.Shared.Damage; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Body.Prototypes; @@ -8,6 +10,9 @@ public sealed class BonePrototype : IPrototype { [IdDataField] public string ID { get; } = default!; + [DataField("boneType", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string BoneType = string.Empty; + private string _name = string.Empty; [DataField("name")] diff --git a/Content.Shared/Body/Prototypes/BoneTypePrototype.cs b/Content.Shared/Body/Prototypes/BoneTypePrototype.cs new file mode 100644 index 00000000000000..c0a870d73b5558 --- /dev/null +++ b/Content.Shared/Body/Prototypes/BoneTypePrototype.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Body.Prototypes; + +[Prototype("boneType")] +public sealed class BoneTypePrototype : IPrototype +{ + [IdDataField] public string ID { get; } = default!; +} From 3579d2612c2fa62f619f968a46e316309b9e5c11 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 20:56:29 -0800 Subject: [PATCH 005/148] fixing a stupid with damage specifier --- Content.Shared/Body/Organ/OrganComponent.cs | 1 + .../Body/Part/BodyCoveringComponent.cs | 5 +++- Content.Shared/Body/Part/BodyPartComponent.cs | 1 + .../Body/Prototypes/BodyCoveringPrototype.cs | 2 +- .../Body/Prototypes/BonePrototype.cs | 2 +- .../Wounds/Components/WoundableComponent.cs | 4 +++ .../Medical/Wounds/Systems/WoundSystem.cs | 29 +++++++++++++++++++ 7 files changed, 41 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Body/Organ/OrganComponent.cs b/Content.Shared/Body/Organ/OrganComponent.cs index 1f87783ce2ce7d..28d94a6e09f6ff 100644 --- a/Content.Shared/Body/Organ/OrganComponent.cs +++ b/Content.Shared/Body/Organ/OrganComponent.cs @@ -11,6 +11,7 @@ public sealed class OrganComponent : Component [DataField("body")] public EntityUid? Body; + //is this organ exposed? [ViewVariables] [DataField("internal")] public bool Internal = true; diff --git a/Content.Shared/Body/Part/BodyCoveringComponent.cs b/Content.Shared/Body/Part/BodyCoveringComponent.cs index 01968f64ce6c2c..bc69cb93d549c9 100644 --- a/Content.Shared/Body/Part/BodyCoveringComponent.cs +++ b/Content.Shared/Body/Part/BodyCoveringComponent.cs @@ -1,6 +1,6 @@ using Content.Shared.Body.Prototypes; using Content.Shared.Body.Systems; - +using Content.Shared.Damage; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Body.Part; @@ -17,4 +17,7 @@ public sealed class BodyCoveringComponent : Component [ViewVariables, DataField("secondaryCoveringPercentage")] public float SecondaryCoveringPercentage = 0f; + + [ViewVariables, DataField("damageResistance", required:false)] + public DamageModifierSet DamageResistance = new(); } diff --git a/Content.Shared/Body/Part/BodyPartComponent.cs b/Content.Shared/Body/Part/BodyPartComponent.cs index ed4fdb7b59c42c..58758bf75dc599 100644 --- a/Content.Shared/Body/Part/BodyPartComponent.cs +++ b/Content.Shared/Body/Part/BodyPartComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Body.Components; using Content.Shared.Body.Organ; using Content.Shared.Body.Systems; +using Content.Shared.Damage; using Robust.Shared.GameStates; namespace Content.Shared.Body.Part; diff --git a/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs b/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs index 387a1fee5d6760..25fa87c9063347 100644 --- a/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs +++ b/Content.Shared/Body/Prototypes/BodyCoveringPrototype.cs @@ -20,7 +20,7 @@ public string Name public string Description = string.Empty; [DataField("damageResistance", required: false)] - public DamageSpecifier Resistance = new DamageSpecifier(); + public DamageModifierSet Resistance = new(); [DataField("hardened", required: false)] public bool Hardened = false; diff --git a/Content.Shared/Body/Prototypes/BonePrototype.cs b/Content.Shared/Body/Prototypes/BonePrototype.cs index 17065f632d9b9c..71481cb338e81f 100644 --- a/Content.Shared/Body/Prototypes/BonePrototype.cs +++ b/Content.Shared/Body/Prototypes/BonePrototype.cs @@ -26,7 +26,7 @@ public string Name public string Description = string.Empty; [DataField("damageResistance", required: false)] - public DamageSpecifier Resistance = new DamageSpecifier(); + public DamageModifierSet Resistance = new(); //support for exoskeletons because bugs and xenos are cool. Also crabs. Because everything returns to crab. [DataField("internal", required: false)] diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 6e34ede9ac30af..7d30fd0131f21c 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -11,4 +11,8 @@ public sealed class WoundableComponent : Component { [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] public Dictionary> Wounds = new(); + + [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] + [ViewVariables, DataField("damageResistance", required:false)] + public DamageModifierSet DamageResistance = new(); } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 5a1e5836d6b9e4..3f6b5a9afab932 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -21,6 +21,35 @@ public override void Initialize() { } + //TODO: return an out woundhandle from this! + public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) + { + var success = false; + if (!EntityManager.TryGetComponent(target, out var woundContainer)) + return false; + if (EntityManager.TryGetComponent(target, out var bodyPart)) + { + success = TryApplyWoundsBodyPart(target, bodyPart, damage); + } + if (EntityManager.TryGetComponent(target, out var organ)) + { + success |= TryApplyWoundsOrgan(target, organ, damage); + } + return success; + } + private bool TryApplyWoundsBodyPart(EntityUid target, BodyPartComponent bodyPart, DamageSpecifier damage) + { + + + + + return true; + } + private bool TryApplyWoundsOrgan(EntityUid target, OrganComponent organ, DamageSpecifier damage) + { + + return true; + } } From 170ac18d521926048f6295cee85b91a1dd0e2f8f Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 22:35:03 -0800 Subject: [PATCH 006/148] fixing some serialization fuckups and implementing per part damage caps --- Content.Shared/Body/Organ/OrganComponent.cs | 4 ++ Content.Shared/Body/Part/BodyPartComponent.cs | 3 ++ .../Damage/Prototypes/DamageTypePrototype.cs | 26 ++++++++----- .../Wounds/Components/WoundableComponent.cs | 13 +++++++ .../Medical/Wounds/Systems/WoundSystem.cs | 39 +++++++++++++++---- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/Content.Shared/Body/Organ/OrganComponent.cs b/Content.Shared/Body/Organ/OrganComponent.cs index 28d94a6e09f6ff..d16af091c1aba1 100644 --- a/Content.Shared/Body/Organ/OrganComponent.cs +++ b/Content.Shared/Body/Organ/OrganComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Body.Systems; +using Content.Shared.Damage; using Robust.Shared.GameStates; namespace Content.Shared.Body.Organ; @@ -16,6 +17,9 @@ public sealed class OrganComponent : Component [DataField("internal")] public bool Internal = true; + [ViewVariables, DataField("damageResistance", required:false)] + public DamageModifierSet DamageResistance = new(); + [ViewVariables] [DataField("parent")] public OrganSlot? ParentSlot; diff --git a/Content.Shared/Body/Part/BodyPartComponent.cs b/Content.Shared/Body/Part/BodyPartComponent.cs index 58758bf75dc599..11fd2b8e155434 100644 --- a/Content.Shared/Body/Part/BodyPartComponent.cs +++ b/Content.Shared/Body/Part/BodyPartComponent.cs @@ -39,6 +39,9 @@ public sealed class BodyPartComponent : Component [DataField("vital")] public bool IsVital; + [ViewVariables, DataField("damageResistance", required:false)] + public DamageModifierSet DamageResistance = new(); + [ViewVariables] [DataField("symmetry")] public BodyPartSymmetry Symmetry = BodyPartSymmetry.None; diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index 5fddbda75edd53..f16c56b8b4edb6 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -12,28 +12,34 @@ namespace Content.Shared.Damage.Prototypes [Serializable, NetSerializable] public sealed class DamageTypePrototype : IPrototype { - [IdDataFieldAttribute] + [IdDataField] public string ID { get; } = default!; + //TODO: Net serialize this shit! + // --=== Wounding/Medical configuration ===--- //Note: these should be defined in order of severity! //surface wounds are wounds on skin or exposed bodyparts - [DataField("surfaceWounds", required: false, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SurfaceWounds { get; init; } = new(); + [DataField("surfaceWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet SurfaceWounds { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("solidWounds", required: false, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SolidWounds { get; init; } = new(); + [DataField("solidWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet SolidWounds { get; init; } = new(); //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh - [DataField("internalWounds", required: false, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet InternalWounds { get; init; } = new(); + [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet InternalWounds { get; init; } = new(); + + //used to calculate how much damage penetrates the skin + [DataField("skinPenMod")] public float SkinPenModifier = 1.0f; - //Modifier for adjusting how much penetration this damage type has to apply internal wounding - [DataField("penModifier", required: false)] - public float PenetrationModifier { get; init; } = 1.0f; + //used to calculate how much damage penetrates a bodypart/flesh + [DataField("fleshPenMod")] public float FleshPenModifier = 1.0f; + //used to calculate how much damage propogates through a bone if it is protecting organs + [DataField("bonePenMod")] public float BonePenModifier = 1.0f; } } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 7d30fd0131f21c..e0034caed7b0ec 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -1,8 +1,12 @@ using System.Linq; using System.Runtime.InteropServices; using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; using Content.Shared.Medical.Wounds.Prototypes; using Content.Shared.Medical.Wounds.Systems; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Medical.Wounds.Components; @@ -15,4 +19,13 @@ public sealed class WoundableComponent : Component [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] [ViewVariables, DataField("damageResistance", required:false)] public DamageModifierSet DamageResistance = new(); + + [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] + [ViewVariables, DataField("woundDamageCaps", required: false, customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary WoundDamageCaps = new(); } + + +[Serializable, NetSerializable, DataRecord] +public readonly record struct WoundDamageCap ([field:DataField("skinDamageCap")]float SkinDamageCap, + [field:DataField("fleshDamageCap")]float InternalDamageCap, [field:DataField("solidDamageCap")]float SolidDamageCap); diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 3f6b5a9afab932..40ec0614d4c23d 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using Content.Shared.Body.Organ; using Content.Shared.Body.Part; +using Content.Shared.Body.Prototypes; using Content.Shared.Damage; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; @@ -16,36 +17,60 @@ public sealed class WoundSystem : EntitySystem { [Dependency] private IPrototypeManager _prototypeManager = default!; [Dependency] private RobustRandom _random = default!; + //DamageTypeId + //private Dictionary> public override void Initialize() { } + + + + //TODO: return an out woundhandle from this! public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) { var success = false; if (!EntityManager.TryGetComponent(target, out var woundContainer)) return false; - if (EntityManager.TryGetComponent(target, out var bodyPart)) + + EntityManager.TryGetComponent(target, out var bodyPart); + //if (bodyPart!.) //TODO: Check if skeleton is external and apply damage to it first if that is the case + if (EntityManager.TryGetComponent(target, out var covering)) + { + var coveringResistance = _prototypeManager.Index(covering.PrimaryBodyCoveringId).Resistance; + DamageSpecifier.ApplyModifierSet(damage, coveringResistance); //apply covering resistances first! + //TODO: eventually take into account second skin covering for damage resistance + DamageSpecifier.ApplyModifierSet(damage, covering.DamageResistance); + success = TryApplyWoundsCovering(target, covering, damage, woundContainer); + } + if (bodyPart != null) { - success = TryApplyWoundsBodyPart(target, bodyPart, damage); + DamageSpecifier.ApplyModifierSet(damage, bodyPart.DamageResistance); + success |= TryApplyWoundsBodyPart(target, bodyPart, damage, woundContainer); } if (EntityManager.TryGetComponent(target, out var organ)) { - success |= TryApplyWoundsOrgan(target, organ, damage); + DamageSpecifier.ApplyModifierSet(damage, organ.DamageResistance); + success |= TryApplyWoundsOrgan(target, organ, damage, woundContainer); } return success; } - private bool TryApplyWoundsBodyPart(EntityUid target, BodyPartComponent bodyPart, DamageSpecifier damage) - { - + private bool TryApplyWoundsCovering(EntityUid target, BodyCoveringComponent covering, DamageSpecifier damage, + WoundableComponent woundContainer) + { + //TODO: get damage from cached protos + return true; + } + private bool TryApplyWoundsBodyPart(EntityUid target, BodyPartComponent bodyPart, DamageSpecifier damage, WoundableComponent woundContainer) + { return true; } - private bool TryApplyWoundsOrgan(EntityUid target, OrganComponent organ, DamageSpecifier damage) + private bool TryApplyWoundsOrgan(EntityUid target, OrganComponent organ, DamageSpecifier damage, WoundableComponent woundContainer) { return true; From bdf11c4fa427abf2bef2b77a4ae4337edd388e30 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 23:24:36 -0800 Subject: [PATCH 007/148] updating --- ...eringComponent.cs => BodySkinComponent.cs} | 4 +- .../Body/Systems/SharedBodySystem.Parts.cs | 8 +- .../Damage/Prototypes/DamageTypePrototype.cs | 6 +- .../Wounds/Components/WoundableComponent.cs | 19 ++--- .../Medical/Wounds/Systems/WoundSystem.cs | 73 +++++++++++++------ 5 files changed, 69 insertions(+), 41 deletions(-) rename Content.Shared/Body/Part/{BodyCoveringComponent.cs => BodySkinComponent.cs} (89%) diff --git a/Content.Shared/Body/Part/BodyCoveringComponent.cs b/Content.Shared/Body/Part/BodySkinComponent.cs similarity index 89% rename from Content.Shared/Body/Part/BodyCoveringComponent.cs rename to Content.Shared/Body/Part/BodySkinComponent.cs index bc69cb93d549c9..7e9ae5761c0480 100644 --- a/Content.Shared/Body/Part/BodyCoveringComponent.cs +++ b/Content.Shared/Body/Part/BodySkinComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Body.Part; [RegisterComponent, NetworkedComponent] [Access(typeof(SharedBodySystem))] -public sealed class BodyCoveringComponent : Component +public sealed class BodySkinComponent : Component { [ViewVariables, DataField("primaryCoveringId", required:true, customTypeSerializer: typeof(PrototypeIdSerializer))] public string PrimaryBodyCoveringId = string.Empty; @@ -19,5 +19,5 @@ public sealed class BodyCoveringComponent : Component public float SecondaryCoveringPercentage = 0f; [ViewVariables, DataField("damageResistance", required:false)] - public DamageModifierSet DamageResistance = new(); + public DamageModifierSet DamageModifier = new(); } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index b84cbdde350cad..c54882fde48206 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -21,8 +21,8 @@ private void InitializeParts() SubscribeLocalEvent(OnPartGetState); SubscribeLocalEvent(OnPartHandleState); //SKIIIIIIIINNNNN - SubscribeLocalEvent(OnCoveringGetState); - SubscribeLocalEvent(OnCoveringHandleState); + SubscribeLocalEvent(OnCoveringGetState); + SubscribeLocalEvent(OnCoveringHandleState); } private void OnPartGetState(EntityUid uid, BodyPartComponent part, ref ComponentGetState args) @@ -65,13 +65,13 @@ private void OnPartRemoved(EntityUid uid, BodyPartComponent part, ComponentRemov } } - private void OnCoveringGetState(EntityUid uid, BodyCoveringComponent part, ref ComponentGetState args) + private void OnCoveringGetState(EntityUid uid, BodySkinComponent part, ref ComponentGetState args) { args.State = new BodyCoveringComponentState(part.PrimaryBodyCoveringId, part.SecondaryBodyCoveringId, part.SecondaryCoveringPercentage); } - private void OnCoveringHandleState(EntityUid uid, BodyCoveringComponent part, ref ComponentHandleState args) + private void OnCoveringHandleState(EntityUid uid, BodySkinComponent part, ref ComponentHandleState args) { if (args.Current is not BodyCoveringComponentState state) return; diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index f16c56b8b4edb6..ed4c87eb7b6e00 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -32,13 +32,13 @@ public sealed class DamageTypePrototype : IPrototype [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet InternalWounds { get; init; } = new(); - //used to calculate how much damage penetrates the skin + //used to calculate how much this damage type propogates the skin [DataField("skinPenMod")] public float SkinPenModifier = 1.0f; - //used to calculate how much damage penetrates a bodypart/flesh + //used to calculate how much this damage type propogates a bodypart/flesh [DataField("fleshPenMod")] public float FleshPenModifier = 1.0f; - //used to calculate how much damage propogates through a bone if it is protecting organs + //used to calculate how much this damage type propogates through a bone if it is protecting organs [DataField("bonePenMod")] public float BonePenModifier = 1.0f; } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index e0034caed7b0ec..4e9557b0f351a6 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -7,6 +7,7 @@ using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared.Medical.Wounds.Components; @@ -14,18 +15,18 @@ namespace Content.Shared.Medical.Wounds.Components; public sealed class WoundableComponent : Component { [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - public Dictionary> Wounds = new(); + [DataField("WoundData", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary WoundData = new(); [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - [ViewVariables, DataField("damageResistance", required:false)] + [ViewVariables, DataField("damageResistance")] public DamageModifierSet DamageResistance = new(); - - [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] - [ViewVariables, DataField("woundDamageCaps", required: false, customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary WoundDamageCaps = new(); } - [Serializable, NetSerializable, DataRecord] -public readonly record struct WoundDamageCap ([field:DataField("skinDamageCap")]float SkinDamageCap, - [field:DataField("fleshDamageCap")]float InternalDamageCap, [field:DataField("solidDamageCap")]float SolidDamageCap); +public record struct WoundableData ( + [field:DataField("wounds", customTypeSerializer:typeof(PrototypeIdListSerializer))]List Wounds, + [field:DataField("skinDamageCap")]float SkinDamageCap, + [field:DataField("fleshDamageCap")]float InternalDamageCap, + [field:DataField("solidDamageCap")]float SolidDamageCap + ); diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 40ec0614d4c23d..4ef07c90f6b1e6 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -5,6 +5,8 @@ using Content.Shared.Body.Part; using Content.Shared.Body.Prototypes; using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; @@ -17,8 +19,9 @@ public sealed class WoundSystem : EntitySystem { [Dependency] private IPrototypeManager _prototypeManager = default!; [Dependency] private RobustRandom _random = default!; - //DamageTypeId - //private Dictionary> + //TODO: Make these CVARS! + private float _hardenedSkinWoundTypeChance = 0.75f; //Chance for hardened skin to recieve a solid instead of surface wound. + private float _hardenedSkinSeverityAdjustment = 0.2f; //Decrease in severity if hardened skin receives a surface wound public override void Initialize() { @@ -34,21 +37,18 @@ public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) var success = false; if (!EntityManager.TryGetComponent(target, out var woundContainer)) return false; - - EntityManager.TryGetComponent(target, out var bodyPart); - //if (bodyPart!.) //TODO: Check if skeleton is external and apply damage to it first if that is the case - if (EntityManager.TryGetComponent(target, out var covering)) + if (EntityManager.TryGetComponent(target, out var coveringComp)) { - var coveringResistance = _prototypeManager.Index(covering.PrimaryBodyCoveringId).Resistance; - DamageSpecifier.ApplyModifierSet(damage, coveringResistance); //apply covering resistances first! - //TODO: eventually take into account second skin covering for damage resistance - DamageSpecifier.ApplyModifierSet(damage, covering.DamageResistance); - success = TryApplyWoundsCovering(target, covering, damage, woundContainer); + var primaryCovering = _prototypeManager.Index(coveringComp.PrimaryBodyCoveringId); + damage = DamageSpecifier.ApplyModifierSet(damage, primaryCovering.Resistance); //apply skin resistances first! + //TODO: eventually take into account second skin skin for damage resistance + damage = DamageSpecifier.ApplyModifierSet(damage, coveringComp.DamageModifier); + success = TryApplyWoundsSkin(target, coveringComp, damage, woundContainer, primaryCovering.Hardened); } - if (bodyPart != null) + if ( EntityManager.TryGetComponent(target, out var bodyPart)) { - DamageSpecifier.ApplyModifierSet(damage, bodyPart.DamageResistance); - success |= TryApplyWoundsBodyPart(target, bodyPart, damage, woundContainer); + var newDamage = DamageSpecifier.ApplyModifierSet(damage, bodyPart.DamageResistance); + success |= TryApplyWoundsBodyPart(target, bodyPart, newDamage, woundContainer); } if (EntityManager.TryGetComponent(target, out var organ)) { @@ -57,11 +57,17 @@ public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) } return success; } - - private bool TryApplyWoundsCovering(EntityUid target, BodyCoveringComponent covering, DamageSpecifier damage, - WoundableComponent woundContainer) + private bool TryApplyWoundsSkin(EntityUid target, BodySkinComponent skin, DamageSpecifier damage, + WoundableComponent woundContainer, bool hardenedCovering) { - //TODO: get damage from cached protos + if (hardenedCovering) + { + + } + foreach (var DamageData in damage.GetDamagePerGroup()) + { + + } return true; } @@ -76,17 +82,38 @@ private bool TryApplyWoundsOrgan(EntityUid target, OrganComponent organ, DamageS return true; } - + private float CalculateWoundSeverity(WoundableComponent woundableContainer, string damageType, float damage, WoundType woundType) + { + var newDamage = new DamageSpecifier(_prototypeManager.Index(damageType), damage); + damage = DamageSpecifier.ApplyModifierSet(newDamage, woundableContainer.DamageResistance).DamageDict[damageType].Float(); + switch (woundType) + { + case WoundType.Skin: + { + return Math.Clamp(damage/woundableContainer.WoundData[damageType].SkinDamageCap, 0f, 1.0f) ; + } + case WoundType.Internal: + { + return Math.Clamp(damage/woundableContainer.WoundData[damageType].InternalDamageCap, 0f, 1.0f) ; + } + case WoundType.Solid: + { + return Math.Clamp(damage/woundableContainer.WoundData[damageType].SolidDamageCap, 0f, 1.0f) ; + } + default: + throw new ArgumentException("WoundType was None!"); + } + } } [Serializable, NetSerializable] [Flags] -public enum WoundDepth +public enum WoundType { None = 0, - Surface = 1 <<1, - Internal = 1 << 2, - Solid = 1 << 3, + Skin = 1, + Internal = 2, + Solid = 3 } [Serializable, NetSerializable, DataRecord] From 854b176cd365452142658fe14d60b4473f768cb4 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 12 Nov 2022 23:49:24 -0800 Subject: [PATCH 008/148] interm commit --- Content.Shared/Body/Organ/OrganComponent.cs | 7 +-- Content.Shared/Body/Part/BodyPartComponent.cs | 3 -- .../Damage/Prototypes/DamageTypePrototype.cs | 2 +- .../Wounds/Prototypes/WoundGroupPrototype.cs | 2 +- .../Medical/Wounds/Systems/WoundSystem.cs | 54 ++++++++++--------- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/Content.Shared/Body/Organ/OrganComponent.cs b/Content.Shared/Body/Organ/OrganComponent.cs index d16af091c1aba1..8a4e58a147c24e 100644 --- a/Content.Shared/Body/Organ/OrganComponent.cs +++ b/Content.Shared/Body/Organ/OrganComponent.cs @@ -14,11 +14,8 @@ public sealed class OrganComponent : Component //is this organ exposed? [ViewVariables] - [DataField("internal")] - public bool Internal = true; - - [ViewVariables, DataField("damageResistance", required:false)] - public DamageModifierSet DamageResistance = new(); + [DataField("exposed")] + public bool Exposed = true; [ViewVariables] [DataField("parent")] diff --git a/Content.Shared/Body/Part/BodyPartComponent.cs b/Content.Shared/Body/Part/BodyPartComponent.cs index 11fd2b8e155434..58758bf75dc599 100644 --- a/Content.Shared/Body/Part/BodyPartComponent.cs +++ b/Content.Shared/Body/Part/BodyPartComponent.cs @@ -39,9 +39,6 @@ public sealed class BodyPartComponent : Component [DataField("vital")] public bool IsVital; - [ViewVariables, DataField("damageResistance", required:false)] - public DamageModifierSet DamageResistance = new(); - [ViewVariables] [DataField("symmetry")] public BodyPartSymmetry Symmetry = BodyPartSymmetry.None; diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index ed4c87eb7b6e00..2f6f3fc0818c90 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -21,7 +21,7 @@ public sealed class DamageTypePrototype : IPrototype //Note: these should be defined in order of severity! //surface wounds are wounds on skin or exposed bodyparts - [DataField("surfaceWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + [DataField("SurfaceWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet SurfaceWounds { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs index 00405f860eebd6..3d51ff59d3ea86 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs @@ -18,7 +18,7 @@ public sealed class WoundGroupPrototype : IPrototype //--- wound prototypes ordered in severity based on the initial --- //surface wounds are wounds on skin or exposed bodyparts - [DataField("surfaceWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + [DataField("SurfaceWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet SurfaceWoundProtos { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 4ef07c90f6b1e6..50ca51a1941a68 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -43,30 +43,40 @@ public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) damage = DamageSpecifier.ApplyModifierSet(damage, primaryCovering.Resistance); //apply skin resistances first! //TODO: eventually take into account second skin skin for damage resistance damage = DamageSpecifier.ApplyModifierSet(damage, coveringComp.DamageModifier); - success = TryApplyWoundsSkin(target, coveringComp, damage, woundContainer, primaryCovering.Hardened); + foreach (var damageData in damage.DamageDict) + { + success = TryApplyWoundsSkin(target, coveringComp, damageData.Key, damageData.Value.Float(), woundContainer, primaryCovering.Hardened); + } } if ( EntityManager.TryGetComponent(target, out var bodyPart)) { - var newDamage = DamageSpecifier.ApplyModifierSet(damage, bodyPart.DamageResistance); + var newDamage = DamageSpecifier.ApplyModifierSet(damage, woundContainer.DamageResistance); success |= TryApplyWoundsBodyPart(target, bodyPart, newDamage, woundContainer); } if (EntityManager.TryGetComponent(target, out var organ)) { - DamageSpecifier.ApplyModifierSet(damage, organ.DamageResistance); + DamageSpecifier.ApplyModifierSet(damage, woundContainer.DamageResistance); success |= TryApplyWoundsOrgan(target, organ, damage, woundContainer); } return success; } - private bool TryApplyWoundsSkin(EntityUid target, BodySkinComponent skin, DamageSpecifier damage, - WoundableComponent woundContainer, bool hardenedCovering) + private bool TryApplyWoundsSkin(EntityUid target, BodySkinComponent skin, string damageType ,float damage, + WoundableComponent woundContainer, bool hardenedSkin) { - if (hardenedCovering) - { + if (!woundContainer.WoundData.TryGetValue(damageType, out var woundData)) + return false; - } - foreach (var DamageData in damage.GetDamagePerGroup()) + var woundSeverity = 0f; + if (hardenedSkin) { - + if (_random.Prob(_hardenedSkinWoundTypeChance)) + { + woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundType.Solid); + } + else + { + woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundType.Skin)*_hardenedSkinSeverityAdjustment; + } } return true; } @@ -86,23 +96,15 @@ private float CalculateWoundSeverity(WoundableComponent woundableContainer, stri { var newDamage = new DamageSpecifier(_prototypeManager.Index(damageType), damage); damage = DamageSpecifier.ApplyModifierSet(newDamage, woundableContainer.DamageResistance).DamageDict[damageType].Float(); - switch (woundType) + return woundType switch { - case WoundType.Skin: - { - return Math.Clamp(damage/woundableContainer.WoundData[damageType].SkinDamageCap, 0f, 1.0f) ; - } - case WoundType.Internal: - { - return Math.Clamp(damage/woundableContainer.WoundData[damageType].InternalDamageCap, 0f, 1.0f) ; - } - case WoundType.Solid: - { - return Math.Clamp(damage/woundableContainer.WoundData[damageType].SolidDamageCap, 0f, 1.0f) ; - } - default: - throw new ArgumentException("WoundType was None!"); - } + WoundType.Skin => Math.Clamp(damage / woundableContainer.WoundData[damageType].SkinDamageCap, 0f, 1.0f), + WoundType.Internal => Math.Clamp(damage / woundableContainer.WoundData[damageType].InternalDamageCap, 0f, + 1.0f), + WoundType.Solid => Math.Clamp(damage / woundableContainer.WoundData[damageType].SolidDamageCap, 0f, 1.0f), + WoundType.None => throw new ArgumentException("WoundType was None! This should never happen!"), + _ => throw new ArgumentException("WoundType was None! This should never happen!") + }; } } From f8cf84f0da6d4a51aa534558262b742fbd19e960 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sun, 13 Nov 2022 04:36:33 -0800 Subject: [PATCH 009/148] implemented wound table caching, updated wound tables to use severity --- .../Damage/Prototypes/DamageTypePrototype.cs | 18 ++--- .../Medical/Wounds/Systems/WoundSystem.cs | 69 ++++++++++++++----- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index bfea693f2534f9..b7a777da4dff80 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -1,6 +1,8 @@ +using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Damage.Prototypes @@ -27,22 +29,22 @@ public sealed class DamageTypePrototype : IPrototype [DataField("armorFlatPrice")] public double ArmorPriceFlat { get; set; } - //TODO: Net serialize this shit! - // --=== Wounding/Medical configuration ===--- + //TODO: update these to use the value protoId dictionary serializer + //Note: these should be defined in order of severity! //surface wounds are wounds on skin or exposed bodyparts - [DataField("SurfaceWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SurfaceWounds { get; init; } = new(); + [DataField("SurfaceWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary SurfaceWounds { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("solidWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SolidWounds { get; init; } = new(); + [DataField("solidWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary SolidWounds { get; init; } = new(); //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh - [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet InternalWounds { get; init; } = new(); + [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary InternalWounds { get; init; } = new(); //used to calculate how much this damage type propogates the skin [DataField("skinPenMod")] public float SkinPenModifier = 1.0f; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 50ca51a1941a68..7bd5cf7d7eca5f 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -23,15 +23,37 @@ public sealed class WoundSystem : EntitySystem private float _hardenedSkinWoundTypeChance = 0.75f; //Chance for hardened skin to recieve a solid instead of surface wound. private float _hardenedSkinSeverityAdjustment = 0.2f; //Decrease in severity if hardened skin receives a surface wound + private readonly Dictionary _cachedDamageWoundTables = new(); + public override void Initialize() { + CacheData(null); + _prototypeManager.PrototypesReloaded += CacheData; } + private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) + { + _cachedDamageWoundTables.Clear(); + foreach (var damageProto in _prototypeManager.EnumeratePrototypes()) + { + _cachedDamageWoundTables.Add(damageProto.ID, new CachedWoundTable(damageProto)); + } + } + public IReadOnlyDictionary? GetWoundTableForDamageType(string damageTypeId, WoundLayer woundLayer) + { + if (!_cachedDamageWoundTables.ContainsKey(damageTypeId)) + return null; + return woundLayer switch + { + WoundLayer.Surface => _cachedDamageWoundTables[damageTypeId].SurfaceWounds, + WoundLayer.Internal => _cachedDamageWoundTables[damageTypeId].InternalWounds, + WoundLayer.Solid => _cachedDamageWoundTables[damageTypeId].SolidWounds, + _ => throw new ArgumentException("WoundLayer was invalid! This should never happen!") + }; + } - - - //TODO: return an out woundhandle from this! + //TODO: output a woundhandle from this! public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) { var success = false; @@ -71,11 +93,11 @@ private bool TryApplyWoundsSkin(EntityUid target, BodySkinComponent skin, string { if (_random.Prob(_hardenedSkinWoundTypeChance)) { - woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundType.Solid); + woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundLayer.Solid); } else { - woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundType.Skin)*_hardenedSkinSeverityAdjustment; + woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundLayer.Surface)*_hardenedSkinSeverityAdjustment; } } return true; @@ -92,30 +114,43 @@ private bool TryApplyWoundsOrgan(EntityUid target, OrganComponent organ, DamageS return true; } - private float CalculateWoundSeverity(WoundableComponent woundableContainer, string damageType, float damage, WoundType woundType) + private float CalculateWoundSeverity(WoundableComponent woundableContainer, string damageType, float damage, WoundLayer woundLayer) { var newDamage = new DamageSpecifier(_prototypeManager.Index(damageType), damage); damage = DamageSpecifier.ApplyModifierSet(newDamage, woundableContainer.DamageResistance).DamageDict[damageType].Float(); - return woundType switch + return woundLayer switch { - WoundType.Skin => Math.Clamp(damage / woundableContainer.WoundData[damageType].SkinDamageCap, 0f, 1.0f), - WoundType.Internal => Math.Clamp(damage / woundableContainer.WoundData[damageType].InternalDamageCap, 0f, + WoundLayer.Surface => Math.Clamp(damage / woundableContainer.WoundData[damageType].SkinDamageCap, 0f, 1.0f), + WoundLayer.Internal => Math.Clamp(damage / woundableContainer.WoundData[damageType].InternalDamageCap, 0f, 1.0f), - WoundType.Solid => Math.Clamp(damage / woundableContainer.WoundData[damageType].SolidDamageCap, 0f, 1.0f), - WoundType.None => throw new ArgumentException("WoundType was None! This should never happen!"), - _ => throw new ArgumentException("WoundType was None! This should never happen!") + WoundLayer.Solid => Math.Clamp(damage / woundableContainer.WoundData[damageType].SolidDamageCap, 0f, 1.0f), + _ => throw new ArgumentException("WoundLayer was invalid! This should never happen!") }; } + + private readonly struct CachedWoundTable + { + //we do some defensive coding + public readonly IReadOnlyDictionary? SurfaceWounds; + public readonly IReadOnlyDictionary? InternalWounds; + public readonly IReadOnlyDictionary? SolidWounds; + + public CachedWoundTable(DamageTypePrototype damageTypeProto) + { + InternalWounds = damageTypeProto.InternalWounds; + SurfaceWounds = damageTypeProto.SurfaceWounds; + SolidWounds = damageTypeProto.SolidWounds; + } + } } [Serializable, NetSerializable] [Flags] -public enum WoundType +public enum WoundLayer { - None = 0, - Skin = 1, - Internal = 2, - Solid = 3 + Surface = 0, + Internal = 1, + Solid = 2 } [Serializable, NetSerializable, DataRecord] From c9831224bcda47563d8dc26342c9493777a9f264 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 14 Nov 2022 02:13:52 -0800 Subject: [PATCH 010/148] Implemented Wounding :D --- .../Body/Part/BodyCoveringComponentState.cs | 20 -- Content.Shared/Body/Part/BodyPartComponent.cs | 1 - Content.Shared/Body/Part/BodySkinComponent.cs | 34 +- .../Body/Part/BodySkinComponentState.cs | 14 + .../Body/Systems/SharedBodySystem.Parts.cs | 10 +- .../Damage/Prototypes/DamageTypePrototype.cs | 20 +- .../Wounds/Components/WoundableComponent.cs | 59 +++- .../Medical/Wounds/Systems/WoundSystem.cs | 309 +++++++++++++----- 8 files changed, 327 insertions(+), 140 deletions(-) delete mode 100644 Content.Shared/Body/Part/BodyCoveringComponentState.cs create mode 100644 Content.Shared/Body/Part/BodySkinComponentState.cs diff --git a/Content.Shared/Body/Part/BodyCoveringComponentState.cs b/Content.Shared/Body/Part/BodyCoveringComponentState.cs deleted file mode 100644 index 3a6a926b2bbe37..00000000000000 --- a/Content.Shared/Body/Part/BodyCoveringComponentState.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Body.Part; - -[Serializable, NetSerializable] -public sealed class BodyCoveringComponentState : ComponentState -{ - public string PrimaryBodyCoveringId; - - public string SecondaryBodyCoveringId; - - public float SecondaryCoveringPercentage; - - public BodyCoveringComponentState(string primaryBodyCoveringId, string secondaryBodyCoveringId, float secondaryCoveringPercentage) - { - PrimaryBodyCoveringId = primaryBodyCoveringId; - SecondaryBodyCoveringId = secondaryBodyCoveringId; - SecondaryCoveringPercentage = secondaryCoveringPercentage; - } -} diff --git a/Content.Shared/Body/Part/BodyPartComponent.cs b/Content.Shared/Body/Part/BodyPartComponent.cs index 58758bf75dc599..ed4fdb7b59c42c 100644 --- a/Content.Shared/Body/Part/BodyPartComponent.cs +++ b/Content.Shared/Body/Part/BodyPartComponent.cs @@ -1,7 +1,6 @@ using Content.Shared.Body.Components; using Content.Shared.Body.Organ; using Content.Shared.Body.Systems; -using Content.Shared.Damage; using Robust.Shared.GameStates; namespace Content.Shared.Body.Part; diff --git a/Content.Shared/Body/Part/BodySkinComponent.cs b/Content.Shared/Body/Part/BodySkinComponent.cs index 7e9ae5761c0480..9545880289c3a0 100644 --- a/Content.Shared/Body/Part/BodySkinComponent.cs +++ b/Content.Shared/Body/Part/BodySkinComponent.cs @@ -1,7 +1,10 @@ using Content.Shared.Body.Prototypes; using Content.Shared.Body.Systems; using Content.Shared.Damage; +using Content.Shared.Medical.Wounds; +using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Body.Part; @@ -9,15 +12,26 @@ namespace Content.Shared.Body.Part; [Access(typeof(SharedBodySystem))] public sealed class BodySkinComponent : Component { - [ViewVariables, DataField("primaryCoveringId", required:true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string PrimaryBodyCoveringId = string.Empty; + [Access(typeof(WoundSystem), typeof(SharedBodySystem), Other = AccessPermissions.Read)] + [DataField("skinLayers")] + public List SkinLayers = new(); + public SkinlayerData? PrimaryLayer + { + get + { + if (SkinLayers.Count > 0) + return SkinLayers[0]; + return null; + } + } - [ViewVariables, DataField("secondaryCoveringId", required:false, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string SecondaryBodyCoveringId = string.Empty; - - [ViewVariables, DataField("secondaryCoveringPercentage")] - public float SecondaryCoveringPercentage = 0f; - - [ViewVariables, DataField("damageResistance", required:false)] - public DamageModifierSet DamageModifier = new(); } + +[Serializable, NetSerializable, DataRecord] +public record struct SkinlayerData( + [field: DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer))] + string? ProtoType, + [field: DataField("name", required:true)] + string Name, + string Description, float Coverage = 1.0f, + DamageModifierSet? Resistance = null); diff --git a/Content.Shared/Body/Part/BodySkinComponentState.cs b/Content.Shared/Body/Part/BodySkinComponentState.cs new file mode 100644 index 00000000000000..bfc7ae758af323 --- /dev/null +++ b/Content.Shared/Body/Part/BodySkinComponentState.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Body.Part; + +[Serializable, NetSerializable] +public sealed class BodySkinComponentState : ComponentState +{ + public List SkinLayers; + + public BodySkinComponentState(BodySkinComponent part) + { + SkinLayers = part.SkinLayers; + } +} diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index c54882fde48206..85069572f52582 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -67,17 +67,15 @@ private void OnPartRemoved(EntityUid uid, BodyPartComponent part, ComponentRemov private void OnCoveringGetState(EntityUid uid, BodySkinComponent part, ref ComponentGetState args) { - args.State = new BodyCoveringComponentState(part.PrimaryBodyCoveringId, part.SecondaryBodyCoveringId, - part.SecondaryCoveringPercentage); + args.State = new BodySkinComponentState(part); } private void OnCoveringHandleState(EntityUid uid, BodySkinComponent part, ref ComponentHandleState args) { - if (args.Current is not BodyCoveringComponentState state) + if (args.Current is not BodySkinComponentState state) return; - part.PrimaryBodyCoveringId = state.SecondaryBodyCoveringId; - part.SecondaryBodyCoveringId = state.SecondaryBodyCoveringId; - part.SecondaryCoveringPercentage = state.SecondaryCoveringPercentage; + part.SkinLayers.Clear(); + part.SkinLayers.AddRange(state.SkinLayers); } private BodyPartSlot? CreatePartSlot( diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index b7a777da4dff80..6c72f9e76d8d80 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -35,25 +35,25 @@ public sealed class DamageTypePrototype : IPrototype //Note: these should be defined in order of severity! //surface wounds are wounds on skin or exposed bodyparts - [DataField("SurfaceWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary SurfaceWounds { get; init; } = new(); - - //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("solidWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary SolidWounds { get; init; } = new(); + [DataField("surfaceWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public SortedDictionary SurfaceWounds { get; init; } = new(); //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary InternalWounds { get; init; } = new(); + public SortedDictionary? InternalWounds { get; init; } = new(); + + //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton + [DataField("structuralWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public SortedDictionary? StructuralWounds { get; init; } = new(); //used to calculate how much this damage type propogates the skin - [DataField("skinPenMod")] public float SkinPenModifier = 1.0f; + [DataField("surfacePenMod")] public float SurfacePenModifier = 1.0f; //used to calculate how much this damage type propogates a bodypart/flesh - [DataField("fleshPenMod")] public float FleshPenModifier = 1.0f; + [DataField("internalPenMod")] public float InternalPenModifier = 1.0f; //used to calculate how much this damage type propogates through a bone if it is protecting organs - [DataField("bonePenMod")] public float BonePenModifier = 1.0f; + [DataField("structurePenMod")] public float StructurePenModifier = 1.0f; } } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 4e9557b0f351a6..129b5fdcdcffc1 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -1,32 +1,59 @@ -using System.Linq; -using System.Runtime.InteropServices; -using Content.Shared.Damage; +using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Medical.Wounds.Prototypes; using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent] public sealed class WoundableComponent : Component { - [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - [DataField("WoundData", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary WoundData = new(); + [DataField("surfaceWoundInfo", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary SurfaceDamageCap = new(); + + [DataField("internalWoundInfo", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary InternalDamageCap = new(); + + [DataField("structuralWoundInfo", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary StructuralDamageCap = new(); [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] [ViewVariables, DataField("damageResistance")] - public DamageModifierSet DamageResistance = new(); + public DamageModifierSet? DamageResistance = new(); + + [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] + [DataField("surfaceWounds")] + public List? SurfaceWounds; + + [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] + [DataField("internalWounds")] + public List? InternalWounds; + + [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] + [DataField("structuralWounds")] + public List? StructuralWounds; + + [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] + [DataField("forcedSurfaceWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet? ForcedSurfaceWounds; //List of wounds that can be applied regardless of damage type + + [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] + [DataField("forcedInternalWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet? ForcedInternalWounds; //List of wounds that can be applied regardless of damage type + + [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] + [DataField("forceStructuralWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet? ForcedStructuralWounds; //List of wounds that can be applied regardless of damage type } -[Serializable, NetSerializable, DataRecord] -public record struct WoundableData ( - [field:DataField("wounds", customTypeSerializer:typeof(PrototypeIdListSerializer))]List Wounds, - [field:DataField("skinDamageCap")]float SkinDamageCap, - [field:DataField("fleshDamageCap")]float InternalDamageCap, - [field:DataField("solidDamageCap")]float SolidDamageCap - ); +//if a structural wound reaches level 1 and severity 1 the part/skin/bone WILL be lost! +[Serializable, NetSerializable] +public enum WoundCategory +{ + Surface = 1 << 0, + Internal = 1 << 1, + Structural = 1 << 2 +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 7bd5cf7d7eca5f..e9b94dc241b20f 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,29 +1,22 @@ using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.InteropServices; using Content.Shared.Body.Organ; using Content.Shared.Body.Part; -using Content.Shared.Body.Prototypes; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; -using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; - +using Robust.Shared.Utility; namespace Content.Shared.Medical.Wounds.Systems; public sealed class WoundSystem : EntitySystem { [Dependency] private IPrototypeManager _prototypeManager = default!; [Dependency] private RobustRandom _random = default!; - //TODO: Make these CVARS! - private float _hardenedSkinWoundTypeChance = 0.75f; //Chance for hardened skin to recieve a solid instead of surface wound. - private float _hardenedSkinSeverityAdjustment = 0.2f; //Decrease in severity if hardened skin receives a surface wound - private readonly Dictionary _cachedDamageWoundTables = new(); + private readonly Dictionary _cachedDamageWoundMetaData = new(); public override void Initialize() { @@ -33,125 +26,287 @@ public override void Initialize() private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) { - _cachedDamageWoundTables.Clear(); + _cachedDamageWoundMetaData.Clear(); foreach (var damageProto in _prototypeManager.EnumeratePrototypes()) { - _cachedDamageWoundTables.Add(damageProto.ID, new CachedWoundTable(damageProto)); + _cachedDamageWoundMetaData.Add(damageProto.ID, new WoundMetaData(damageProto)); } } - public IReadOnlyDictionary? GetWoundTableForDamageType(string damageTypeId, WoundLayer woundLayer) + private SortedDictionary? GetWoundTableForDamageType(string damageTypeId, WoundCategory woundCategory) { - if (!_cachedDamageWoundTables.ContainsKey(damageTypeId)) + if (!_cachedDamageWoundMetaData.ContainsKey(damageTypeId)) return null; - return woundLayer switch + return woundCategory switch { - WoundLayer.Surface => _cachedDamageWoundTables[damageTypeId].SurfaceWounds, - WoundLayer.Internal => _cachedDamageWoundTables[damageTypeId].InternalWounds, - WoundLayer.Solid => _cachedDamageWoundTables[damageTypeId].SolidWounds, - _ => throw new ArgumentException("WoundLayer was invalid! This should never happen!") + WoundCategory.Surface => _cachedDamageWoundMetaData[damageTypeId].SurfaceWounds, + WoundCategory.Internal => _cachedDamageWoundMetaData[damageTypeId].InternalWounds, + WoundCategory.Structural => _cachedDamageWoundMetaData[damageTypeId].StructuralWounds, + _ => throw new ArgumentException("WoundLayers was invalid! This should never happen!") }; } - //TODO: output a woundhandle from this! + public bool TryGetWoundPen(string damageType, WoundCategory category, out float? woundPen) + { + woundPen = null; + if (!_cachedDamageWoundMetaData.TryGetValue(damageType, out var woundMetaData)) + return false; + switch (category) + { + case WoundCategory.Surface: + { + woundPen = woundMetaData.SurfacePenModifier; + return true; + + } + case WoundCategory.Internal: + { + woundPen = woundMetaData.InternalPenModifier; + return true; + + } + case WoundCategory.Structural: + { + woundPen = woundMetaData.StructuralPenModifier; + return true; + } + default: + throw new ArgumentException("Wound category is invalid! This should never happen!"); + } + } + + public WoundData? CreateWound(string damageType, WoundCategory woundCategory, float level) + { + if (level < 0) //if level is invalid return null + return null; + var woundTable = GetWoundTableForDamageType(damageType, woundCategory); + if (woundTable == null) + return null; + //wound severity is the percentage to reach the next wound level, it describes how severe this particular wound is + var woundId = PickWound(level, woundTable, out var severity); + return new WoundData(woundId, severity, woundCategory, damageType); + } + //TODO: output a WoundHandle + public bool ForceApplyWound(EntityUid target, string woundId, WoundCategory category, float severity) + { + if (!EntityManager.TryGetComponent(target, out var woundContainer)) + return false; + if (!ContainsForcableWound(woundContainer, woundId)) + return false; + AddWound(woundContainer, category, new WoundData(woundId, severity, category)); + return true; + } + + + //TODO: output a WoundHandle + public bool ApplyWound(EntityUid target, WoundData woundData, WoundCategory woundCategory) + { + if (!EntityManager.TryGetComponent(target, out var woundContainer)) + return false; + //TODO: implement stacking/severity increases later! + AddWound(woundContainer, woundCategory, woundData); + return true; + } + public bool ContainsForcableWound(WoundableComponent woundableComponent, string woundId) + {//Here be dragons + return //fuck if statements, we use boolean ors in this neighborhood + (woundableComponent.ForcedSurfaceWounds != null && woundableComponent.ForcedSurfaceWounds.Contains(woundId)) + || + (woundableComponent.ForcedInternalWounds != null && woundableComponent.ForcedInternalWounds.Contains(woundId)) + || + (woundableComponent.ForcedStructuralWounds != null && woundableComponent.ForcedStructuralWounds.Contains(woundId)); + } + + public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, float level, out WoundData? newWound) + { + newWound = CreateWound(damageType, category, level); + if (!newWound.HasValue) + return false; + AddWound(woundComponent, category, newWound.Value); + return true; + + } + + public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, float level) + { + return CreateAndApplyWound(woundComponent, damageType, category, level, out var _); + } + + public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float level, out WoundData? newWound) + { + newWound = CreateWound(damageType, category, level); + return newWound.HasValue && ApplyWound(target, newWound.Value, category); + } + + public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float severity) + { + return CreateAndApplyWound(target, damageType, category, severity,out _); + } + + public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) + { + var success = false; + foreach (var damageData in damage.DamageDict) + { + if (damageData.Value > 0) + success |= TryApplyWounds(target, damageData.Key, damageData.Value); + } + return success; + } + + //try to apply wounds to a part for specific damage + public bool TryApplyWounds(EntityUid target, string damageType, FixedPoint2 damage) { var success = false; if (!EntityManager.TryGetComponent(target, out var woundContainer)) return false; - if (EntityManager.TryGetComponent(target, out var coveringComp)) + if (!_prototypeManager.TryIndex(damageType, out var damageTypeProto)) + return false; + var damageSpec = new DamageSpecifier(damageTypeProto, damage); + if (woundContainer.DamageResistance != null) + damageSpec = DamageSpecifier.ApplyModifierSet(damageSpec, woundContainer.DamageResistance); + if (damageSpec.DamageDict[damageType] == 0) + return false; + //TODO: implement custom wounds defined by skins + //if (EntityManager.TryGetComponent(target, out var coveringComp)) + if (woundContainer.SurfaceWounds != null) { - var primaryCovering = _prototypeManager.Index(coveringComp.PrimaryBodyCoveringId); - damage = DamageSpecifier.ApplyModifierSet(damage, primaryCovering.Resistance); //apply skin resistances first! - //TODO: eventually take into account second skin skin for damage resistance - damage = DamageSpecifier.ApplyModifierSet(damage, coveringComp.DamageModifier); - foreach (var damageData in damage.DamageDict) - { - success = TryApplyWoundsSkin(target, coveringComp, damageData.Key, damageData.Value.Float(), woundContainer, primaryCovering.Hardened); - } + damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Surface, damageType, damage.Float(), out _); } - if ( EntityManager.TryGetComponent(target, out var bodyPart)) + if (woundContainer.InternalWounds != null && damageSpec.DamageDict[damageType] > 0) { - var newDamage = DamageSpecifier.ApplyModifierSet(damage, woundContainer.DamageResistance); - success |= TryApplyWoundsBodyPart(target, bodyPart, newDamage, woundContainer); + damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Internal, damageType, damage.Float(), out _); } - if (EntityManager.TryGetComponent(target, out var organ)) + if (woundContainer.StructuralWounds == null || damageSpec.DamageDict[damageType] <= 0) + return success; + damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Structural, damageType, damage.Float(),out var woundLevel); + if (woundLevel >= 1.0f) { - DamageSpecifier.ApplyModifierSet(damage, woundContainer.DamageResistance); - success |= TryApplyWoundsOrgan(target, organ, damage, woundContainer); + //TODO: implement structural wound destruction + //Destroy this if it's a bodypart! } return success; } - private bool TryApplyWoundsSkin(EntityUid target, BodySkinComponent skin, string damageType ,float damage, - WoundableComponent woundContainer, bool hardenedSkin) + //Apply a layering wound and return and penetrating damage + private float ApplyLayeringWound( WoundableComponent woundContainer, WoundCategory category, string damageType, float damage, out float woundLevel) { - if (!woundContainer.WoundData.TryGetValue(damageType, out var woundData)) - return false; + woundLevel = CalculateWoundLevel(woundContainer, WoundCategory.Surface, damageType, damage, out var penDamage); + if (penDamage != 0 && TryGetWoundPen(damageType, WoundCategory.Surface, out var penMultiplier)) + penDamage *= penMultiplier!.Value; //apply penMultiplier + CreateAndApplyWound(woundContainer, damageType, WoundCategory.Surface, woundLevel); + if (penDamage < 0)//this should never be the case but it's good to double check incase someone does a stupid + penDamage = 0; + return penDamage; + } - var woundSeverity = 0f; - if (hardenedSkin) + private void AddWound(WoundableComponent woundContainer, WoundCategory category, WoundData wound) + { + switch (category) { - if (_random.Prob(_hardenedSkinWoundTypeChance)) - { - woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundLayer.Solid); - } - else + case WoundCategory.Surface: + woundContainer.SurfaceWounds?.Add(wound); + return; + case WoundCategory.Internal: + woundContainer.InternalWounds?.Add(wound); + return; + case WoundCategory.Structural: + woundContainer.StructuralWounds?.Add(wound); + return; + default: + throw new ArgumentException("Wound category is invalid! This should never happen!"); + } + } + + private string PickWound(float level, SortedDictionary woundTable, out float severity) + { + var nextLevel = 1f; + var levelFloor = 0f; + var woundId = string.Empty; + foreach (var woundLevel in woundTable) + { + if (woundLevel.Key > level) { - woundSeverity = CalculateWoundSeverity(woundContainer, damageType, damage, WoundLayer.Surface)*_hardenedSkinSeverityAdjustment; + nextLevel = woundLevel.Key.Float(); + break; } + woundId = woundLevel.Value; + levelFloor = woundLevel.Key.Float(); } - return true; + severity = (level - levelFloor) / (nextLevel - level); + return woundId; } - private bool TryApplyWoundsBodyPart(EntityUid target, BodyPartComponent bodyPart, DamageSpecifier damage, WoundableComponent woundContainer) + private float GetDamageCapForDamageType(WoundableComponent woundableComponent, WoundCategory category, string damageType) { - - return true; + switch (category) + { + case WoundCategory.Surface: + if (woundableComponent.SurfaceDamageCap.TryGetValue(damageType, out var cap1)) + return cap1; + return -1; + case WoundCategory.Internal: + if (woundableComponent.InternalDamageCap.TryGetValue(damageType, out var cap2)) + return cap2; + return -1; + case WoundCategory.Structural: + if (woundableComponent.StructuralDamageCap.TryGetValue(damageType, out var cap3)) + return cap3; + return -1; + default: + throw new ArgumentException("Wound category is invalid! This should never happen!"); + } } - private bool TryApplyWoundsOrgan(EntityUid target, OrganComponent organ, DamageSpecifier damage, WoundableComponent woundContainer) - { - return true; + //an implementation of calculate wound level that outputs excess damage + public float CalculateWoundLevel(WoundableComponent woundableComponent,WoundCategory category ,string damageType, FixedPoint2 damage, out float overDamage) + { + var damageCap = GetDamageCapForDamageType(woundableComponent, category, damageType); + overDamage = 0.0f; + var level =damage.Float() / damageCap; + if (!(level > 1.0f)) + return level; + level = 1.0f; + overDamage = damage.Float() - damageCap; + return level; } - - private float CalculateWoundSeverity(WoundableComponent woundableContainer, string damageType, float damage, WoundLayer woundLayer) + //a simpler implementation of calculating wound levels without excess damage output + public float CalculateWoundLevel(WoundableComponent woundableContainer, WoundCategory category, string damageType, FixedPoint2 damage) { - var newDamage = new DamageSpecifier(_prototypeManager.Index(damageType), damage); - damage = DamageSpecifier.ApplyModifierSet(newDamage, woundableContainer.DamageResistance).DamageDict[damageType].Float(); - return woundLayer switch + return category switch { - WoundLayer.Surface => Math.Clamp(damage / woundableContainer.WoundData[damageType].SkinDamageCap, 0f, 1.0f), - WoundLayer.Internal => Math.Clamp(damage / woundableContainer.WoundData[damageType].InternalDamageCap, 0f, + WoundCategory.Surface => Math.Clamp(damage.Float() / woundableContainer.SurfaceDamageCap[damageType], 0f, 1.0f), + WoundCategory.Internal => Math.Clamp(damage.Float() / woundableContainer.InternalDamageCap[damageType], 0f, 1.0f), - WoundLayer.Solid => Math.Clamp(damage / woundableContainer.WoundData[damageType].SolidDamageCap, 0f, 1.0f), - _ => throw new ArgumentException("WoundLayer was invalid! This should never happen!") + WoundCategory.Structural => Math.Clamp(damage.Float() / woundableContainer.StructuralDamageCap[damageType], 0f, 1.0f), + _ => throw new ArgumentException("WoundLayers was invalid! This should never happen!") }; } - private readonly struct CachedWoundTable + private readonly struct WoundMetaData { //we do some defensive coding - public readonly IReadOnlyDictionary? SurfaceWounds; - public readonly IReadOnlyDictionary? InternalWounds; - public readonly IReadOnlyDictionary? SolidWounds; + public readonly SortedDictionary? SurfaceWounds; + public readonly SortedDictionary? InternalWounds; + public readonly SortedDictionary? StructuralWounds; + public readonly float SurfacePenModifier; + public readonly float InternalPenModifier; + public readonly float StructuralPenModifier; - public CachedWoundTable(DamageTypePrototype damageTypeProto) + public WoundMetaData(DamageTypePrototype damageTypeProto) { InternalWounds = damageTypeProto.InternalWounds; SurfaceWounds = damageTypeProto.SurfaceWounds; - SolidWounds = damageTypeProto.SolidWounds; + StructuralWounds = damageTypeProto.StructuralWounds; + SurfacePenModifier = damageTypeProto.SurfacePenModifier; + InternalPenModifier = damageTypeProto.InternalPenModifier; + StructuralPenModifier = damageTypeProto.StructurePenModifier; } } } -[Serializable, NetSerializable] -[Flags] -public enum WoundLayer -{ - Surface = 0, - Internal = 1, - Solid = 2 -} [Serializable, NetSerializable, DataRecord] -public record struct WoundData (string WoundId, float Severity, float Tended, float Size, float Infected); +public record struct WoundData(string Id, float Severity,WoundCategory WoundCategory, string? DamageType = null, float Tended = 0f, float Infected = 0f) +{ +}; From 31e290cbfecd9a080dbb5e5c9f767dcd7efeab2c Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 14 Nov 2022 02:22:43 -0800 Subject: [PATCH 011/148] added listener for damage --- Content.Shared/Medical/Wounds/Systems/WoundSystem.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index e9b94dc241b20f..c7f870c183858e 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -22,6 +22,13 @@ public override void Initialize() { CacheData(null); _prototypeManager.PrototypesReloaded += CacheData; + SubscribeLocalEvent(OnDamageReceived); + } + + private void OnDamageReceived(EntityUid uid, DamageableComponent component, DamageChangedEvent args) + { + if (args.DamageDelta != null) + TryApplyWounds(uid, args.DamageDelta); } private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) From 8d19e70cdb20fc57d0ec06a8cc881a0d0d62d51c Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 14 Nov 2022 12:49:19 -0800 Subject: [PATCH 012/148] Interm test --- .../Damage/Prototypes/DamageTypePrototype.cs | 19 +++++++++--------- .../Medical/Wounds/Systems/WoundSystem.cs | 20 ++++++++----------- RobustToolbox | 2 +- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index 6c72f9e76d8d80..a00ae4b9b4b101 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -3,7 +3,6 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Damage.Prototypes { @@ -35,24 +34,24 @@ public sealed class DamageTypePrototype : IPrototype //Note: these should be defined in order of severity! //surface wounds are wounds on skin or exposed bodyparts - [DataField("surfaceWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public SortedDictionary SurfaceWounds { get; init; } = new(); + [DataField("surfaceWounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary SurfaceWounds { get; init; } = new(); //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh - [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public SortedDictionary? InternalWounds { get; init; } = new(); + [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary? InternalWounds { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("structuralWounds", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public SortedDictionary? StructuralWounds { get; init; } = new(); + [DataField("structuralWounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary? StructuralWounds { get; init; } = new(); - //used to calculate how much this damage type propogates the skin + //used to calculate how much this damage type propogates to internal [DataField("surfacePenMod")] public float SurfacePenModifier = 1.0f; - //used to calculate how much this damage type propogates a bodypart/flesh + //used to calculate how much this damage type propogates to structure [DataField("internalPenMod")] public float InternalPenModifier = 1.0f; - //used to calculate how much this damage type propogates through a bone if it is protecting organs + //used to calculate how much this damage type propogates to the next layer [DataField("structurePenMod")] public float StructurePenModifier = 1.0f; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index c7f870c183858e..15e7cf2959a631 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,14 +1,10 @@ -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Body.Organ; -using Content.Shared.Body.Part; -using Content.Shared.Damage; +using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; -using Robust.Shared.Utility; namespace Content.Shared.Medical.Wounds.Systems; public sealed class WoundSystem : EntitySystem @@ -40,7 +36,7 @@ private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) } } - private SortedDictionary? GetWoundTableForDamageType(string damageTypeId, WoundCategory woundCategory) + private SortedDictionary? GetWoundTableForDamageType(string damageTypeId, WoundCategory woundCategory) { if (!_cachedDamageWoundMetaData.ContainsKey(damageTypeId)) return null; @@ -225,7 +221,7 @@ private void AddWound(WoundableComponent woundContainer, WoundCategory category, } } - private string PickWound(float level, SortedDictionary woundTable, out float severity) + private string PickWound(float level, SortedDictionary woundTable, out float severity) { var nextLevel = 1f; var levelFloor = 0f; @@ -234,11 +230,11 @@ private string PickWound(float level, SortedDictionary woun { if (woundLevel.Key > level) { - nextLevel = woundLevel.Key.Float(); + nextLevel = woundLevel.Key; break; } woundId = woundLevel.Value; - levelFloor = woundLevel.Key.Float(); + levelFloor = woundLevel.Key; } severity = (level - levelFloor) / (nextLevel - level); return woundId; @@ -293,9 +289,9 @@ public float CalculateWoundLevel(WoundableComponent woundableContainer, WoundCa private readonly struct WoundMetaData { //we do some defensive coding - public readonly SortedDictionary? SurfaceWounds; - public readonly SortedDictionary? InternalWounds; - public readonly SortedDictionary? StructuralWounds; + public readonly SortedDictionary? SurfaceWounds; + public readonly SortedDictionary? InternalWounds; + public readonly SortedDictionary? StructuralWounds; public readonly float SurfacePenModifier; public readonly float InternalPenModifier; public readonly float StructuralPenModifier; diff --git a/RobustToolbox b/RobustToolbox index 38f2808816f259..a08ee58446c022 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 38f2808816f259d74892edc5988bd94431fca6b8 +Subproject commit a08ee58446c022d693d18cef9ab1116b12d94275 From eb97064092473524c2b87f1e5455105732ae1e8f Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Mon, 14 Nov 2022 22:23:33 +0100 Subject: [PATCH 013/148] Fix errors --- Content.Shared/Damage/Prototypes/DamageTypePrototype.cs | 3 --- .../Medical/Wounds/Prototypes/WoundGroupPrototype.cs | 9 ++++----- .../Medical/Wounds/Prototypes/WoundPrototype.cs | 8 +++----- Content.Shared/Medical/Wounds/Systems/WoundSystem.cs | 5 ++--- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index a00ae4b9b4b101..6fb087d0393df8 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -1,7 +1,5 @@ -using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Damage.Prototypes @@ -10,7 +8,6 @@ namespace Content.Shared.Damage.Prototypes /// A single damage type. These types are grouped together in s. /// [Prototype("damageType")] - [Serializable, NetSerializable] public sealed class DamageTypePrototype : IPrototype { [IdDataField] diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs index 3d51ff59d3ea86..e956612d46cd96 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs @@ -1,11 +1,10 @@ using Content.Shared.Damage.Prototypes; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Medical.Wounds.Prototypes; -[DataDefinition] +[Prototype("woundGroup")] public sealed class WoundGroupPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; @@ -19,15 +18,15 @@ public sealed class WoundGroupPrototype : IPrototype //--- wound prototypes ordered in severity based on the initial --- //surface wounds are wounds on skin or exposed bodyparts [DataField("SurfaceWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SurfaceWoundProtos { get; init; } = new(); + public HashSet SurfaceWoundProtos { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton [DataField("solidWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SolidWoundProtos { get; init; } = new(); + public HashSet SolidWoundProtos { get; init; } = new(); //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh [DataField("internalWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet InternalWoundProtos { get; init; } = new(); + public HashSet InternalWoundProtos { get; init; } = new(); } diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs index 9ed65fc8d60445..7a0fe8483f6e5d 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs @@ -1,10 +1,8 @@ -using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; + namespace Content.Shared.Medical.Wounds.Prototypes; -[DataDefinition] +[Prototype("wound")] public sealed class WoundPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 15e7cf2959a631..0dd1bbc196375a 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -3,14 +3,13 @@ using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Robust.Shared.Serialization; + namespace Content.Shared.Medical.Wounds.Systems; public sealed class WoundSystem : EntitySystem { - [Dependency] private IPrototypeManager _prototypeManager = default!; - [Dependency] private RobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly Dictionary _cachedDamageWoundMetaData = new(); From 7629390b60e4e240ea6dd6b472c1710722d6e5dd Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 14 Nov 2022 18:15:46 -0800 Subject: [PATCH 014/148] Moved woundPools out of damagetype to their own prototype --- .../Damage/Prototypes/DamageTypePrototype.cs | 32 +------ .../Wounds/Prototypes/WoundGroupPrototype.cs | 36 ++++---- .../Medical/Wounds/Systems/WoundSystem.cs | 84 ++++++++++++------- 3 files changed, 78 insertions(+), 74 deletions(-) diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index 6fb087d0393df8..c4fe397e015473 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -1,6 +1,4 @@ -using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Damage.Prototypes { @@ -10,8 +8,7 @@ namespace Content.Shared.Damage.Prototypes [Prototype("damageType")] public sealed class DamageTypePrototype : IPrototype { - [IdDataField] - public string ID { get; } = default!; + [IdDataField] public string ID { get; } = default!; /// /// The price for each 1% damage reduction in armors @@ -24,32 +21,5 @@ public sealed class DamageTypePrototype : IPrototype /// [DataField("armorFlatPrice")] public double ArmorPriceFlat { get; set; } - - // --=== Wounding/Medical configuration ===--- - - //TODO: update these to use the value protoId dictionary serializer - - //Note: these should be defined in order of severity! - //surface wounds are wounds on skin or exposed bodyparts - [DataField("surfaceWounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary SurfaceWounds { get; init; } = new(); - - //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh - [DataField("internalWounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary? InternalWounds { get; init; } = new(); - - //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("structuralWounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary? StructuralWounds { get; init; } = new(); - - //used to calculate how much this damage type propogates to internal - [DataField("surfacePenMod")] public float SurfacePenModifier = 1.0f; - - //used to calculate how much this damage type propogates to structure - [DataField("internalPenMod")] public float InternalPenModifier = 1.0f; - - //used to calculate how much this damage type propogates to the next layer - [DataField("structurePenMod")] public float StructurePenModifier = 1.0f; - } } diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs index e956612d46cd96..29b439fe053591 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs @@ -1,6 +1,7 @@ using Content.Shared.Damage.Prototypes; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Medical.Wounds.Prototypes; @@ -9,24 +10,31 @@ public sealed class WoundGroupPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; - [DataField("damageType", required: true)] - public DamageTypePrototype DamageType { get; init; } = default!; + [DataField("damageType", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string DamageType { get; init; } = default!; - [DataField("depthModifier", required: false)] - public float DepthModifier { get; init; } = 1.0f; - - //--- wound prototypes ordered in severity based on the initial --- + //Note: these should be defined in order of severity! //surface wounds are wounds on skin or exposed bodyparts - [DataField("SurfaceWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SurfaceWoundProtos { get; init; } = new(); + [DataField("surfaceWounds", + customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary SurfaceWounds { get; init; } = new(); + + //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh + [DataField("internalWounds", + customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary? InternalWounds { get; init; } = new(); //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("solidWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet SolidWoundProtos { get; init; } = new(); + [DataField("structuralWounds", + customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary? StructuralWounds { get; init; } = new(); - //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh - [DataField("internalWounds", required: true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet InternalWoundProtos { get; init; } = new(); + //used to calculate how much this damage type propogates to internal + [DataField("surfacePenMod")] public float SurfacePenModifier = 1.0f; + //used to calculate how much this damage type propogates to structure + [DataField("internalPenMod")] public float InternalPenModifier = 1.0f; + //used to calculate how much this damage type propogates to the next layer + [DataField("structurePenMod")] public float StructurePenModifier = 1.0f; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 0dd1bbc196375a..41901fa2629bc2 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; +using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -29,13 +30,16 @@ private void OnDamageReceived(EntityUid uid, DamageableComponent component, Dama private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) { _cachedDamageWoundMetaData.Clear(); - foreach (var damageProto in _prototypeManager.EnumeratePrototypes()) + foreach (var woundGroup in _prototypeManager.EnumeratePrototypes()) { - _cachedDamageWoundMetaData.Add(damageProto.ID, new WoundMetaData(damageProto)); + if (!_cachedDamageWoundMetaData.TryAdd(woundGroup.DamageType, new WoundMetaData(woundGroup))) + Logger.Error("Woundgroup has duplicate damageType! ID:" + woundGroup.ID + " DamageType:" + + woundGroup.DamageType); } } - private SortedDictionary? GetWoundTableForDamageType(string damageTypeId, WoundCategory woundCategory) + private SortedDictionary? GetWoundTableForDamageType(string damageTypeId, + WoundCategory woundCategory) { if (!_cachedDamageWoundMetaData.ContainsKey(damageTypeId)) return null; @@ -59,13 +63,11 @@ public bool TryGetWoundPen(string damageType, WoundCategory category, out float? { woundPen = woundMetaData.SurfacePenModifier; return true; - } case WoundCategory.Internal: { woundPen = woundMetaData.InternalPenModifier; return true; - } case WoundCategory.Structural: { @@ -88,6 +90,7 @@ public bool TryGetWoundPen(string damageType, WoundCategory category, out float? var woundId = PickWound(level, woundTable, out var severity); return new WoundData(woundId, severity, woundCategory, damageType); } + //TODO: output a WoundHandle public bool ForceApplyWound(EntityUid target, string woundId, WoundCategory category, float severity) { @@ -109,32 +112,38 @@ public bool ApplyWound(EntityUid target, WoundData woundData, WoundCategory woun AddWound(woundContainer, woundCategory, woundData); return true; } + public bool ContainsForcableWound(WoundableComponent woundableComponent, string woundId) - {//Here be dragons + { + //Here be dragons return //fuck if statements, we use boolean ors in this neighborhood (woundableComponent.ForcedSurfaceWounds != null && woundableComponent.ForcedSurfaceWounds.Contains(woundId)) - || - (woundableComponent.ForcedInternalWounds != null && woundableComponent.ForcedInternalWounds.Contains(woundId)) - || - (woundableComponent.ForcedStructuralWounds != null && woundableComponent.ForcedStructuralWounds.Contains(woundId)); + || + (woundableComponent.ForcedInternalWounds != null && + woundableComponent.ForcedInternalWounds.Contains(woundId)) + || + (woundableComponent.ForcedStructuralWounds != null && + woundableComponent.ForcedStructuralWounds.Contains(woundId)); } - public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, float level, out WoundData? newWound) + public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, + float level, out WoundData? newWound) { newWound = CreateWound(damageType, category, level); if (!newWound.HasValue) return false; AddWound(woundComponent, category, newWound.Value); return true; - } - public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, float level) + public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, + float level) { return CreateAndApplyWound(woundComponent, damageType, category, level, out var _); } - public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float level, out WoundData? newWound) + public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float level, + out WoundData? newWound) { newWound = CreateWound(damageType, category, level); return newWound.HasValue && ApplyWound(target, newWound.Value, category); @@ -142,7 +151,7 @@ public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCatego public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float severity) { - return CreateAndApplyWound(target, damageType, category, severity,out _); + return CreateAndApplyWound(target, damageType, category, severity, out _); } @@ -154,6 +163,7 @@ public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) if (damageData.Value > 0) success |= TryApplyWounds(target, damageData.Key, damageData.Value); } + return success; } @@ -174,30 +184,38 @@ public bool TryApplyWounds(EntityUid target, string damageType, FixedPoint2 dama //if (EntityManager.TryGetComponent(target, out var coveringComp)) if (woundContainer.SurfaceWounds != null) { - damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Surface, damageType, damage.Float(), out _); + damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Surface, damageType, + damage.Float(), out _); } + if (woundContainer.InternalWounds != null && damageSpec.DamageDict[damageType] > 0) { - damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Internal, damageType, damage.Float(), out _); + damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Internal, damageType, + damage.Float(), out _); } + if (woundContainer.StructuralWounds == null || damageSpec.DamageDict[damageType] <= 0) return success; - damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Structural, damageType, damage.Float(),out var woundLevel); + damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Structural, damageType, + damage.Float(), out var woundLevel); if (woundLevel >= 1.0f) { //TODO: implement structural wound destruction //Destroy this if it's a bodypart! } + return success; } + //Apply a layering wound and return and penetrating damage - private float ApplyLayeringWound( WoundableComponent woundContainer, WoundCategory category, string damageType, float damage, out float woundLevel) + private float ApplyLayeringWound(WoundableComponent woundContainer, WoundCategory category, string damageType, + float damage, out float woundLevel) { woundLevel = CalculateWoundLevel(woundContainer, WoundCategory.Surface, damageType, damage, out var penDamage); if (penDamage != 0 && TryGetWoundPen(damageType, WoundCategory.Surface, out var penMultiplier)) penDamage *= penMultiplier!.Value; //apply penMultiplier CreateAndApplyWound(woundContainer, damageType, WoundCategory.Surface, woundLevel); - if (penDamage < 0)//this should never be the case but it's good to double check incase someone does a stupid + if (penDamage < 0) //this should never be the case but it's good to double check incase someone does a stupid penDamage = 0; return penDamage; } @@ -232,14 +250,17 @@ private string PickWound(float level, SortedDictionary woundTable nextLevel = woundLevel.Key; break; } + woundId = woundLevel.Value; levelFloor = woundLevel.Key; } + severity = (level - levelFloor) / (nextLevel - level); return woundId; } - private float GetDamageCapForDamageType(WoundableComponent woundableComponent, WoundCategory category, string damageType) + private float GetDamageCapForDamageType(WoundableComponent woundableComponent, WoundCategory category, + string damageType) { switch (category) { @@ -261,26 +282,31 @@ private float GetDamageCapForDamageType(WoundableComponent woundableComponent, W } //an implementation of calculate wound level that outputs excess damage - public float CalculateWoundLevel(WoundableComponent woundableComponent,WoundCategory category ,string damageType, FixedPoint2 damage, out float overDamage) + public float CalculateWoundLevel(WoundableComponent woundableComponent, WoundCategory category, string damageType, + FixedPoint2 damage, out float overDamage) { var damageCap = GetDamageCapForDamageType(woundableComponent, category, damageType); overDamage = 0.0f; - var level =damage.Float() / damageCap; + var level = damage.Float() / damageCap; if (!(level > 1.0f)) return level; level = 1.0f; overDamage = damage.Float() - damageCap; return level; } + //a simpler implementation of calculating wound levels without excess damage output - public float CalculateWoundLevel(WoundableComponent woundableContainer, WoundCategory category, string damageType, FixedPoint2 damage) + public float CalculateWoundLevel(WoundableComponent woundableContainer, WoundCategory category, string damageType, + FixedPoint2 damage) { return category switch { - WoundCategory.Surface => Math.Clamp(damage.Float() / woundableContainer.SurfaceDamageCap[damageType], 0f, 1.0f), + WoundCategory.Surface => Math.Clamp(damage.Float() / woundableContainer.SurfaceDamageCap[damageType], 0f, + 1.0f), WoundCategory.Internal => Math.Clamp(damage.Float() / woundableContainer.InternalDamageCap[damageType], 0f, 1.0f), - WoundCategory.Structural => Math.Clamp(damage.Float() / woundableContainer.StructuralDamageCap[damageType], 0f, 1.0f), + WoundCategory.Structural => Math.Clamp(damage.Float() / woundableContainer.StructuralDamageCap[damageType], + 0f, 1.0f), _ => throw new ArgumentException("WoundLayers was invalid! This should never happen!") }; } @@ -295,7 +321,7 @@ private readonly struct WoundMetaData public readonly float InternalPenModifier; public readonly float StructuralPenModifier; - public WoundMetaData(DamageTypePrototype damageTypeProto) + public WoundMetaData(WoundGroupPrototype damageTypeProto) { InternalWounds = damageTypeProto.InternalWounds; SurfaceWounds = damageTypeProto.SurfaceWounds; @@ -307,8 +333,8 @@ public WoundMetaData(DamageTypePrototype damageTypeProto) } } - [Serializable, NetSerializable, DataRecord] -public record struct WoundData(string Id, float Severity,WoundCategory WoundCategory, string? DamageType = null, float Tended = 0f, float Infected = 0f) +public record struct WoundData(string Id, float Severity, WoundCategory WoundCategory, string? DamageType = null, + float Tended = 0f, float Infected = 0f) { }; From ced971e3aa09e375dd7113567e2fe7f8b7be2d84 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Mon, 14 Nov 2022 18:23:19 -0800 Subject: [PATCH 015/148] Unfuck Csproj --- Content.Shared/Content.Shared.csproj | 5 ----- .../{WoundGroupPrototype.cs => WoundPoolPrototype.cs} | 4 ++-- Content.Shared/Medical/Wounds/Systems/WoundSystem.cs | 4 ++-- 3 files changed, 4 insertions(+), 9 deletions(-) rename Content.Shared/Medical/Wounds/Prototypes/{WoundGroupPrototype.cs => WoundPoolPrototype.cs} (96%) diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 4a7e144d576e27..b29b228b355bb5 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -30,11 +30,6 @@ - - - - - diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs similarity index 96% rename from Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs rename to Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs index 29b439fe053591..47ba5db9defde0 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundGroupPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs @@ -5,8 +5,8 @@ namespace Content.Shared.Medical.Wounds.Prototypes; -[Prototype("woundGroup")] -public sealed class WoundGroupPrototype : IPrototype +[Prototype("woundPool")] +public sealed class WoundPoolPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 41901fa2629bc2..aaf26ad7c83356 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -30,7 +30,7 @@ private void OnDamageReceived(EntityUid uid, DamageableComponent component, Dama private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) { _cachedDamageWoundMetaData.Clear(); - foreach (var woundGroup in _prototypeManager.EnumeratePrototypes()) + foreach (var woundGroup in _prototypeManager.EnumeratePrototypes()) { if (!_cachedDamageWoundMetaData.TryAdd(woundGroup.DamageType, new WoundMetaData(woundGroup))) Logger.Error("Woundgroup has duplicate damageType! ID:" + woundGroup.ID + " DamageType:" + @@ -321,7 +321,7 @@ private readonly struct WoundMetaData public readonly float InternalPenModifier; public readonly float StructuralPenModifier; - public WoundMetaData(WoundGroupPrototype damageTypeProto) + public WoundMetaData(WoundPoolPrototype damageTypeProto) { InternalWounds = damageTypeProto.InternalWounds; SurfaceWounds = damageTypeProto.SurfaceWounds; From 2db4d1e5324b1507f2884c474e8b3ea500fd5ee8 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Wed, 16 Nov 2022 02:14:49 -0800 Subject: [PATCH 016/148] Converted to new trauma-damage based system --- Content.Shared/Body/Part/BodySkinComponent.cs | 9 +- .../Wounds/Components/InjurableComponent.cs | 60 ++++ .../Components/TraumaInflictorComponent.cs | 11 + .../Wounds/Components/WoundableComponent.cs | 59 --- .../{WoundPrototype.cs => InjuryPrototype.cs} | 11 +- .../Wounds/Prototypes/TraumaTypePrototype.cs | 17 + .../Wounds/Prototypes/WoundPoolPrototype.cs | 40 --- .../Medical/Wounds/Systems/InjurySystem.cs | 173 +++++++++ .../Medical/Wounds/Systems/WoundSystem.cs | 340 ------------------ .../Medical/Wounds/TraumaModifierSet.cs | 34 ++ .../Medical/Wounds/TraumaSpecifier.cs | 71 ++++ 11 files changed, 376 insertions(+), 449 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Components/InjurableComponent.cs create mode 100644 Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs delete mode 100644 Content.Shared/Medical/Wounds/Components/WoundableComponent.cs rename Content.Shared/Medical/Wounds/Prototypes/{WoundPrototype.cs => InjuryPrototype.cs} (66%) create mode 100644 Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs delete mode 100644 Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs create mode 100644 Content.Shared/Medical/Wounds/Systems/InjurySystem.cs delete mode 100644 Content.Shared/Medical/Wounds/Systems/WoundSystem.cs create mode 100644 Content.Shared/Medical/Wounds/TraumaModifierSet.cs create mode 100644 Content.Shared/Medical/Wounds/TraumaSpecifier.cs diff --git a/Content.Shared/Body/Part/BodySkinComponent.cs b/Content.Shared/Body/Part/BodySkinComponent.cs index 9545880289c3a0..0f59cfd3b40d6c 100644 --- a/Content.Shared/Body/Part/BodySkinComponent.cs +++ b/Content.Shared/Body/Part/BodySkinComponent.cs @@ -1,20 +1,20 @@ using Content.Shared.Body.Prototypes; using Content.Shared.Body.Systems; using Content.Shared.Damage; -using Content.Shared.Medical.Wounds; using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + namespace Content.Shared.Body.Part; [RegisterComponent, NetworkedComponent] [Access(typeof(SharedBodySystem))] public sealed class BodySkinComponent : Component { - [Access(typeof(WoundSystem), typeof(SharedBodySystem), Other = AccessPermissions.Read)] - [DataField("skinLayers")] + [Access(typeof(InjurySystem), typeof(SharedBodySystem), Other = AccessPermissions.Read)] [DataField("skinLayers")] public List SkinLayers = new(); + public SkinlayerData? PrimaryLayer { get @@ -24,14 +24,13 @@ public SkinlayerData? PrimaryLayer return null; } } - } [Serializable, NetSerializable, DataRecord] public record struct SkinlayerData( [field: DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer))] string? ProtoType, - [field: DataField("name", required:true)] + [field: DataField("name", required: true)] string Name, string Description, float Coverage = 1.0f, DamageModifierSet? Resistance = null); diff --git a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs new file mode 100644 index 00000000000000..c34f3b00ae1789 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs @@ -0,0 +1,60 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Prototypes; +using Content.Shared.Medical.Wounds.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; + +namespace Content.Shared.Medical.Wounds.Components; + +[RegisterComponent, NetworkedComponent] +public sealed class InjurableComponent : Component +{ + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [DataField("injuries")] + public List? Injuries; + + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] + [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet? AllowedTraumaTypes; + + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("traumaResistance")] + public TraumaModifierSet? TraumaResistance; + + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("traumaPenResistance")] + public TraumaModifierSet? TraumaPenResistance; + + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("allowBleeds")] + public bool AllowBleeds = true; + + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("appliesPain")] + public bool AppliesPain = true; + + //How much health does this woundable have, when this reaches 0, it starts taking structural damage + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] + [ViewVariables, DataField("maxHealth", required: true)] + public FixedPoint2 MaxHealth; + + //How much health does this woundable have, when this reaches 0, it starts taking structural damage + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("health", required: true)] + public FixedPoint2 Health; + + //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] + [ViewVariables, DataField("maxStructure", required: true)] + public FixedPoint2 MaxStructure; + + //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] + [ViewVariables, DataField("structure", required: true)] + public FixedPoint2 StructuralPool; +} + +[DataRecord, NetSerializable, Serializable] +public record struct Injury(string InjuryId, FixedPoint2 Severity, FixedPoint2 Healed, FixedPoint2 Bleed, + FixedPoint2 Infected) +{ + public Injury(string injuryId, FixedPoint2 severity) : this(injuryId, severity, FixedPoint2.Zero, FixedPoint2.Zero, + FixedPoint2.Zero) + { + } +} diff --git a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs new file mode 100644 index 00000000000000..ae97db5e2f1e5c --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Medical.Wounds.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Medical.Wounds.Components; + +[RegisterComponent, NetworkedComponent] +public sealed class TraumaInflictorComponent : Component +{ + [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [DataField("Trauma", required: true)] + public TraumaSpecifier Trauma = new TraumaSpecifier(); +} diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs deleted file mode 100644 index 129b5fdcdcffc1..00000000000000 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; -using Content.Shared.Medical.Wounds.Prototypes; -using Content.Shared.Medical.Wounds.Systems; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; - -namespace Content.Shared.Medical.Wounds.Components; - -[RegisterComponent] -public sealed class WoundableComponent : Component -{ - [DataField("surfaceWoundInfo", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary SurfaceDamageCap = new(); - - [DataField("internalWoundInfo", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary InternalDamageCap = new(); - - [DataField("structuralWoundInfo", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary StructuralDamageCap = new(); - - [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - [ViewVariables, DataField("damageResistance")] - public DamageModifierSet? DamageResistance = new(); - - [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - [DataField("surfaceWounds")] - public List? SurfaceWounds; - - [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - [DataField("internalWounds")] - public List? InternalWounds; - - [Access(typeof(WoundSystem),Other = AccessPermissions.Read)] - [DataField("structuralWounds")] - public List? StructuralWounds; - - [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] - [DataField("forcedSurfaceWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet? ForcedSurfaceWounds; //List of wounds that can be applied regardless of damage type - - [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] - [DataField("forcedInternalWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet? ForcedInternalWounds; //List of wounds that can be applied regardless of damage type - - [Access(typeof(WoundSystem), Other = AccessPermissions.Read)] - [DataField("forceStructuralWounds", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet? ForcedStructuralWounds; //List of wounds that can be applied regardless of damage type -} - -//if a structural wound reaches level 1 and severity 1 the part/skin/bone WILL be lost! -[Serializable, NetSerializable] -public enum WoundCategory -{ - Surface = 1 << 0, - Internal = 1 << 1, - Structural = 1 << 2 -} diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs similarity index 66% rename from Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs rename to Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs index 7a0fe8483f6e5d..dba94c096c49f3 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs @@ -2,23 +2,24 @@ namespace Content.Shared.Medical.Wounds.Prototypes; -[Prototype("wound")] -public sealed class WoundPrototype : IPrototype +[Prototype("injury")] +public sealed class InjuryPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; [DataField("allowStacking")] public bool AllowStacking { get; init; } = true; - [DataField("name", required: true)] - public string DisplayName { get; init; } = string.Empty; + [DataField("name", required: true)] public string DisplayName { get; init; } = string.Empty; [DataField("description", required: true)] public string Description { get; init; } = string.Empty; [DataField("pain", required: true)] public float Pain { get; init; } - [DataField("bleedRate", required: false)] public float Bleed { get; init; } + [DataField("bleedRate", required: false)] + public float Bleed { get; init; } } + public static class A { // IT LIVES ON! FOREVER IN OUR HEARTS! diff --git a/Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs new file mode 100644 index 00000000000000..2ddfb3ce113843 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs @@ -0,0 +1,17 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; + +namespace Content.Shared.Medical.Wounds.Prototypes; + +[Prototype("traumaType")] +public sealed class TraumaTypePrototype : IPrototype +{ + [IdDataField] public string ID { get; init; } = string.Empty; + + //Note: these should be defined in order of severity! + //list of possible wounds sorted by their trauma cutoffs + [DataField("woundPool", required: true, + customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + public SortedDictionary WoundPool { get; init; } = new(); +} diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs deleted file mode 100644 index 47ba5db9defde0..00000000000000 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundPoolPrototype.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Content.Shared.Damage.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; - -namespace Content.Shared.Medical.Wounds.Prototypes; - -[Prototype("woundPool")] -public sealed class WoundPoolPrototype : IPrototype -{ - [IdDataField] public string ID { get; init; } = string.Empty; - - [DataField("damageType", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string DamageType { get; init; } = default!; - - //Note: these should be defined in order of severity! - //surface wounds are wounds on skin or exposed bodyparts - [DataField("surfaceWounds", - customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary SurfaceWounds { get; init; } = new(); - - //internal wounds are wounds that are caused when an injury affects internal soft tissue such as organs or flesh - [DataField("internalWounds", - customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary? InternalWounds { get; init; } = new(); - - //solid wounds are wounds that get caused when affecting a solid surface/object, such as bones or an exoskeleton - [DataField("structuralWounds", - customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary? StructuralWounds { get; init; } = new(); - - //used to calculate how much this damage type propogates to internal - [DataField("surfacePenMod")] public float SurfacePenModifier = 1.0f; - - //used to calculate how much this damage type propogates to structure - [DataField("internalPenMod")] public float InternalPenModifier = 1.0f; - - //used to calculate how much this damage type propogates to the next layer - [DataField("structurePenMod")] public float StructurePenModifier = 1.0f; -} diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs new file mode 100644 index 00000000000000..b8e77ee6cc36dd --- /dev/null +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -0,0 +1,173 @@ +using System.Collections.ObjectModel; +using Content.Shared.Body.Systems; +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Components; +using Content.Shared.Medical.Wounds.Prototypes; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Shared.Medical.Wounds.Systems; + +public sealed class InjurySystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedBodySystem _bodySystem = default!; + private readonly Dictionary _cachedInjuryTables = new(); + + public override void Initialize() + { + CacheData(null); + _prototypeManager.PrototypesReloaded += CacheData; + } + + private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) + { + _cachedInjuryTables.Clear(); + foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) + { + _cachedInjuryTables.Add(traumaType.ID, new InjuryTable(traumaType)); + } + } + + public ReadOnlyDictionary GetInjuryTable(string traumaType) + { + return new ReadOnlyDictionary(_cachedInjuryTables[traumaType].Injuries); + } + + public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) + { + var success = false; + foreach (var (traumaType, trauma) in traumaSpec.TraumaValues) + { + var validTarget = GetValidInjurable(target, traumaType); + if (!validTarget.HasValue) + return false; + var injuryContainer = validTarget.Value.injurable; + target = validTarget.Value.target; + + success |= AddInjury_internal(target, injuryContainer, traumaType, + PickInjury(traumaType, + ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage))); + + if (trauma.PenTraumaType == null || !_random.Prob(trauma.PenetrationChance.Float())) + continue; + validTarget = GetValidInjurable(target, trauma.PenTraumaType); + if (!validTarget.HasValue) + continue; + //Apply penetrating wounds + injuryContainer = validTarget.Value.injurable; + target = validTarget.Value.target; + success |= AddInjury_internal(target, injuryContainer, trauma.PenTraumaType, + PickInjury(trauma.PenTraumaType, + ApplyTraumaModifiers(trauma.PenTraumaType, injuryContainer.TraumaResistance, trauma.Damage))); + } + + return success; + } + + public bool AddInjury(EntityUid target, string traumaType, Trauma trauma) + { + var checkedContainer = GetValidInjurable(target, traumaType); + if (checkedContainer == null) + return false; + var injuryContainer = checkedContainer.Value.injurable; + target = checkedContainer.Value.target; + return AddInjury_internal(target, injuryContainer, traumaType, PickInjury(traumaType, trauma.Damage)); + } + + private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) + { + if (!modifiers.HasValue) + return damage; + if (modifiers.Value.TryGetCoefficentForTraumaType(traumaType, out var coeff)) + damage *= coeff; + if (modifiers.Value.TryGetFlatReductionForTraumaType(traumaType, out var reduction)) + damage -= reduction; + return damage; + } + + private bool AddInjury_internal(EntityUid target, InjurableComponent injuryContainer, string traumaType, + Injury injury) + { + injuryContainer.Injuries ??= new(); + injuryContainer.Injuries!.Add(injury); + return true; + } + + private (InjurableComponent injurable, EntityUid target)? GetValidInjurable(EntityUid target, string traumaType) + { + if (!TryComp(target, out var injuryContainer)) + return null; + if (injuryContainer.AllowedTraumaTypes == null) + return (injuryContainer, target); + if (injuryContainer.AllowedTraumaTypes.Contains(traumaType)) + return (injuryContainer, target); + var childInjuryContainer = FindValidInjurableInAdjacent(target, traumaType); + return childInjuryContainer; + } + + + private Injury PickInjury(string traumaType, FixedPoint2 trauma) + { + var nextLevel = 1f; + var levelFloor = 0f; + var injuryId = _cachedInjuryTables[traumaType].Injuries[0]; + foreach (var injuryData in _cachedInjuryTables[traumaType].Injuries) + { + if (injuryData.Key > trauma) + { + nextLevel = injuryData.Key.Float(); + break; + } + + injuryId = injuryData.Value; + levelFloor = injuryData.Key.Float(); + } + + return new Injury(injuryId, (trauma - levelFloor) / (nextLevel - trauma)); + } + + private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacent(EntityUid target, + string traumaType) + { + //search all the children in the body for a part that accepts the wound we want to apply + //checks organs first, then recursively checks child parts and their organs. + var foundContainer = FindValidInjurableInOrgans(target, traumaType) ?? + FindValidInjurableInAdjacentParts(target, traumaType); + return foundContainer; + } + + private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacentParts(EntityUid target, + string traumaType) + { + //Stub method for SMUG to implement <3 + return null; + } + + private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInOrgans(EntityUid target, + string traumaType) + { + //TODO: replace this with bodypartOrganComps + foreach (var data in _bodySystem.GetBodyOrganComponents(target)) + { + if (data.Comp.AllowedTraumaTypes == null) + continue; + if (data.Comp.AllowedTraumaTypes.Contains(traumaType)) + return (data.Comp, target); + } + + return null; + } +} + +public readonly struct InjuryTable +{ + //we do some defensive coding + public readonly SortedDictionary Injuries; + + public InjuryTable(TraumaTypePrototype traumaProto) + { + Injuries = traumaProto.WoundPool; + } +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs deleted file mode 100644 index aaf26ad7c83356..00000000000000 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ /dev/null @@ -1,340 +0,0 @@ -using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; -using Content.Shared.FixedPoint; -using Content.Shared.Medical.Wounds.Components; -using Content.Shared.Medical.Wounds.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; - -namespace Content.Shared.Medical.Wounds.Systems; - -public sealed class WoundSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - private readonly Dictionary _cachedDamageWoundMetaData = new(); - - public override void Initialize() - { - CacheData(null); - _prototypeManager.PrototypesReloaded += CacheData; - SubscribeLocalEvent(OnDamageReceived); - } - - private void OnDamageReceived(EntityUid uid, DamageableComponent component, DamageChangedEvent args) - { - if (args.DamageDelta != null) - TryApplyWounds(uid, args.DamageDelta); - } - - private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) - { - _cachedDamageWoundMetaData.Clear(); - foreach (var woundGroup in _prototypeManager.EnumeratePrototypes()) - { - if (!_cachedDamageWoundMetaData.TryAdd(woundGroup.DamageType, new WoundMetaData(woundGroup))) - Logger.Error("Woundgroup has duplicate damageType! ID:" + woundGroup.ID + " DamageType:" + - woundGroup.DamageType); - } - } - - private SortedDictionary? GetWoundTableForDamageType(string damageTypeId, - WoundCategory woundCategory) - { - if (!_cachedDamageWoundMetaData.ContainsKey(damageTypeId)) - return null; - return woundCategory switch - { - WoundCategory.Surface => _cachedDamageWoundMetaData[damageTypeId].SurfaceWounds, - WoundCategory.Internal => _cachedDamageWoundMetaData[damageTypeId].InternalWounds, - WoundCategory.Structural => _cachedDamageWoundMetaData[damageTypeId].StructuralWounds, - _ => throw new ArgumentException("WoundLayers was invalid! This should never happen!") - }; - } - - public bool TryGetWoundPen(string damageType, WoundCategory category, out float? woundPen) - { - woundPen = null; - if (!_cachedDamageWoundMetaData.TryGetValue(damageType, out var woundMetaData)) - return false; - switch (category) - { - case WoundCategory.Surface: - { - woundPen = woundMetaData.SurfacePenModifier; - return true; - } - case WoundCategory.Internal: - { - woundPen = woundMetaData.InternalPenModifier; - return true; - } - case WoundCategory.Structural: - { - woundPen = woundMetaData.StructuralPenModifier; - return true; - } - default: - throw new ArgumentException("Wound category is invalid! This should never happen!"); - } - } - - public WoundData? CreateWound(string damageType, WoundCategory woundCategory, float level) - { - if (level < 0) //if level is invalid return null - return null; - var woundTable = GetWoundTableForDamageType(damageType, woundCategory); - if (woundTable == null) - return null; - //wound severity is the percentage to reach the next wound level, it describes how severe this particular wound is - var woundId = PickWound(level, woundTable, out var severity); - return new WoundData(woundId, severity, woundCategory, damageType); - } - - //TODO: output a WoundHandle - public bool ForceApplyWound(EntityUid target, string woundId, WoundCategory category, float severity) - { - if (!EntityManager.TryGetComponent(target, out var woundContainer)) - return false; - if (!ContainsForcableWound(woundContainer, woundId)) - return false; - AddWound(woundContainer, category, new WoundData(woundId, severity, category)); - return true; - } - - - //TODO: output a WoundHandle - public bool ApplyWound(EntityUid target, WoundData woundData, WoundCategory woundCategory) - { - if (!EntityManager.TryGetComponent(target, out var woundContainer)) - return false; - //TODO: implement stacking/severity increases later! - AddWound(woundContainer, woundCategory, woundData); - return true; - } - - public bool ContainsForcableWound(WoundableComponent woundableComponent, string woundId) - { - //Here be dragons - return //fuck if statements, we use boolean ors in this neighborhood - (woundableComponent.ForcedSurfaceWounds != null && woundableComponent.ForcedSurfaceWounds.Contains(woundId)) - || - (woundableComponent.ForcedInternalWounds != null && - woundableComponent.ForcedInternalWounds.Contains(woundId)) - || - (woundableComponent.ForcedStructuralWounds != null && - woundableComponent.ForcedStructuralWounds.Contains(woundId)); - } - - public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, - float level, out WoundData? newWound) - { - newWound = CreateWound(damageType, category, level); - if (!newWound.HasValue) - return false; - AddWound(woundComponent, category, newWound.Value); - return true; - } - - public bool CreateAndApplyWound(WoundableComponent woundComponent, string damageType, WoundCategory category, - float level) - { - return CreateAndApplyWound(woundComponent, damageType, category, level, out var _); - } - - public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float level, - out WoundData? newWound) - { - newWound = CreateWound(damageType, category, level); - return newWound.HasValue && ApplyWound(target, newWound.Value, category); - } - - public bool CreateAndApplyWound(EntityUid target, string damageType, WoundCategory category, float severity) - { - return CreateAndApplyWound(target, damageType, category, severity, out _); - } - - - public bool TryApplyWounds(EntityUid target, DamageSpecifier damage) - { - var success = false; - foreach (var damageData in damage.DamageDict) - { - if (damageData.Value > 0) - success |= TryApplyWounds(target, damageData.Key, damageData.Value); - } - - return success; - } - - //try to apply wounds to a part for specific damage - public bool TryApplyWounds(EntityUid target, string damageType, FixedPoint2 damage) - { - var success = false; - if (!EntityManager.TryGetComponent(target, out var woundContainer)) - return false; - if (!_prototypeManager.TryIndex(damageType, out var damageTypeProto)) - return false; - var damageSpec = new DamageSpecifier(damageTypeProto, damage); - if (woundContainer.DamageResistance != null) - damageSpec = DamageSpecifier.ApplyModifierSet(damageSpec, woundContainer.DamageResistance); - if (damageSpec.DamageDict[damageType] == 0) - return false; - //TODO: implement custom wounds defined by skins - //if (EntityManager.TryGetComponent(target, out var coveringComp)) - if (woundContainer.SurfaceWounds != null) - { - damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Surface, damageType, - damage.Float(), out _); - } - - if (woundContainer.InternalWounds != null && damageSpec.DamageDict[damageType] > 0) - { - damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Internal, damageType, - damage.Float(), out _); - } - - if (woundContainer.StructuralWounds == null || damageSpec.DamageDict[damageType] <= 0) - return success; - damageSpec.DamageDict[damageType] = ApplyLayeringWound(woundContainer, WoundCategory.Structural, damageType, - damage.Float(), out var woundLevel); - if (woundLevel >= 1.0f) - { - //TODO: implement structural wound destruction - //Destroy this if it's a bodypart! - } - - return success; - } - - //Apply a layering wound and return and penetrating damage - private float ApplyLayeringWound(WoundableComponent woundContainer, WoundCategory category, string damageType, - float damage, out float woundLevel) - { - woundLevel = CalculateWoundLevel(woundContainer, WoundCategory.Surface, damageType, damage, out var penDamage); - if (penDamage != 0 && TryGetWoundPen(damageType, WoundCategory.Surface, out var penMultiplier)) - penDamage *= penMultiplier!.Value; //apply penMultiplier - CreateAndApplyWound(woundContainer, damageType, WoundCategory.Surface, woundLevel); - if (penDamage < 0) //this should never be the case but it's good to double check incase someone does a stupid - penDamage = 0; - return penDamage; - } - - private void AddWound(WoundableComponent woundContainer, WoundCategory category, WoundData wound) - { - switch (category) - { - case WoundCategory.Surface: - woundContainer.SurfaceWounds?.Add(wound); - return; - case WoundCategory.Internal: - woundContainer.InternalWounds?.Add(wound); - return; - case WoundCategory.Structural: - woundContainer.StructuralWounds?.Add(wound); - return; - default: - throw new ArgumentException("Wound category is invalid! This should never happen!"); - } - } - - private string PickWound(float level, SortedDictionary woundTable, out float severity) - { - var nextLevel = 1f; - var levelFloor = 0f; - var woundId = string.Empty; - foreach (var woundLevel in woundTable) - { - if (woundLevel.Key > level) - { - nextLevel = woundLevel.Key; - break; - } - - woundId = woundLevel.Value; - levelFloor = woundLevel.Key; - } - - severity = (level - levelFloor) / (nextLevel - level); - return woundId; - } - - private float GetDamageCapForDamageType(WoundableComponent woundableComponent, WoundCategory category, - string damageType) - { - switch (category) - { - case WoundCategory.Surface: - if (woundableComponent.SurfaceDamageCap.TryGetValue(damageType, out var cap1)) - return cap1; - return -1; - case WoundCategory.Internal: - if (woundableComponent.InternalDamageCap.TryGetValue(damageType, out var cap2)) - return cap2; - return -1; - case WoundCategory.Structural: - if (woundableComponent.StructuralDamageCap.TryGetValue(damageType, out var cap3)) - return cap3; - return -1; - default: - throw new ArgumentException("Wound category is invalid! This should never happen!"); - } - } - - //an implementation of calculate wound level that outputs excess damage - public float CalculateWoundLevel(WoundableComponent woundableComponent, WoundCategory category, string damageType, - FixedPoint2 damage, out float overDamage) - { - var damageCap = GetDamageCapForDamageType(woundableComponent, category, damageType); - overDamage = 0.0f; - var level = damage.Float() / damageCap; - if (!(level > 1.0f)) - return level; - level = 1.0f; - overDamage = damage.Float() - damageCap; - return level; - } - - //a simpler implementation of calculating wound levels without excess damage output - public float CalculateWoundLevel(WoundableComponent woundableContainer, WoundCategory category, string damageType, - FixedPoint2 damage) - { - return category switch - { - WoundCategory.Surface => Math.Clamp(damage.Float() / woundableContainer.SurfaceDamageCap[damageType], 0f, - 1.0f), - WoundCategory.Internal => Math.Clamp(damage.Float() / woundableContainer.InternalDamageCap[damageType], 0f, - 1.0f), - WoundCategory.Structural => Math.Clamp(damage.Float() / woundableContainer.StructuralDamageCap[damageType], - 0f, 1.0f), - _ => throw new ArgumentException("WoundLayers was invalid! This should never happen!") - }; - } - - private readonly struct WoundMetaData - { - //we do some defensive coding - public readonly SortedDictionary? SurfaceWounds; - public readonly SortedDictionary? InternalWounds; - public readonly SortedDictionary? StructuralWounds; - public readonly float SurfacePenModifier; - public readonly float InternalPenModifier; - public readonly float StructuralPenModifier; - - public WoundMetaData(WoundPoolPrototype damageTypeProto) - { - InternalWounds = damageTypeProto.InternalWounds; - SurfaceWounds = damageTypeProto.SurfaceWounds; - StructuralWounds = damageTypeProto.StructuralWounds; - SurfacePenModifier = damageTypeProto.SurfacePenModifier; - InternalPenModifier = damageTypeProto.InternalPenModifier; - StructuralPenModifier = damageTypeProto.StructurePenModifier; - } - } -} - -[Serializable, NetSerializable, DataRecord] -public record struct WoundData(string Id, float Severity, WoundCategory WoundCategory, string? DamageType = null, - float Tended = 0f, float Infected = 0f) -{ -}; diff --git a/Content.Shared/Medical/Wounds/TraumaModifierSet.cs b/Content.Shared/Medical/Wounds/TraumaModifierSet.cs new file mode 100644 index 00000000000000..9b90bcc4f0be6e --- /dev/null +++ b/Content.Shared/Medical/Wounds/TraumaModifierSet.cs @@ -0,0 +1,34 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; + +namespace Content.Shared.Medical.Wounds; + +[DataDefinition] +[Serializable, NetSerializable] +public struct TraumaModifierSet +{ + [DataField("coefficients", + customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary Coefficients = new(); + + [DataField("flatReductions", + customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary FlatReduction = new(); + + public bool TryGetCoefficentForTraumaType(string traumaType, out FixedPoint2 coefficent) + { + return Coefficients.TryGetValue(traumaType, out coefficent); + } + + public bool TryGetFlatReductionForTraumaType(string traumaType, out FixedPoint2 reduction) + { + return Coefficients.TryGetValue(traumaType, out reduction); + } + + + public TraumaModifierSet() + { + } +} diff --git a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs new file mode 100644 index 00000000000000..2b7e9d3c07e688 --- /dev/null +++ b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs @@ -0,0 +1,71 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; + +namespace Content.Shared.Medical.Wounds; + +[DataDefinition, NetSerializable, Serializable] +public struct TraumaSpecifier +{ + [DataField("traumas", + customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + private readonly Dictionary _traumaData; + + public Dictionary TraumaValues => _traumaData; + + public void ApplyModifiers(TraumaModifierSet modifiers) + { + foreach (var traumaType in modifiers.Coefficients.Keys) + { + if (!_traumaData.ContainsKey(traumaType)) + continue; + var temp = _traumaData[traumaType]; + _traumaData[traumaType] = temp with {Damage = temp.Damage * modifiers.Coefficients[traumaType]}; + } + + foreach (var traumaType in modifiers.FlatReduction.Keys) + { + if (!_traumaData.ContainsKey(traumaType)) + continue; + var temp = _traumaData[traumaType]; + _traumaData[traumaType] = temp with {Damage = temp.Damage - modifiers.FlatReduction[traumaType]}; + } + } + + public void ApplyPenModifiers(TraumaModifierSet modifiers) + { + foreach (var traumaType in modifiers.Coefficients.Keys) + { + if (!_traumaData.ContainsKey(traumaType)) + continue; + var temp = _traumaData[traumaType]; + _traumaData[traumaType] = temp with + { + PenetrationChance = temp.PenetrationChance * modifiers.Coefficients[traumaType] + }; + } + + foreach (var traumaType in modifiers.FlatReduction.Keys) + { + if (!_traumaData.ContainsKey(traumaType)) + continue; + var temp = _traumaData[traumaType]; + _traumaData[traumaType] = temp with + { + PenetrationChance = temp.PenetrationChance - modifiers.FlatReduction[traumaType] + }; + } + } +} + +[DataRecord, Serializable, NetSerializable] +public record struct Trauma( + [field: DataField("Damage", required: true)] + FixedPoint2 Damage, //Damage represents the amount of trauma dealt + [field: DataField("Chance", required: false)] + FixedPoint2 PenetrationChance, + [field: DataField("penTraumaType", required: false, customTypeSerializer: typeof(PrototypeIdSerializer<>))] + string? PenTraumaType = null +); //Penetration represents how much this damage penetrates to hit child parts From 8476dd5f9f5594aad805c97096297d8b490a389e Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 11:31:36 +0100 Subject: [PATCH 017/148] Use methods from merge --- .../Body/Systems/SharedBodySystem.Parts.cs | 2 +- .../Medical/Wounds/Systems/InjurySystem.cs | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index a3f531bf700f6f..f11c311d7fcfa2 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -371,7 +371,7 @@ public IEnumerable GetBodyPartAdjacentParts(EntityUid partId, BodyPar if (!Resolve(partId, ref part, false)) yield break; - if (part.ParentSlot != null) + if (part.ParentSlot != null && HasComp(partId)) yield return part.ParentSlot.Parent; foreach (var slot in part.Children.Values) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index b8e77ee6cc36dd..6223f33aee76ef 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -141,19 +141,21 @@ private Injury PickInjury(string traumaType, FixedPoint2 trauma) private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacentParts(EntityUid target, string traumaType) { - //Stub method for SMUG to implement <3 + foreach (var data in _bodySystem.GetBodyPartAdjacentPartsComponents(target)) + { + if (data.Component.AllowedTraumaTypes?.Contains(traumaType) ?? false) + return (data.Component, target); + } + return null; } private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInOrgans(EntityUid target, string traumaType) { - //TODO: replace this with bodypartOrganComps - foreach (var data in _bodySystem.GetBodyOrganComponents(target)) + foreach (var data in _bodySystem.GetBodyPartOrganComponents(target)) { - if (data.Comp.AllowedTraumaTypes == null) - continue; - if (data.Comp.AllowedTraumaTypes.Contains(traumaType)) + if (data.Comp.AllowedTraumaTypes?.Contains(traumaType) ?? false) return (data.Comp, target); } From 5bd8295eae7b9251bd4e468bfedd69107c65aa95 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 11:37:55 +0100 Subject: [PATCH 018/148] Cleanup --- .../Wounds/Components/InjurableComponent.cs | 25 +++++++------ .../Medical/Wounds/Systems/InjurySystem.cs | 36 ++++++++++++------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs index c34f3b00ae1789..7d0355ac0f97a8 100644 --- a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs @@ -8,44 +8,43 @@ namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent, NetworkedComponent] +[Access(typeof(InjurySystem))] public sealed class InjurableComponent : Component { - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [DataField("injuries")] + [DataField("injuries")] public List? Injuries; - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet? AllowedTraumaTypes; - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("traumaResistance")] + [DataField("traumaResistance")] public TraumaModifierSet? TraumaResistance; - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("traumaPenResistance")] + [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("allowBleeds")] + [DataField("allowBleeds")] public bool AllowBleeds = true; - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("appliesPain")] + [DataField("appliesPain")] public bool AppliesPain = true; //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] - [ViewVariables, DataField("maxHealth", required: true)] + [DataField("maxHealth", required: true)] public FixedPoint2 MaxHealth; //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [ViewVariables, DataField("health", required: true)] + [DataField("health", required: true)] public FixedPoint2 Health; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] - [ViewVariables, DataField("maxStructure", required: true)] + [Access(typeof(InjurySystem))] + [DataField("maxStructure", required: true)] public FixedPoint2 MaxStructure; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] - [ViewVariables, DataField("structure", required: true)] + [Access(typeof(InjurySystem))] + [DataField("structure", required: true)] public FixedPoint2 StructuralPool; } diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 6223f33aee76ef..edc06a6e894eb5 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -24,6 +24,7 @@ public override void Initialize() private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) { _cachedInjuryTables.Clear(); + foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) { _cachedInjuryTables.Add(traumaType.ID, new InjuryTable(traumaType)); @@ -41,24 +42,29 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) foreach (var (traumaType, trauma) in traumaSpec.TraumaValues) { var validTarget = GetValidInjurable(target, traumaType); + if (!validTarget.HasValue) return false; + var injuryContainer = validTarget.Value.injurable; target = validTarget.Value.target; - success |= AddInjury_internal(target, injuryContainer, traumaType, + success |= AddInjury(target, injuryContainer, traumaType, PickInjury(traumaType, ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage))); if (trauma.PenTraumaType == null || !_random.Prob(trauma.PenetrationChance.Float())) continue; + validTarget = GetValidInjurable(target, trauma.PenTraumaType); + if (!validTarget.HasValue) continue; + //Apply penetrating wounds injuryContainer = validTarget.Value.injurable; target = validTarget.Value.target; - success |= AddInjury_internal(target, injuryContainer, trauma.PenTraumaType, + success |= AddInjury(target, injuryContainer, trauma.PenTraumaType, PickInjury(trauma.PenTraumaType, ApplyTraumaModifiers(trauma.PenTraumaType, injuryContainer.TraumaResistance, trauma.Damage))); } @@ -66,32 +72,38 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) return success; } - public bool AddInjury(EntityUid target, string traumaType, Trauma trauma) + public bool TryAddInjury(EntityUid target, string traumaType, Trauma trauma) { var checkedContainer = GetValidInjurable(target, traumaType); + if (checkedContainer == null) return false; + var injuryContainer = checkedContainer.Value.injurable; target = checkedContainer.Value.target; - return AddInjury_internal(target, injuryContainer, traumaType, PickInjury(traumaType, trauma.Damage)); + return AddInjury(target, injuryContainer, traumaType, PickInjury(traumaType, trauma.Damage)); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) { if (!modifiers.HasValue) return damage; + if (modifiers.Value.TryGetCoefficentForTraumaType(traumaType, out var coeff)) damage *= coeff; + if (modifiers.Value.TryGetFlatReductionForTraumaType(traumaType, out var reduction)) damage -= reduction; + return damage; } - private bool AddInjury_internal(EntityUid target, InjurableComponent injuryContainer, string traumaType, + private bool AddInjury(EntityUid target, InjurableComponent injuryContainer, string traumaType, Injury injury) { - injuryContainer.Injuries ??= new(); - injuryContainer.Injuries!.Add(injury); + injuryContainer.Injuries ??= new List(); + injuryContainer.Injuries.Add(injury); + return true; } @@ -99,20 +111,20 @@ private bool AddInjury_internal(EntityUid target, InjurableComponent injuryConta { if (!TryComp(target, out var injuryContainer)) return null; - if (injuryContainer.AllowedTraumaTypes == null) - return (injuryContainer, target); - if (injuryContainer.AllowedTraumaTypes.Contains(traumaType)) + + if (injuryContainer.AllowedTraumaTypes?.Contains(traumaType) ?? false) return (injuryContainer, target); + var childInjuryContainer = FindValidInjurableInAdjacent(target, traumaType); return childInjuryContainer; } - private Injury PickInjury(string traumaType, FixedPoint2 trauma) { var nextLevel = 1f; var levelFloor = 0f; - var injuryId = _cachedInjuryTables[traumaType].Injuries[0]; + var injuryId = _cachedInjuryTables[traumaType].Injuries[FixedPoint2.Zero]; + foreach (var injuryData in _cachedInjuryTables[traumaType].Injuries) { if (injuryData.Key > trauma) From c0e8821512d9d0c0fb09afc2e3dabce196cd8ac1 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 11:42:11 +0100 Subject: [PATCH 019/148] Cleanup --- Content.Shared/Medical/Wounds/Systems/InjurySystem.cs | 2 +- Content.Shared/Medical/Wounds/TraumaModifierSet.cs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index edc06a6e894eb5..47cc398fe02c20 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -89,7 +89,7 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m if (!modifiers.HasValue) return damage; - if (modifiers.Value.TryGetCoefficentForTraumaType(traumaType, out var coeff)) + if (modifiers.Value.TryGetCoefficientForTraumaType(traumaType, out var coeff)) damage *= coeff; if (modifiers.Value.TryGetFlatReductionForTraumaType(traumaType, out var reduction)) diff --git a/Content.Shared/Medical/Wounds/TraumaModifierSet.cs b/Content.Shared/Medical/Wounds/TraumaModifierSet.cs index 9b90bcc4f0be6e..d33721dc070afe 100644 --- a/Content.Shared/Medical/Wounds/TraumaModifierSet.cs +++ b/Content.Shared/Medical/Wounds/TraumaModifierSet.cs @@ -17,9 +17,9 @@ public struct TraumaModifierSet customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] public Dictionary FlatReduction = new(); - public bool TryGetCoefficentForTraumaType(string traumaType, out FixedPoint2 coefficent) + public bool TryGetCoefficientForTraumaType(string traumaType, out FixedPoint2 coefficient) { - return Coefficients.TryGetValue(traumaType, out coefficent); + return Coefficients.TryGetValue(traumaType, out coefficient); } public bool TryGetFlatReductionForTraumaType(string traumaType, out FixedPoint2 reduction) @@ -27,7 +27,6 @@ public bool TryGetFlatReductionForTraumaType(string traumaType, out FixedPoint2 return Coefficients.TryGetValue(traumaType, out reduction); } - public TraumaModifierSet() { } From 5bb74478201a434a73956963db724337ac5a2efc Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 11:57:39 +0100 Subject: [PATCH 020/148] Default to traumaType if no penTraumaType --- .../Medical/Wounds/Systems/InjurySystem.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 47cc398fe02c20..4122491a6c68d0 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -53,10 +53,15 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) PickInjury(traumaType, ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage))); - if (trauma.PenTraumaType == null || !_random.Prob(trauma.PenetrationChance.Float())) - continue; + var type = trauma.PenTraumaType ?? traumaType; + + if (trauma.PenTraumaType != null) + { + if (!_random.Prob(trauma.PenetrationChance.Float())) + continue; + } - validTarget = GetValidInjurable(target, trauma.PenTraumaType); + validTarget = GetValidInjurable(target, type); if (!validTarget.HasValue) continue; @@ -64,9 +69,8 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) //Apply penetrating wounds injuryContainer = validTarget.Value.injurable; target = validTarget.Value.target; - success |= AddInjury(target, injuryContainer, trauma.PenTraumaType, - PickInjury(trauma.PenTraumaType, - ApplyTraumaModifiers(trauma.PenTraumaType, injuryContainer.TraumaResistance, trauma.Damage))); + success |= AddInjury(target, injuryContainer, type, + PickInjury(type, ApplyTraumaModifiers(type, injuryContainer.TraumaResistance, trauma.Damage))); } return success; @@ -98,8 +102,7 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m return damage; } - private bool AddInjury(EntityUid target, InjurableComponent injuryContainer, string traumaType, - Injury injury) + private bool AddInjury(EntityUid target, InjurableComponent injuryContainer, string traumaType, Injury injury) { injuryContainer.Injuries ??= new List(); injuryContainer.Injuries.Add(injury); From 02a73769f1ab21e184c54f51c6b5c1f02f30f7f1 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 11:58:26 +0100 Subject: [PATCH 021/148] Remove infected --- .../Medical/Wounds/Components/InjurableComponent.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs index 7d0355ac0f97a8..2f449cc0361dfc 100644 --- a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs @@ -49,11 +49,9 @@ public sealed class InjurableComponent : Component } [DataRecord, NetSerializable, Serializable] -public record struct Injury(string InjuryId, FixedPoint2 Severity, FixedPoint2 Healed, FixedPoint2 Bleed, - FixedPoint2 Infected) +public record struct Injury(string InjuryId, FixedPoint2 Severity, FixedPoint2 Healed, FixedPoint2 Bleed) { - public Injury(string injuryId, FixedPoint2 severity) : this(injuryId, severity, FixedPoint2.Zero, FixedPoint2.Zero, - FixedPoint2.Zero) + public Injury(string injuryId, FixedPoint2 severity) : this(injuryId, severity, FixedPoint2.Zero, FixedPoint2.Zero) { } } From 1a215cf6289f6bccc0bce8cbb8df291f150fe421 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 11:59:06 +0100 Subject: [PATCH 022/148] Cleanup --- .../Medical/Wounds/Components/TraumaInflictorComponent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs index ae97db5e2f1e5c..f3e2717d8e1e17 100644 --- a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs @@ -4,8 +4,9 @@ namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent, NetworkedComponent] +[Access(typeof(InjurySystem))] public sealed class TraumaInflictorComponent : Component { - [Access(typeof(InjurySystem), Other = AccessPermissions.Read)] [DataField("Trauma", required: true)] + [DataField("Trauma", required: true)] public TraumaSpecifier Trauma = new TraumaSpecifier(); } From d87d7d7c8fce84a27b1695be6437073831872296 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 12:01:43 +0100 Subject: [PATCH 023/148] Add health damage and integrity damage --- .../Wounds/Prototypes/InjuryPrototype.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs index dba94c096c49f3..035f021d40ba80 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; namespace Content.Shared.Medical.Wounds.Prototypes; @@ -7,17 +8,26 @@ public sealed class InjuryPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; - [DataField("allowStacking")] public bool AllowStacking { get; init; } = true; + [DataField("allowStacking")] + public readonly bool AllowStacking = true; - [DataField("name", required: true)] public string DisplayName { get; init; } = string.Empty; + [DataField("name", required: true)] + public readonly string DisplayName = string.Empty; [DataField("description", required: true)] - public string Description { get; init; } = string.Empty; + public readonly string Description = string.Empty; - [DataField("pain", required: true)] public float Pain { get; init; } + [DataField("pain", required: true)] + public readonly float Pain; [DataField("bleedRate", required: false)] - public float Bleed { get; init; } + public readonly float Bleed; + + [DataField("healthDamage")] + public readonly FixedPoint2 HealthDamage; + + [DataField("integrityDamage")] + public readonly FixedPoint2 IntegrityDamage; } public static class A From 070a1c4bb9ab405137f59defd71a58f552db1000 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 20:40:11 +0100 Subject: [PATCH 024/148] Fix sandbox error --- Content.Shared/Medical/Wounds/Systems/InjurySystem.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 4122491a6c68d0..6f899e0a7e2821 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -1,5 +1,4 @@ -using System.Collections.ObjectModel; -using Content.Shared.Body.Systems; +using Content.Shared.Body.Systems; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; @@ -31,9 +30,9 @@ private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) } } - public ReadOnlyDictionary GetInjuryTable(string traumaType) + public IReadOnlyDictionary GetInjuryTable(string traumaType) { - return new ReadOnlyDictionary(_cachedInjuryTables[traumaType].Injuries); + return _cachedInjuryTables[traumaType].Injuries; } public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) From 24df4b77900c3beb59186c6cb61aff31a177175b Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 16 Nov 2022 21:53:31 +0100 Subject: [PATCH 025/148] Add test injury crack --- .../Body/Systems/SharedBodySystem.Body.cs | 1 + .../Components/TraumaInflictorComponent.cs | 2 +- .../Medical/Wounds/Systems/InjurySystem.cs | 19 +++++++++- .../Medical/Wounds/TraumaSpecifier.cs | 36 +++++++++---------- .../Prototypes/Body/Injuries/injuries.yml | 7 ++++ Resources/Prototypes/Body/Parts/human.yml | 7 ++++ Resources/Prototypes/Body/Traumas/trauma.yml | 4 +++ .../Entities/Objects/Weapons/Melee/sword.yml | 7 ++++ 8 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 Resources/Prototypes/Body/Injuries/injuries.yml create mode 100644 Resources/Prototypes/Body/Traumas/trauma.yml diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 593b54e271c5c7..bf1136ee7ae909 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -28,6 +28,7 @@ private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args var prototype = Prototypes.Index(body.Prototype); InitBody(body, prototype); + Dirty(body); } private void OnBodyGetState(EntityUid uid, BodyComponent body, ref ComponentGetState args) diff --git a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs index f3e2717d8e1e17..3ecc7897423f01 100644 --- a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs @@ -7,6 +7,6 @@ namespace Content.Shared.Medical.Wounds.Components; [Access(typeof(InjurySystem))] public sealed class TraumaInflictorComponent : Component { - [DataField("Trauma", required: true)] + [DataField("trauma", required: true)] public TraumaSpecifier Trauma = new TraumaSpecifier(); } diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 6f899e0a7e2821..898f3e8392c5b8 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -1,7 +1,9 @@ -using Content.Shared.Body.Systems; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; +using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -12,12 +14,26 @@ public sealed class InjurySystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedBodySystem _bodySystem = default!; + private readonly Dictionary _cachedInjuryTables = new(); public override void Initialize() { CacheData(null); _prototypeManager.PrototypesReloaded += CacheData; + + SubscribeLocalEvent(OnBodyAttacked); + } + + private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEvent args) + { + if (!TryComp(args.Used, out TraumaInflictorComponent? inflictor)) + return; + + foreach (var child in _bodySystem.GetBodyChildren(uid, component)) + { + TryApplyWound(child.Id, inflictor.Trauma); + } } private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) @@ -125,6 +141,7 @@ private Injury PickInjury(string traumaType, FixedPoint2 trauma) { var nextLevel = 1f; var levelFloor = 0f; + // TODO what if 0 is not in the dict var injuryId = _cachedInjuryTables[traumaType].Injuries[FixedPoint2.Zero]; foreach (var injuryData in _cachedInjuryTables[traumaType].Injuries) diff --git a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs index 2b7e9d3c07e688..a4904b0dbdc105 100644 --- a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs +++ b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs @@ -9,28 +9,26 @@ namespace Content.Shared.Medical.Wounds; [DataDefinition, NetSerializable, Serializable] public struct TraumaSpecifier { - [DataField("traumas", + [field: DataField("traumas", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - private readonly Dictionary _traumaData; - - public Dictionary TraumaValues => _traumaData; + public Dictionary TraumaValues { get; } public void ApplyModifiers(TraumaModifierSet modifiers) { foreach (var traumaType in modifiers.Coefficients.Keys) { - if (!_traumaData.ContainsKey(traumaType)) + if (!TraumaValues.ContainsKey(traumaType)) continue; - var temp = _traumaData[traumaType]; - _traumaData[traumaType] = temp with {Damage = temp.Damage * modifiers.Coefficients[traumaType]}; + var temp = TraumaValues[traumaType]; + TraumaValues[traumaType] = temp with {Damage = temp.Damage * modifiers.Coefficients[traumaType]}; } foreach (var traumaType in modifiers.FlatReduction.Keys) { - if (!_traumaData.ContainsKey(traumaType)) + if (!TraumaValues.ContainsKey(traumaType)) continue; - var temp = _traumaData[traumaType]; - _traumaData[traumaType] = temp with {Damage = temp.Damage - modifiers.FlatReduction[traumaType]}; + var temp = TraumaValues[traumaType]; + TraumaValues[traumaType] = temp with {Damage = temp.Damage - modifiers.FlatReduction[traumaType]}; } } @@ -38,10 +36,10 @@ public void ApplyPenModifiers(TraumaModifierSet modifiers) { foreach (var traumaType in modifiers.Coefficients.Keys) { - if (!_traumaData.ContainsKey(traumaType)) + if (!TraumaValues.ContainsKey(traumaType)) continue; - var temp = _traumaData[traumaType]; - _traumaData[traumaType] = temp with + var temp = TraumaValues[traumaType]; + TraumaValues[traumaType] = temp with { PenetrationChance = temp.PenetrationChance * modifiers.Coefficients[traumaType] }; @@ -49,10 +47,10 @@ public void ApplyPenModifiers(TraumaModifierSet modifiers) foreach (var traumaType in modifiers.FlatReduction.Keys) { - if (!_traumaData.ContainsKey(traumaType)) + if (!TraumaValues.ContainsKey(traumaType)) continue; - var temp = _traumaData[traumaType]; - _traumaData[traumaType] = temp with + var temp = TraumaValues[traumaType]; + TraumaValues[traumaType] = temp with { PenetrationChance = temp.PenetrationChance - modifiers.FlatReduction[traumaType] }; @@ -62,10 +60,10 @@ public void ApplyPenModifiers(TraumaModifierSet modifiers) [DataRecord, Serializable, NetSerializable] public record struct Trauma( - [field: DataField("Damage", required: true)] + [field: DataField("damage", required: true)] FixedPoint2 Damage, //Damage represents the amount of trauma dealt - [field: DataField("Chance", required: false)] + [field: DataField("chance", required: false)] FixedPoint2 PenetrationChance, - [field: DataField("penTraumaType", required: false, customTypeSerializer: typeof(PrototypeIdSerializer<>))] + [field: DataField("penTraumaType", required: false, customTypeSerializer: typeof(PrototypeIdSerializer))] string? PenTraumaType = null ); //Penetration represents how much this damage penetrates to hit child parts diff --git a/Resources/Prototypes/Body/Injuries/injuries.yml b/Resources/Prototypes/Body/Injuries/injuries.yml new file mode 100644 index 00000000000000..f03a9c6303a442 --- /dev/null +++ b/Resources/Prototypes/Body/Injuries/injuries.yml @@ -0,0 +1,7 @@ +- type: injury # TODO wounds before merge + id: Crack + name: Crack # TODO loc + description: Crack # TODO + pain: 1 + healthDamage: 5 + integrityDamage: 10 diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml index be8a9e4edc152b..83999493873adf 100644 --- a/Resources/Prototypes/Body/Parts/human.yml +++ b/Resources/Prototypes/Body/Parts/human.yml @@ -15,6 +15,13 @@ ents: [] - type: StaticPrice price: 100 + - type: Injurable # TODO wounds before merge + allowedTraumaTypes: + - Impact + maxHealth: 50 + health: 50 + maxStructure: 50 + structure: 50 - type: entity id: TorsoHuman diff --git a/Resources/Prototypes/Body/Traumas/trauma.yml b/Resources/Prototypes/Body/Traumas/trauma.yml new file mode 100644 index 00000000000000..55421ce35f3b1c --- /dev/null +++ b/Resources/Prototypes/Body/Traumas/trauma.yml @@ -0,0 +1,4 @@ +- type: traumaType # TODO wounds before merge + id: Impact + woundPool: + 0: Crack diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index dd70ca9b632091..72c3345484c5e9 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -20,6 +20,13 @@ tags: - CaptainSabre - type: DisarmMalus + - type: TraumaInflictor # TODO wounds before merge + trauma: + traumas: + Impact: + damage: 10 + chance: 0.25 + penTraumaType: Impact - type: entity name: katana From f2a286c608062ff57b308e8a5b78a5e0d933d199 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Thu, 17 Nov 2022 06:44:55 +0100 Subject: [PATCH 026/148] Roll pen chance with default type as pen type --- Content.Shared/Medical/Wounds/Systems/InjurySystem.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 898f3e8392c5b8..8a2995a9463aaf 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -70,11 +70,8 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) var type = trauma.PenTraumaType ?? traumaType; - if (trauma.PenTraumaType != null) - { - if (!_random.Prob(trauma.PenetrationChance.Float())) - continue; - } + if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) + continue; validTarget = GetValidInjurable(target, type); From 381e5c00db3e55eb7d2bbd5806f64a8c2a42a6c2 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Wed, 16 Nov 2022 22:52:07 -0800 Subject: [PATCH 027/148] removing Severity from wounds --- .../Wounds/Components/InjurableComponent.cs | 28 +++++++------------ .../Medical/Wounds/Systems/InjurySystem.cs | 2 +- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs index 2f449cc0361dfc..a4424ccad9550b 100644 --- a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs @@ -11,47 +11,39 @@ namespace Content.Shared.Medical.Wounds.Components; [Access(typeof(InjurySystem))] public sealed class InjurableComponent : Component { - [DataField("injuries")] - public List? Injuries; + [DataField("injuries")] public List? Injuries; [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet? AllowedTraumaTypes; - [DataField("traumaResistance")] - public TraumaModifierSet? TraumaResistance; + [DataField("traumaResistance")] public TraumaModifierSet? TraumaResistance; - [DataField("traumaPenResistance")] - public TraumaModifierSet? TraumaPenResistance; + [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; - [DataField("allowBleeds")] - public bool AllowBleeds = true; + [DataField("allowBleeds")] public bool AllowBleeds = true; - [DataField("appliesPain")] - public bool AppliesPain = true; + [DataField("appliesPain")] public bool AppliesPain = true; //How much health does this woundable have, when this reaches 0, it starts taking structural damage [DataField("maxHealth", required: true)] public FixedPoint2 MaxHealth; //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("health", required: true)] - public FixedPoint2 Health; + [DataField("health", required: true)] public FixedPoint2 Health; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(InjurySystem))] - [DataField("maxStructure", required: true)] + [Access(typeof(InjurySystem))] [DataField("maxStructure", required: true)] public FixedPoint2 MaxStructure; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(InjurySystem))] - [DataField("structure", required: true)] + [Access(typeof(InjurySystem))] [DataField("structure", required: true)] public FixedPoint2 StructuralPool; } [DataRecord, NetSerializable, Serializable] -public record struct Injury(string InjuryId, FixedPoint2 Severity, FixedPoint2 Healed, FixedPoint2 Bleed) +public record struct Injury(string InjuryId, FixedPoint2 Healed, FixedPoint2 Bleed) { - public Injury(string injuryId, FixedPoint2 severity) : this(injuryId, severity, FixedPoint2.Zero, FixedPoint2.Zero) + public Injury(string injuryId) : this(injuryId, FixedPoint2.Zero, FixedPoint2.Zero) { } } diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 898f3e8392c5b8..d76133be162550 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -156,7 +156,7 @@ private Injury PickInjury(string traumaType, FixedPoint2 trauma) levelFloor = injuryData.Key.Float(); } - return new Injury(injuryId, (trauma - levelFloor) / (nextLevel - trauma)); + return new Injury(injuryId); } private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacent(EntityUid target, From e9ac961c033499ab54606c999ef6f6a8e01c4de0 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Thu, 17 Nov 2022 08:01:10 +0100 Subject: [PATCH 028/148] Change TryPickInjury to get the first injury --- .../Medical/Wounds/Systems/InjurySystem.cs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 8a2995a9463aaf..ca57080b162c2f 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Shared.Medical.Wounds.Systems; @@ -64,9 +65,11 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) var injuryContainer = validTarget.Value.injurable; target = validTarget.Value.target; - success |= AddInjury(target, injuryContainer, traumaType, - PickInjury(traumaType, - ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage))); + var modifiers = ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage); + if (TryPickInjury(traumaType, modifiers, out var injury)) + { + success |= AddInjury(target, injuryContainer, traumaType, injury); + } var type = trauma.PenTraumaType ?? traumaType; @@ -81,8 +84,12 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) //Apply penetrating wounds injuryContainer = validTarget.Value.injurable; target = validTarget.Value.target; - success |= AddInjury(target, injuryContainer, type, - PickInjury(type, ApplyTraumaModifiers(type, injuryContainer.TraumaResistance, trauma.Damage))); + + modifiers = ApplyTraumaModifiers(type, injuryContainer.TraumaResistance, trauma.Damage); + if (TryPickInjury(type, modifiers, out injury)) + { + success |= AddInjury(target, injuryContainer, type, injury); + } } return success; @@ -97,7 +104,9 @@ public bool TryAddInjury(EntityUid target, string traumaType, Trauma trauma) var injuryContainer = checkedContainer.Value.injurable; target = checkedContainer.Value.target; - return AddInjury(target, injuryContainer, traumaType, PickInjury(traumaType, trauma.Damage)); + + return TryPickInjury(traumaType, trauma.Damage, out var injury) && + AddInjury(target, injuryContainer, traumaType, injury); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -134,12 +143,18 @@ private bool AddInjury(EntityUid target, InjurableComponent injuryContainer, str return childInjuryContainer; } - private Injury PickInjury(string traumaType, FixedPoint2 trauma) + private bool TryPickInjury(string traumaType, FixedPoint2 trauma, out Injury injury) { var nextLevel = 1f; var levelFloor = 0f; // TODO what if 0 is not in the dict - var injuryId = _cachedInjuryTables[traumaType].Injuries[FixedPoint2.Zero]; + if (!_cachedInjuryTables[traumaType].Injuries.TryFirstOrNull(out var cachedInjury)) + { + injury = default; + return false; + } + + var injuryId = cachedInjury.Value.Value; foreach (var injuryData in _cachedInjuryTables[traumaType].Injuries) { @@ -153,7 +168,8 @@ private Injury PickInjury(string traumaType, FixedPoint2 trauma) levelFloor = injuryData.Key.Float(); } - return new Injury(injuryId, (trauma - levelFloor) / (nextLevel - trauma)); + injury = new Injury(injuryId, (trauma - levelFloor) / (nextLevel - trauma)); + return true; } private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacent(EntityUid target, From 5fcc42c738e33b92b8e483ec45eb4b80bd46e7c7 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Thu, 17 Nov 2022 10:11:46 +0100 Subject: [PATCH 029/148] Add slash injuries, remove TraumaSpecifier, cleanup --- .../Wounds/Components/InjurableComponent.cs | 6 +- .../Components/TraumaInflicterComponent.cs | 14 +++++ .../Components/TraumaInflictorComponent.cs | 12 ---- .../Wounds/Prototypes/InjuryPrototype.cs | 14 ++--- ...umaTypePrototype.cs => TraumaPrototype.cs} | 10 ++-- .../Medical/Wounds/Systems/InjurySystem.cs | 43 +++++++++----- .../Medical/Wounds/TraumaModifierSet.cs | 14 +---- .../Medical/Wounds/TraumaSpecifier.cs | 59 +------------------ Resources/Prototypes/Body/Injuries/blunt.yml | 5 ++ .../Prototypes/Body/Injuries/injuries.yml | 7 --- Resources/Prototypes/Body/Parts/human.yml | 2 +- Resources/Prototypes/Body/Traumas/blunt.yml | 9 +++ Resources/Prototypes/Body/Traumas/slash.yml | 33 +++++++++++ Resources/Prototypes/Body/Traumas/trauma.yml | 4 -- .../Entities/Objects/Weapons/Melee/sword.yml | 13 ++-- 15 files changed, 112 insertions(+), 133 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs delete mode 100644 Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs rename Content.Shared/Medical/Wounds/Prototypes/{TraumaTypePrototype.cs => TraumaPrototype.cs} (69%) create mode 100644 Resources/Prototypes/Body/Injuries/blunt.yml delete mode 100644 Resources/Prototypes/Body/Injuries/injuries.yml create mode 100644 Resources/Prototypes/Body/Traumas/blunt.yml create mode 100644 Resources/Prototypes/Body/Traumas/slash.yml delete mode 100644 Resources/Prototypes/Body/Traumas/trauma.yml diff --git a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs index a4424ccad9550b..86410b467df9c4 100644 --- a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs @@ -13,17 +13,13 @@ public sealed class InjurableComponent : Component { [DataField("injuries")] public List? Injuries; - [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet? AllowedTraumaTypes; [DataField("traumaResistance")] public TraumaModifierSet? TraumaResistance; [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; - [DataField("allowBleeds")] public bool AllowBleeds = true; - - [DataField("appliesPain")] public bool AppliesPain = true; - //How much health does this woundable have, when this reaches 0, it starts taking structural damage [DataField("maxHealth", required: true)] public FixedPoint2 MaxHealth; diff --git a/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs b/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs new file mode 100644 index 00000000000000..e71faa01e02655 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Medical.Wounds.Prototypes; +using Content.Shared.Medical.Wounds.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; + +namespace Content.Shared.Medical.Wounds.Components; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(InjurySystem))] +public sealed class TraumaInflicterComponent : Component +{ + [DataField("traumas", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public readonly Dictionary Traumas = new(); +} diff --git a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs b/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs deleted file mode 100644 index 3ecc7897423f01..00000000000000 --- a/Content.Shared/Medical/Wounds/Components/TraumaInflictorComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Content.Shared.Medical.Wounds.Systems; -using Robust.Shared.GameStates; - -namespace Content.Shared.Medical.Wounds.Components; - -[RegisterComponent, NetworkedComponent] -[Access(typeof(InjurySystem))] -public sealed class TraumaInflictorComponent : Component -{ - [DataField("trauma", required: true)] - public TraumaSpecifier Trauma = new TraumaSpecifier(); -} diff --git a/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs index 035f021d40ba80..2a26a3a20ddf4a 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs @@ -8,26 +8,22 @@ public sealed class InjuryPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; - [DataField("allowStacking")] - public readonly bool AllowStacking = true; - [DataField("name", required: true)] public readonly string DisplayName = string.Empty; [DataField("description", required: true)] public readonly string Description = string.Empty; - [DataField("pain", required: true)] - public readonly float Pain; - - [DataField("bleedRate", required: false)] - public readonly float Bleed; - [DataField("healthDamage")] public readonly FixedPoint2 HealthDamage; [DataField("integrityDamage")] public readonly FixedPoint2 IntegrityDamage; + + [DataField("bleed")] + public readonly FixedPoint2 Bleed; + + // TODO pain } public static class A diff --git a/Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs similarity index 69% rename from Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs rename to Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs index 2ddfb3ce113843..abe49343a3d60d 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/TraumaTypePrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs @@ -4,14 +4,16 @@ namespace Content.Shared.Medical.Wounds.Prototypes; -[Prototype("traumaType")] -public sealed class TraumaTypePrototype : IPrototype +[Prototype("trauma")] +public sealed class TraumaPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; //Note: these should be defined in order of severity! //list of possible wounds sorted by their trauma cutoffs - [DataField("woundPool", required: true, + [DataField("wounds", required: true, customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] - public SortedDictionary WoundPool { get; init; } = new(); + public SortedDictionary Wounds { get; init; } = new(); + + // TODO wounds wound cap } diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 6072db0eeeec44..23dc7f0cca237a 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -28,12 +28,12 @@ public override void Initialize() private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEvent args) { - if (!TryComp(args.Used, out TraumaInflictorComponent? inflictor)) + if (!TryComp(args.Used, out TraumaInflicterComponent? inflicter)) return; foreach (var child in _bodySystem.GetBodyChildren(uid, component)) { - TryApplyWound(child.Id, inflictor.Trauma); + TryApplyWound(child.Id, inflicter); } } @@ -41,7 +41,7 @@ private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) { _cachedInjuryTables.Clear(); - foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) + foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) { _cachedInjuryTables.Add(traumaType.ID, new InjuryTable(traumaType)); } @@ -52,10 +52,10 @@ public IReadOnlyDictionary GetInjuryTable(string traumaType return _cachedInjuryTables[traumaType].Injuries; } - public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) + public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) { var success = false; - foreach (var (traumaType, trauma) in traumaSpec.TraumaValues) + foreach (var (traumaType, trauma) in inflicter.Traumas) { var validTarget = GetValidInjurable(target, traumaType); @@ -67,9 +67,7 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) var modifiers = ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage); if (TryPickInjury(traumaType, modifiers, out var injury)) - { success |= AddInjury(target, injuryContainer, traumaType, injury); - } var type = trauma.PenTraumaType ?? traumaType; @@ -87,15 +85,13 @@ public bool TryApplyWound(EntityUid target, TraumaSpecifier traumaSpec) modifiers = ApplyTraumaModifiers(type, injuryContainer.TraumaResistance, trauma.Damage); if (TryPickInjury(type, modifiers, out injury)) - { success |= AddInjury(target, injuryContainer, type, injury); - } } return success; } - public bool TryAddInjury(EntityUid target, string traumaType, Trauma trauma) + public bool TryAddInjury(EntityUid target, string traumaType, TraumaDamage damage) { var checkedContainer = GetValidInjurable(target, traumaType); @@ -105,7 +101,7 @@ public bool TryAddInjury(EntityUid target, string traumaType, Trauma trauma) var injuryContainer = checkedContainer.Value.injurable; target = checkedContainer.Value.target; - return TryPickInjury(traumaType, trauma.Damage, out var injury) && + return TryPickInjury(traumaType, damage.Damage, out var injury) && AddInjury(target, injuryContainer, traumaType, injury); } @@ -114,10 +110,10 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m if (!modifiers.HasValue) return damage; - if (modifiers.Value.TryGetCoefficientForTraumaType(traumaType, out var coeff)) - damage *= coeff; + if (modifiers.Value.Coefficients.TryGetValue(traumaType, out var coefficient)) + damage *= coefficient; - if (modifiers.Value.TryGetFlatReductionForTraumaType(traumaType, out var reduction)) + if (modifiers.Value.FlatReduction.TryGetValue(traumaType, out var reduction)) damage -= reduction; return damage; @@ -205,6 +201,21 @@ private bool TryPickInjury(string traumaType, FixedPoint2 trauma, out Injury inj return null; } + + public FixedPoint2 ApplyModifiers(string type, FixedPoint2 damage, TraumaModifierSet modifiers) + { + if (modifiers.Coefficients.TryGetValue(type, out var coefficient)) + { + damage *= coefficient; + } + + if (modifiers.FlatReduction.TryGetValue(type, out var flat)) + { + damage -= flat; + } + + return FixedPoint2.Max(damage, FixedPoint2.Zero); + } } public readonly struct InjuryTable @@ -212,8 +223,8 @@ public readonly struct InjuryTable //we do some defensive coding public readonly SortedDictionary Injuries; - public InjuryTable(TraumaTypePrototype traumaProto) + public InjuryTable(TraumaPrototype traumaProto) { - Injuries = traumaProto.WoundPool; + Injuries = traumaProto.Wounds; } } diff --git a/Content.Shared/Medical/Wounds/TraumaModifierSet.cs b/Content.Shared/Medical/Wounds/TraumaModifierSet.cs index d33721dc070afe..206e3da139976b 100644 --- a/Content.Shared/Medical/Wounds/TraumaModifierSet.cs +++ b/Content.Shared/Medical/Wounds/TraumaModifierSet.cs @@ -10,23 +10,13 @@ namespace Content.Shared.Medical.Wounds; public struct TraumaModifierSet { [DataField("coefficients", - customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] public Dictionary Coefficients = new(); [DataField("flatReductions", - customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] public Dictionary FlatReduction = new(); - public bool TryGetCoefficientForTraumaType(string traumaType, out FixedPoint2 coefficient) - { - return Coefficients.TryGetValue(traumaType, out coefficient); - } - - public bool TryGetFlatReductionForTraumaType(string traumaType, out FixedPoint2 reduction) - { - return Coefficients.TryGetValue(traumaType, out reduction); - } - public TraumaModifierSet() { } diff --git a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs index a4904b0dbdc105..5fb1d46d0bb053 100644 --- a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs +++ b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs @@ -2,68 +2,15 @@ using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Medical.Wounds; -[DataDefinition, NetSerializable, Serializable] -public struct TraumaSpecifier -{ - [field: DataField("traumas", - customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] - public Dictionary TraumaValues { get; } - - public void ApplyModifiers(TraumaModifierSet modifiers) - { - foreach (var traumaType in modifiers.Coefficients.Keys) - { - if (!TraumaValues.ContainsKey(traumaType)) - continue; - var temp = TraumaValues[traumaType]; - TraumaValues[traumaType] = temp with {Damage = temp.Damage * modifiers.Coefficients[traumaType]}; - } - - foreach (var traumaType in modifiers.FlatReduction.Keys) - { - if (!TraumaValues.ContainsKey(traumaType)) - continue; - var temp = TraumaValues[traumaType]; - TraumaValues[traumaType] = temp with {Damage = temp.Damage - modifiers.FlatReduction[traumaType]}; - } - } - - public void ApplyPenModifiers(TraumaModifierSet modifiers) - { - foreach (var traumaType in modifiers.Coefficients.Keys) - { - if (!TraumaValues.ContainsKey(traumaType)) - continue; - var temp = TraumaValues[traumaType]; - TraumaValues[traumaType] = temp with - { - PenetrationChance = temp.PenetrationChance * modifiers.Coefficients[traumaType] - }; - } - - foreach (var traumaType in modifiers.FlatReduction.Keys) - { - if (!TraumaValues.ContainsKey(traumaType)) - continue; - var temp = TraumaValues[traumaType]; - TraumaValues[traumaType] = temp with - { - PenetrationChance = temp.PenetrationChance - modifiers.FlatReduction[traumaType] - }; - } - } -} - [DataRecord, Serializable, NetSerializable] -public record struct Trauma( +public record struct TraumaDamage( [field: DataField("damage", required: true)] FixedPoint2 Damage, //Damage represents the amount of trauma dealt - [field: DataField("chance", required: false)] + [field: DataField("penChance", required: false)] FixedPoint2 PenetrationChance, - [field: DataField("penTraumaType", required: false, customTypeSerializer: typeof(PrototypeIdSerializer))] + [field: DataField("penType", required: false, customTypeSerializer: typeof(PrototypeIdSerializer))] string? PenTraumaType = null ); //Penetration represents how much this damage penetrates to hit child parts diff --git a/Resources/Prototypes/Body/Injuries/blunt.yml b/Resources/Prototypes/Body/Injuries/blunt.yml new file mode 100644 index 00000000000000..6da401eedffe38 --- /dev/null +++ b/Resources/Prototypes/Body/Injuries/blunt.yml @@ -0,0 +1,5 @@ +- type: injury # TODO wounds before merge + id: Bruise + name: Bruise # TODO loc + description: Bruise # TODO + healthDamage: 1 diff --git a/Resources/Prototypes/Body/Injuries/injuries.yml b/Resources/Prototypes/Body/Injuries/injuries.yml deleted file mode 100644 index f03a9c6303a442..00000000000000 --- a/Resources/Prototypes/Body/Injuries/injuries.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: injury # TODO wounds before merge - id: Crack - name: Crack # TODO loc - description: Crack # TODO - pain: 1 - healthDamage: 5 - integrityDamage: 10 diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml index 83999493873adf..1a9221ed799b22 100644 --- a/Resources/Prototypes/Body/Parts/human.yml +++ b/Resources/Prototypes/Body/Parts/human.yml @@ -17,7 +17,7 @@ price: 100 - type: Injurable # TODO wounds before merge allowedTraumaTypes: - - Impact + - Slash maxHealth: 50 health: 50 maxStructure: 50 diff --git a/Resources/Prototypes/Body/Traumas/blunt.yml b/Resources/Prototypes/Body/Traumas/blunt.yml new file mode 100644 index 00000000000000..58fc0e288068ce --- /dev/null +++ b/Resources/Prototypes/Body/Traumas/blunt.yml @@ -0,0 +1,9 @@ +- type: trauma # TODO wounds before merge + id: Slash + wounds: + 1: Bruise + 5: Scrape + 10: Abrasion + 20: Cut + 30: Laceration + 50: Avulsion diff --git a/Resources/Prototypes/Body/Traumas/slash.yml b/Resources/Prototypes/Body/Traumas/slash.yml new file mode 100644 index 00000000000000..28bd4cc2dac7e1 --- /dev/null +++ b/Resources/Prototypes/Body/Traumas/slash.yml @@ -0,0 +1,33 @@ +- type: injury # TODO wounds before merge + id: Scrape + name: Scrape # TODO loc + description: Scrape # TODO + healthDamage: 3 + # TODO wounds bleed + +- type: injury # TODO wounds before merge + id: Abrasion + name: Abrasion # TODO loc + description: Abrasion # TODO + healthDamage: 7 + +- type: injury # TODO wounds before merge + id: Cut + name: Cut # TODO loc + description: Cut # TODO + healthDamage: 14 + integrityDamage: 5 + +- type: injury # TODO wounds before merge + id: Laceration + name: Laceration # TODO loc + description: Laceration # TODO + healthDamage: 25 + integrityDamage: 20 + +- type: injury # TODO wounds before merge + id: Avulsion + name: Avulsion # TODO loc + description: Avulsion # TODO + healthDamage: 40 + integrityDamage: 40 diff --git a/Resources/Prototypes/Body/Traumas/trauma.yml b/Resources/Prototypes/Body/Traumas/trauma.yml deleted file mode 100644 index 55421ce35f3b1c..00000000000000 --- a/Resources/Prototypes/Body/Traumas/trauma.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: traumaType # TODO wounds before merge - id: Impact - woundPool: - 0: Crack diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 72c3345484c5e9..514a67e94b000a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -20,13 +20,12 @@ tags: - CaptainSabre - type: DisarmMalus - - type: TraumaInflictor # TODO wounds before merge - trauma: - traumas: - Impact: - damage: 10 - chance: 0.25 - penTraumaType: Impact + - type: TraumaInflicter # TODO wounds before merge + traumas: + Slash: + damage: 10 + penChance: 0.3 + penModifier: 0.2 - type: entity name: katana From 53ea81b947db29d28d7714283991499f701e1d91 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Thu, 17 Nov 2022 10:15:46 +0100 Subject: [PATCH 030/148] Pick random part instead of applying to all --- Content.Shared/Medical/Wounds/Systems/InjurySystem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs index 23dc7f0cca237a..84d39c32454019 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs @@ -1,4 +1,5 @@ -using Content.Shared.Body.Components; +using System.Linq; +using Content.Shared.Body.Components; using Content.Shared.Body.Systems; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; @@ -31,10 +32,9 @@ private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEven if (!TryComp(args.Used, out TraumaInflicterComponent? inflicter)) return; - foreach (var child in _bodySystem.GetBodyChildren(uid, component)) - { - TryApplyWound(child.Id, inflicter); - } + var parts = _bodySystem.GetBodyChildren(uid, component).ToList(); + var part = _random.Pick(parts); + TryApplyWound(part.Id, inflicter); } private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) From 33e8ae10f741845b376201fad0e9a5bbe19ace55 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Thu, 17 Nov 2022 21:07:17 +0100 Subject: [PATCH 031/148] Rename injury to wound --- Content.Shared/Body/Part/BodySkinComponent.cs | 2 +- .../Components/TraumaInflicterComponent.cs | 2 +- ...ableComponent.cs => WoundableComponent.cs} | 14 +- .../Wounds/Prototypes/TraumaPrototype.cs | 2 +- .../{InjuryPrototype.cs => WoundPrototype.cs} | 4 +- .../{InjurySystem.cs => WoundSystem.cs} | 132 ++++++++---------- Resources/Prototypes/Body/Parts/human.yml | 10 +- Resources/Prototypes/Body/Traumas/slash.yml | 10 +- .../Body/{Injuries => Wounds}/blunt.yml | 2 +- RobustToolbox | 2 +- SpaceStation14.sln.DotSettings | 1 + 11 files changed, 83 insertions(+), 98 deletions(-) rename Content.Shared/Medical/Wounds/Components/{InjurableComponent.cs => WoundableComponent.cs} (75%) rename Content.Shared/Medical/Wounds/Prototypes/{InjuryPrototype.cs => WoundPrototype.cs} (90%) rename Content.Shared/Medical/Wounds/Systems/{InjurySystem.cs => WoundSystem.cs} (50%) rename Resources/Prototypes/Body/{Injuries => Wounds}/blunt.yml (65%) diff --git a/Content.Shared/Body/Part/BodySkinComponent.cs b/Content.Shared/Body/Part/BodySkinComponent.cs index 0f59cfd3b40d6c..a9eafa0083ac97 100644 --- a/Content.Shared/Body/Part/BodySkinComponent.cs +++ b/Content.Shared/Body/Part/BodySkinComponent.cs @@ -12,7 +12,7 @@ namespace Content.Shared.Body.Part; [Access(typeof(SharedBodySystem))] public sealed class BodySkinComponent : Component { - [Access(typeof(InjurySystem), typeof(SharedBodySystem), Other = AccessPermissions.Read)] [DataField("skinLayers")] + [Access(typeof(WoundSystem), typeof(SharedBodySystem), Other = AccessPermissions.Read)] [DataField("skinLayers")] public List SkinLayers = new(); public SkinlayerData? PrimaryLayer diff --git a/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs b/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs index e71faa01e02655..83ec9dce170529 100644 --- a/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/TraumaInflicterComponent.cs @@ -6,7 +6,7 @@ namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent, NetworkedComponent] -[Access(typeof(InjurySystem))] +[Access(typeof(WoundSystem))] public sealed class TraumaInflicterComponent : Component { [DataField("traumas", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] diff --git a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs similarity index 75% rename from Content.Shared/Medical/Wounds/Components/InjurableComponent.cs rename to Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 86410b467df9c4..eb2dca228319f5 100644 --- a/Content.Shared/Medical/Wounds/Components/InjurableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -8,10 +8,10 @@ namespace Content.Shared.Medical.Wounds.Components; [RegisterComponent, NetworkedComponent] -[Access(typeof(InjurySystem))] -public sealed class InjurableComponent : Component +[Access(typeof(WoundSystem))] +public sealed class WoundableComponent : Component { - [DataField("injuries")] public List? Injuries; + [DataField("wounds")] public List? Wounds; [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet? AllowedTraumaTypes; @@ -28,18 +28,18 @@ public sealed class InjurableComponent : Component [DataField("health", required: true)] public FixedPoint2 Health; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(InjurySystem))] [DataField("maxStructure", required: true)] + [Access(typeof(WoundSystem))] [DataField("maxStructure", required: true)] public FixedPoint2 MaxStructure; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(InjurySystem))] [DataField("structure", required: true)] + [Access(typeof(WoundSystem))] [DataField("structure", required: true)] public FixedPoint2 StructuralPool; } [DataRecord, NetSerializable, Serializable] -public record struct Injury(string InjuryId, FixedPoint2 Healed, FixedPoint2 Bleed) +public record struct Wound(string WoundId, FixedPoint2 Healed, FixedPoint2 Bleed) { - public Injury(string injuryId) : this(injuryId, FixedPoint2.Zero, FixedPoint2.Zero) + public Wound(string woundId) : this(woundId, FixedPoint2.Zero, FixedPoint2.Zero) { } } diff --git a/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs index abe49343a3d60d..4cbc47491ab0e1 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs @@ -12,7 +12,7 @@ public sealed class TraumaPrototype : IPrototype //Note: these should be defined in order of severity! //list of possible wounds sorted by their trauma cutoffs [DataField("wounds", required: true, - customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] public SortedDictionary Wounds { get; init; } = new(); // TODO wounds wound cap diff --git a/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs similarity index 90% rename from Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs rename to Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs index 2a26a3a20ddf4a..5e64c9e9d54aa5 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/InjuryPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs @@ -3,8 +3,8 @@ namespace Content.Shared.Medical.Wounds.Prototypes; -[Prototype("injury")] -public sealed class InjuryPrototype : IPrototype +[Prototype("wound")] +public sealed class WoundPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; diff --git a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs similarity index 50% rename from Content.Shared/Medical/Wounds/Systems/InjurySystem.cs rename to Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 84d39c32454019..7c8efe158f5bf4 100644 --- a/Content.Shared/Medical/Wounds/Systems/InjurySystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -7,17 +7,16 @@ using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; namespace Content.Shared.Medical.Wounds.Systems; -public sealed class InjurySystem : EntitySystem +public sealed class WoundSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedBodySystem _bodySystem = default!; - private readonly Dictionary _cachedInjuryTables = new(); + private readonly Dictionary _cachedWounds = new(); public override void Initialize() { @@ -39,70 +38,65 @@ private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEven private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) { - _cachedInjuryTables.Clear(); + _cachedWounds.Clear(); foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) { - _cachedInjuryTables.Add(traumaType.ID, new InjuryTable(traumaType)); + _cachedWounds.Add(traumaType.ID, new WoundTable(traumaType)); } } - public IReadOnlyDictionary GetInjuryTable(string traumaType) - { - return _cachedInjuryTables[traumaType].Injuries; - } - public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) { var success = false; foreach (var (traumaType, trauma) in inflicter.Traumas) { - var validTarget = GetValidInjurable(target, traumaType); + var validTarget = GetValidWoundable(target, traumaType); if (!validTarget.HasValue) return false; - var injuryContainer = validTarget.Value.injurable; - target = validTarget.Value.target; + var woundContainer = validTarget.Value.Woundable; + target = validTarget.Value.Target; - var modifiers = ApplyTraumaModifiers(traumaType, injuryContainer.TraumaResistance, trauma.Damage); - if (TryPickInjury(traumaType, modifiers, out var injury)) - success |= AddInjury(target, injuryContainer, traumaType, injury); + var modifiers = ApplyTraumaModifiers(traumaType, woundContainer.TraumaResistance, trauma.Damage); + if (TryPickWound(traumaType, modifiers, out var wound)) + success |= AddWound(target, woundContainer, traumaType, wound); var type = trauma.PenTraumaType ?? traumaType; if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) continue; - validTarget = GetValidInjurable(target, type); + validTarget = GetValidWoundable(target, type); if (!validTarget.HasValue) continue; //Apply penetrating wounds - injuryContainer = validTarget.Value.injurable; - target = validTarget.Value.target; + woundContainer = validTarget.Value.Woundable; + target = validTarget.Value.Target; - modifiers = ApplyTraumaModifiers(type, injuryContainer.TraumaResistance, trauma.Damage); - if (TryPickInjury(type, modifiers, out injury)) - success |= AddInjury(target, injuryContainer, type, injury); + modifiers = ApplyTraumaModifiers(type, woundContainer.TraumaResistance, trauma.Damage); + if (TryPickWound(type, modifiers, out wound)) + success |= AddWound(target, woundContainer, type, wound); } return success; } - public bool TryAddInjury(EntityUid target, string traumaType, TraumaDamage damage) + public bool TryAddWound(EntityUid target, string traumaType, TraumaDamage damage) { - var checkedContainer = GetValidInjurable(target, traumaType); + var checkedContainer = GetValidWoundable(target, traumaType); if (checkedContainer == null) return false; - var injuryContainer = checkedContainer.Value.injurable; - target = checkedContainer.Value.target; + var woundContainer = checkedContainer.Value.Woundable; + target = checkedContainer.Value.Target; - return TryPickInjury(traumaType, damage.Damage, out var injury) && - AddInjury(target, injuryContainer, traumaType, injury); + return TryPickWound(traumaType, damage.Damage, out var wound) && + AddWound(target, woundContainer, traumaType, wound); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -119,69 +113,55 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m return damage; } - private bool AddInjury(EntityUid target, InjurableComponent injuryContainer, string traumaType, Injury injury) + private bool AddWound(EntityUid target, WoundableComponent woundContainer, string traumaType, Wound wound) { - injuryContainer.Injuries ??= new List(); - injuryContainer.Injuries.Add(injury); + woundContainer.Wounds ??= new List(); + woundContainer.Wounds.Add(wound); return true; } - private (InjurableComponent injurable, EntityUid target)? GetValidInjurable(EntityUid target, string traumaType) + private (WoundableComponent Woundable, EntityUid Target)? GetValidWoundable(EntityUid target, string traumaType) { - if (!TryComp(target, out var injuryContainer)) + if (!TryComp(target, out var woundable)) return null; - if (injuryContainer.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (injuryContainer, target); + if (woundable.AllowedTraumaTypes?.Contains(traumaType) ?? false) + return (woundable, target); - var childInjuryContainer = FindValidInjurableInAdjacent(target, traumaType); - return childInjuryContainer; + var adjacentWoundable = FindValidWoundableInAdjacent(target, traumaType); + return adjacentWoundable; } - private bool TryPickInjury(string traumaType, FixedPoint2 trauma, out Injury injury) + private bool TryPickWound(string traumaType, FixedPoint2 trauma, out Wound wound) { - var nextLevel = 1f; - var levelFloor = 0f; - // TODO what if 0 is not in the dict - if (!_cachedInjuryTables[traumaType].Injuries.TryFirstOrNull(out var cachedInjury)) + foreach (var woundData in _cachedWounds[traumaType].HighestToLowest()) { - injury = default; - return false; - } - - var injuryId = cachedInjury.Value.Value; + if (woundData.Key > trauma) + continue; - foreach (var injuryData in _cachedInjuryTables[traumaType].Injuries) - { - if (injuryData.Key > trauma) - { - nextLevel = injuryData.Key.Float(); - break; - } - - injuryId = injuryData.Value; - levelFloor = injuryData.Key.Float(); + wound = new Wound(woundData.Value); + return true; } - injury = new Injury(injuryId); - return true; + wound = default; + return false; } - private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacent(EntityUid target, + private (WoundableComponent Woundable, EntityUid Target)? FindValidWoundableInAdjacent(EntityUid target, string traumaType) { //search all the children in the body for a part that accepts the wound we want to apply //checks organs first, then recursively checks child parts and their organs. - var foundContainer = FindValidInjurableInOrgans(target, traumaType) ?? - FindValidInjurableInAdjacentParts(target, traumaType); + var foundContainer = FindValidWoundableInOrgans(target, traumaType) ?? + FindValidWoundableInAdjacentParts(target, traumaType); return foundContainer; } - private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInAdjacentParts(EntityUid target, + private (WoundableComponent Woundable, EntityUid Target)? FindValidWoundableInAdjacentParts(EntityUid target, string traumaType) { - foreach (var data in _bodySystem.GetBodyPartAdjacentPartsComponents(target)) + foreach (var data in _bodySystem.GetBodyPartAdjacentPartsComponents(target)) { if (data.Component.AllowedTraumaTypes?.Contains(traumaType) ?? false) return (data.Component, target); @@ -190,10 +170,10 @@ private bool TryPickInjury(string traumaType, FixedPoint2 trauma, out Injury inj return null; } - private (InjurableComponent Injurable, EntityUid Target)? FindValidInjurableInOrgans(EntityUid target, + private (WoundableComponent Woundable, EntityUid Target)? FindValidWoundableInOrgans(EntityUid target, string traumaType) { - foreach (var data in _bodySystem.GetBodyPartOrganComponents(target)) + foreach (var data in _bodySystem.GetBodyPartOrganComponents(target)) { if (data.Comp.AllowedTraumaTypes?.Contains(traumaType) ?? false) return (data.Comp, target); @@ -216,15 +196,19 @@ public FixedPoint2 ApplyModifiers(string type, FixedPoint2 damage, TraumaModifie return FixedPoint2.Max(damage, FixedPoint2.Zero); } -} - -public readonly struct InjuryTable -{ - //we do some defensive coding - public readonly SortedDictionary Injuries; - public InjuryTable(TraumaPrototype traumaProto) + private readonly struct WoundTable { - Injuries = traumaProto.Wounds; + private readonly SortedDictionary _wounds; + + public WoundTable(TraumaPrototype trauma) + { + _wounds = trauma.Wounds; + } + + public IEnumerable> HighestToLowest() + { + return _wounds.Reverse(); + } } } diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml index 1a9221ed799b22..d56fb67c16f9a7 100644 --- a/Resources/Prototypes/Body/Parts/human.yml +++ b/Resources/Prototypes/Body/Parts/human.yml @@ -15,13 +15,13 @@ ents: [] - type: StaticPrice price: 100 - - type: Injurable # TODO wounds before merge + - type: Woundable # TODO wounds before merge allowedTraumaTypes: - Slash - maxHealth: 50 - health: 50 - maxStructure: 50 - structure: 50 + maxHealth: 100 + health: 100 + maxStructure: 100 + structure: 100 - type: entity id: TorsoHuman diff --git a/Resources/Prototypes/Body/Traumas/slash.yml b/Resources/Prototypes/Body/Traumas/slash.yml index 28bd4cc2dac7e1..32120c2f5fd48a 100644 --- a/Resources/Prototypes/Body/Traumas/slash.yml +++ b/Resources/Prototypes/Body/Traumas/slash.yml @@ -1,31 +1,31 @@ -- type: injury # TODO wounds before merge +- type: wound # TODO wounds before merge id: Scrape name: Scrape # TODO loc description: Scrape # TODO healthDamage: 3 # TODO wounds bleed -- type: injury # TODO wounds before merge +- type: wound # TODO wounds before merge id: Abrasion name: Abrasion # TODO loc description: Abrasion # TODO healthDamage: 7 -- type: injury # TODO wounds before merge +- type: wound # TODO wounds before merge id: Cut name: Cut # TODO loc description: Cut # TODO healthDamage: 14 integrityDamage: 5 -- type: injury # TODO wounds before merge +- type: wound # TODO wounds before merge id: Laceration name: Laceration # TODO loc description: Laceration # TODO healthDamage: 25 integrityDamage: 20 -- type: injury # TODO wounds before merge +- type: wound # TODO wounds before merge id: Avulsion name: Avulsion # TODO loc description: Avulsion # TODO diff --git a/Resources/Prototypes/Body/Injuries/blunt.yml b/Resources/Prototypes/Body/Wounds/blunt.yml similarity index 65% rename from Resources/Prototypes/Body/Injuries/blunt.yml rename to Resources/Prototypes/Body/Wounds/blunt.yml index 6da401eedffe38..0ab0062cb05f57 100644 --- a/Resources/Prototypes/Body/Injuries/blunt.yml +++ b/Resources/Prototypes/Body/Wounds/blunt.yml @@ -1,4 +1,4 @@ -- type: injury # TODO wounds before merge +- type: wound # TODO wounds before merge id: Bruise name: Bruise # TODO loc description: Bruise # TODO diff --git a/RobustToolbox b/RobustToolbox index bb137d69a2d78c..bf6928703f0644 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit bb137d69a2d78cd6a9a984f2b3a419d84fd669d9 +Subproject commit bf6928703f0644c25c55ca268752fca9f602aaa5 diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 1905f9f55e3b03..cac24ec3ca3589 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -618,6 +618,7 @@ public sealed class $CLASS$ : Shared$CLASS$ { True True True + True True True True From d746bb728cd1dd5e9440dfe8d6fbc106d4b30ec9 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Fri, 18 Nov 2022 00:42:22 +0100 Subject: [PATCH 032/148] Change wounds to be entities --- .../Wounds/Components/WoundComponent.cs | 13 +++ .../Wounds/Components/WoundableComponent.cs | 11 --- .../Medical/Wounds/Systems/WoundSystem.cs | 90 +++++++++---------- Resources/Prototypes/Body/Traumas/blunt.yml | 9 -- Resources/Prototypes/Body/Traumas/slash.yml | 42 ++------- Resources/Prototypes/Body/Wounds/blunt.yml | 8 +- Resources/Prototypes/Body/Wounds/slash.yml | 43 +++++++++ 7 files changed, 111 insertions(+), 105 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Components/WoundComponent.cs delete mode 100644 Resources/Prototypes/Body/Traumas/blunt.yml create mode 100644 Resources/Prototypes/Body/Wounds/slash.yml diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs new file mode 100644 index 00000000000000..08e2749f722b1c --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Medical.Wounds.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Medical.Wounds.Components; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(WoundSystem))] +public sealed class WoundComponent : Component +{ + [DataField("healthDamage")] public int HealthDamage; + + [DataField("integrityDamage")] public int IntegrityDamage; +} diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index eb2dca228319f5..70ca8a877b0dc4 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -2,7 +2,6 @@ using Content.Shared.Medical.Wounds.Prototypes; using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.GameStates; -using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Medical.Wounds.Components; @@ -11,8 +10,6 @@ namespace Content.Shared.Medical.Wounds.Components; [Access(typeof(WoundSystem))] public sealed class WoundableComponent : Component { - [DataField("wounds")] public List? Wounds; - [DataField("allowedTraumaTypes", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet? AllowedTraumaTypes; @@ -35,11 +32,3 @@ public sealed class WoundableComponent : Component [Access(typeof(WoundSystem))] [DataField("structure", required: true)] public FixedPoint2 StructuralPool; } - -[DataRecord, NetSerializable, Serializable] -public record struct Wound(string WoundId, FixedPoint2 Healed, FixedPoint2 Bleed) -{ - public Wound(string woundId) : this(woundId, FixedPoint2.Zero, FixedPoint2.Zero) - { - } -} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 7c8efe158f5bf4..f0bee1ef92517c 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,10 +1,12 @@ using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; +using Content.Shared.Coordinates; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -12,9 +14,13 @@ namespace Content.Shared.Medical.Wounds.Systems; public sealed class WoundSystem : EntitySystem { + private const string WoundContainerId = "WoundableComponentWounds"; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedBodySystem _bodySystem = default!; + + [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; private readonly Dictionary _cachedWounds = new(); @@ -31,7 +37,7 @@ private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEven if (!TryComp(args.Used, out TraumaInflicterComponent? inflicter)) return; - var parts = _bodySystem.GetBodyChildren(uid, component).ToList(); + var parts = _body.GetBodyChildren(uid, component).ToList(); var part = _random.Pick(parts); TryApplyWound(part.Id, inflicter); } @@ -51,52 +57,30 @@ public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) var success = false; foreach (var (traumaType, trauma) in inflicter.Traumas) { - var validTarget = GetValidWoundable(target, traumaType); - - if (!validTarget.HasValue) - return false; - - var woundContainer = validTarget.Value.Woundable; - target = validTarget.Value.Target; - - var modifiers = ApplyTraumaModifiers(traumaType, woundContainer.TraumaResistance, trauma.Damage); - if (TryPickWound(traumaType, modifiers, out var wound)) - success |= AddWound(target, woundContainer, traumaType, wound); + success |= TryAddWound(target, traumaType, trauma); + // TODO wounds before merge add support for non penetrating traumas var type = trauma.PenTraumaType ?? traumaType; if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) continue; - validTarget = GetValidWoundable(target, type); - - if (!validTarget.HasValue) - continue; - - //Apply penetrating wounds - woundContainer = validTarget.Value.Woundable; - target = validTarget.Value.Target; - - modifiers = ApplyTraumaModifiers(type, woundContainer.TraumaResistance, trauma.Damage); - if (TryPickWound(type, modifiers, out wound)) - success |= AddWound(target, woundContainer, type, wound); + success |= TryAddWound(target, type, trauma); } return success; } - public bool TryAddWound(EntityUid target, string traumaType, TraumaDamage damage) + public bool TryAddWound(EntityUid target, string traumaType, TraumaDamage traumaDamage) { - var checkedContainer = GetValidWoundable(target, traumaType); - - if (checkedContainer == null) + // TODO wounds before merge turn into tryget + if (GetValidWoundable(target, traumaType) is not {Woundable: var woundable}) return false; - var woundContainer = checkedContainer.Value.Woundable; - target = checkedContainer.Value.Target; + var modifiers = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); - return TryPickWound(traumaType, damage.Damage, out var wound) && - AddWound(target, woundContainer, traumaType, wound); + return TryCreateWound(target, traumaType, modifiers, out var wound) && + AddWound(woundable, wound.Id, wound.Component); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -113,34 +97,42 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m return damage; } - private bool AddWound(EntityUid target, WoundableComponent woundContainer, string traumaType, Wound wound) + private bool AddWound(WoundableComponent woundable, EntityUid woundId, WoundComponent? wound = null) { - woundContainer.Wounds ??= new List(); - woundContainer.Wounds.Add(wound); + if (!Resolve(woundId, ref wound, false)) + return false; - return true; + var wounds = _containers.EnsureContainer(woundable.Owner, WoundContainerId); + return wounds.Insert(woundId); } - private (WoundableComponent Woundable, EntityUid Target)? GetValidWoundable(EntityUid target, string traumaType) + private (EntityUid Target, WoundableComponent Woundable)? GetValidWoundable(EntityUid target, string traumaType) { if (!TryComp(target, out var woundable)) return null; if (woundable.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (woundable, target); + return (target, woundable); var adjacentWoundable = FindValidWoundableInAdjacent(target, traumaType); return adjacentWoundable; } - private bool TryPickWound(string traumaType, FixedPoint2 trauma, out Wound wound) + private bool TryCreateWound( + EntityUid woundableId, + string traumaType, + FixedPoint2 traumaDamage, + out (EntityUid Id, WoundComponent Component) wound) { foreach (var woundData in _cachedWounds[traumaType].HighestToLowest()) { - if (woundData.Key > trauma) + if (woundData.Key > traumaDamage) continue; - wound = new Wound(woundData.Value); + // TODO wounds before merge dont spawn on the client + var woundId = Spawn(woundData.Value, woundableId.ToCoordinates()); + var woundComponent = Comp(woundId); + wound = (woundId, woundComponent); return true; } @@ -148,7 +140,7 @@ private bool TryPickWound(string traumaType, FixedPoint2 trauma, out Wound wound return false; } - private (WoundableComponent Woundable, EntityUid Target)? FindValidWoundableInAdjacent(EntityUid target, + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacent(EntityUid target, string traumaType) { //search all the children in the body for a part that accepts the wound we want to apply @@ -158,25 +150,25 @@ private bool TryPickWound(string traumaType, FixedPoint2 trauma, out Wound wound return foundContainer; } - private (WoundableComponent Woundable, EntityUid Target)? FindValidWoundableInAdjacentParts(EntityUid target, + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacentParts(EntityUid target, string traumaType) { - foreach (var data in _bodySystem.GetBodyPartAdjacentPartsComponents(target)) + foreach (var data in _body.GetBodyPartAdjacentPartsComponents(target)) { if (data.Component.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (data.Component, target); + return (target, data.Component); } return null; } - private (WoundableComponent Woundable, EntityUid Target)? FindValidWoundableInOrgans(EntityUid target, + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInOrgans(EntityUid target, string traumaType) { - foreach (var data in _bodySystem.GetBodyPartOrganComponents(target)) + foreach (var data in _body.GetBodyPartOrganComponents(target)) { if (data.Comp.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (data.Comp, target); + return (target, data.Comp); } return null; diff --git a/Resources/Prototypes/Body/Traumas/blunt.yml b/Resources/Prototypes/Body/Traumas/blunt.yml deleted file mode 100644 index 58fc0e288068ce..00000000000000 --- a/Resources/Prototypes/Body/Traumas/blunt.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: trauma # TODO wounds before merge - id: Slash - wounds: - 1: Bruise - 5: Scrape - 10: Abrasion - 20: Cut - 30: Laceration - 50: Avulsion diff --git a/Resources/Prototypes/Body/Traumas/slash.yml b/Resources/Prototypes/Body/Traumas/slash.yml index 32120c2f5fd48a..58fc0e288068ce 100644 --- a/Resources/Prototypes/Body/Traumas/slash.yml +++ b/Resources/Prototypes/Body/Traumas/slash.yml @@ -1,33 +1,9 @@ -- type: wound # TODO wounds before merge - id: Scrape - name: Scrape # TODO loc - description: Scrape # TODO - healthDamage: 3 - # TODO wounds bleed - -- type: wound # TODO wounds before merge - id: Abrasion - name: Abrasion # TODO loc - description: Abrasion # TODO - healthDamage: 7 - -- type: wound # TODO wounds before merge - id: Cut - name: Cut # TODO loc - description: Cut # TODO - healthDamage: 14 - integrityDamage: 5 - -- type: wound # TODO wounds before merge - id: Laceration - name: Laceration # TODO loc - description: Laceration # TODO - healthDamage: 25 - integrityDamage: 20 - -- type: wound # TODO wounds before merge - id: Avulsion - name: Avulsion # TODO loc - description: Avulsion # TODO - healthDamage: 40 - integrityDamage: 40 +- type: trauma # TODO wounds before merge + id: Slash + wounds: + 1: Bruise + 5: Scrape + 10: Abrasion + 20: Cut + 30: Laceration + 50: Avulsion diff --git a/Resources/Prototypes/Body/Wounds/blunt.yml b/Resources/Prototypes/Body/Wounds/blunt.yml index 0ab0062cb05f57..e8f996c1ad2b01 100644 --- a/Resources/Prototypes/Body/Wounds/blunt.yml +++ b/Resources/Prototypes/Body/Wounds/blunt.yml @@ -1,5 +1,7 @@ -- type: wound # TODO wounds before merge +- type: entity # TODO wounds before merge id: Bruise - name: Bruise # TODO loc + name: Bruise description: Bruise # TODO - healthDamage: 1 + components: + - type: Wound + healthDamage: 1 diff --git a/Resources/Prototypes/Body/Wounds/slash.yml b/Resources/Prototypes/Body/Wounds/slash.yml new file mode 100644 index 00000000000000..5228c42ed6f91d --- /dev/null +++ b/Resources/Prototypes/Body/Wounds/slash.yml @@ -0,0 +1,43 @@ +- type: entity # TODO wounds before merge + id: Scrape + name: Scrape + description: Scrape # TODO + components: + - type: Wound + healthDamage: 3 + # TODO wounds bleed + +- type: entity # TODO wounds before merge + id: Abrasion + name: Abrasion + description: Abrasion # TODO + components: + - type: Wound + healthDamage: 7 + +- type: entity # TODO wounds before merge + id: Cut + name: Cut + description: Cut # TODO + components: + - type: Wound + healthDamage: 14 + integrityDamage: 5 + +- type: entity # TODO wounds before merge + id: Laceration + name: Laceration + description: Laceration # TODO + components: + - type: Wound + healthDamage: 25 + integrityDamage: 20 + +- type: entity # TODO wounds before merge + id: Avulsion + name: Avulsion + description: Avulsion # TODO + components: + - type: Wound + healthDamage: 40 + integrityDamage: 40 From 6e682c24c010e41a4bf6675a47033e373d150c90 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Fri, 18 Nov 2022 18:46:54 -0800 Subject: [PATCH 033/148] Renamed structure to integrity Removed woundPrototype --- .../Wounds/Components/WoundableComponent.cs | 12 ++----- .../Wounds/Prototypes/TraumaPrototype.cs | 2 +- .../Wounds/Prototypes/WoundPrototype.cs | 32 ------------------- 3 files changed, 3 insertions(+), 43 deletions(-) delete mode 100644 Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 70ca8a877b0dc4..7c96d076e733f5 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -17,18 +17,10 @@ public sealed class WoundableComponent : Component [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; - //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("maxHealth", required: true)] - public FixedPoint2 MaxHealth; - //How much health does this woundable have, when this reaches 0, it starts taking structural damage [DataField("health", required: true)] public FixedPoint2 Health; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(WoundSystem))] [DataField("maxStructure", required: true)] - public FixedPoint2 MaxStructure; - - //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(WoundSystem))] [DataField("structure", required: true)] - public FixedPoint2 StructuralPool; + [Access(typeof(WoundSystem))] [DataField("integrity", required: true)] + public FixedPoint2 Integrity; } diff --git a/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs index 4cbc47491ab0e1..23bdb68946f6e4 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs @@ -12,7 +12,7 @@ public sealed class TraumaPrototype : IPrototype //Note: these should be defined in order of severity! //list of possible wounds sorted by their trauma cutoffs [DataField("wounds", required: true, - customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] public SortedDictionary Wounds { get; init; } = new(); // TODO wounds wound cap diff --git a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs deleted file mode 100644 index 5e64c9e9d54aa5..00000000000000 --- a/Content.Shared/Medical/Wounds/Prototypes/WoundPrototype.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Shared.FixedPoint; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Medical.Wounds.Prototypes; - -[Prototype("wound")] -public sealed class WoundPrototype : IPrototype -{ - [IdDataField] public string ID { get; init; } = string.Empty; - - [DataField("name", required: true)] - public readonly string DisplayName = string.Empty; - - [DataField("description", required: true)] - public readonly string Description = string.Empty; - - [DataField("healthDamage")] - public readonly FixedPoint2 HealthDamage; - - [DataField("integrityDamage")] - public readonly FixedPoint2 IntegrityDamage; - - [DataField("bleed")] - public readonly FixedPoint2 Bleed; - - // TODO pain -} - -public static class A -{ - // IT LIVES ON! FOREVER IN OUR HEARTS! -} From da95faf7745ac3343936d1bfa599eceb001bd397 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 19 Nov 2022 06:27:55 +0100 Subject: [PATCH 034/148] Implement wound damage and woundable destruction --- .../Body/Systems/SharedBodySystem.Parts.cs | 13 +++ .../Wounds/Components/WoundComponent.cs | 13 ++- .../Wounds/Components/WoundableComponent.cs | 9 +- .../Medical/Wounds/Systems/WoundEvents.cs | 4 + .../Medical/Wounds/Systems/WoundSystem.cs | 98 +++++++++++++------ Resources/Prototypes/Body/Parts/human.yml | 4 +- .../Entities/Objects/Weapons/Melee/sword.yml | 2 +- 7 files changed, 106 insertions(+), 37 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Systems/WoundEvents.cs diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index f11c311d7fcfa2..ace1fd403f07d8 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -125,6 +125,19 @@ public bool TryCreatePartSlotAndAttach( return TryCreatePartSlot(parentId, id, out var slot, parent) && AttachPart(childId, slot, child); } + public EntityUid? GetPartParent(EntityUid? id, BodyPartComponent? part = null) + { + if (id == null || !Resolve(id.Value, ref part, false)) + return null; + + return part.ParentSlot?.Parent; + } + + public bool TryGetPartParent(EntityUid? id, out EntityUid parent, BodyPartComponent? part = null) + { + return (parent = GetPartParent(id, part).GetValueOrDefault()) != default; + } + public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetPartChildren(EntityUid? id, BodyPartComponent? part = null) { if (id == null || !Resolve(id.Value, ref part, false)) diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index 08e2749f722b1c..a33289d49d1d0f 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -1,4 +1,5 @@ -using Content.Shared.Medical.Wounds.Systems; +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.GameStates; namespace Content.Shared.Medical.Wounds.Components; @@ -7,7 +8,13 @@ namespace Content.Shared.Medical.Wounds.Components; [Access(typeof(WoundSystem))] public sealed class WoundComponent : Component { - [DataField("healthDamage")] public int HealthDamage; + [DataField("healthDamage")] public FixedPoint2 HealthDamage; - [DataField("integrityDamage")] public int IntegrityDamage; + [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; + + [DataField("healthDamageDealt")] public FixedPoint2 HealthDamageDealt; + + [DataField("integrityDamageDealt")] public FixedPoint2 IntegrityDamageDealt; + + [DataField("overflowDamageDealt")] public FixedPoint2 OverflowDamageDealt; } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 7c96d076e733f5..b640bf5fa0ab97 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -2,6 +2,8 @@ using Content.Shared.Medical.Wounds.Prototypes; using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Shared.Medical.Wounds.Components; @@ -18,9 +20,12 @@ public sealed class WoundableComponent : Component [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("health", required: true)] public FixedPoint2 Health; + [DataField("health")] public FixedPoint2 Health; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! - [Access(typeof(WoundSystem))] [DataField("integrity", required: true)] + [DataField("integrity", required: true)] public FixedPoint2 Integrity; + + [DataField("destroyWound", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? DestroyWoundId; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundEvents.cs b/Content.Shared/Medical/Wounds/Systems/WoundEvents.cs new file mode 100644 index 00000000000000..496d7df7158d9c --- /dev/null +++ b/Content.Shared/Medical/Wounds/Systems/WoundEvents.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Medical.Wounds.Systems; + +[ByRefEvent] +public readonly record struct WoundableDestroyedEvent; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index f0bee1ef92517c..b9f855225fc573 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; using Content.Shared.Coordinates; @@ -14,7 +15,7 @@ namespace Content.Shared.Medical.Wounds.Systems; public sealed class WoundSystem : EntitySystem { - private const string WoundContainerId = "WoundableComponentWounds"; + private const string WoundContainerId = "WoundSystemWounds"; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; @@ -26,8 +27,8 @@ public sealed class WoundSystem : EntitySystem public override void Initialize() { - CacheData(null); - _prototypeManager.PrototypesReloaded += CacheData; + CacheData(); + _prototypeManager.PrototypesReloaded += _ => CacheData(); SubscribeLocalEvent(OnBodyAttacked); } @@ -42,7 +43,7 @@ private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEven TryApplyWound(part.Id, inflicter); } - private void CacheData(PrototypesReloadedEventArgs? prototypesReloadedEventArgs) + private void CacheData() { _cachedWounds.Clear(); @@ -57,7 +58,7 @@ public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) var success = false; foreach (var (traumaType, trauma) in inflicter.Traumas) { - success |= TryAddWound(target, traumaType, trauma); + success |= AddWoundToPart(target, traumaType, trauma); // TODO wounds before merge add support for non penetrating traumas var type = trauma.PenTraumaType ?? traumaType; @@ -65,22 +66,22 @@ public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) continue; - success |= TryAddWound(target, type, trauma); + success |= AddWoundToPart(target, type, trauma); } return success; } - public bool TryAddWound(EntityUid target, string traumaType, TraumaDamage traumaDamage) + public bool AddWoundToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) { // TODO wounds before merge turn into tryget - if (GetValidWoundable(target, traumaType) is not {Woundable: var woundable}) + if (GetValidWoundable(target, traumaType) is not {Target: var woundableId, Woundable: var woundable}) return false; - var modifiers = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); + var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); - return TryCreateWound(target, traumaType, modifiers, out var wound) && - AddWound(woundable, wound.Id, wound.Component); + return TryChooseWound(traumaType, modifiedDamage, out var woundId) && + AddWound(woundableId, woundId, woundable); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -97,13 +98,62 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m return damage; } - private bool AddWound(WoundableComponent woundable, EntityUid woundId, WoundComponent? wound = null) + private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableComponent? woundable = null) { - if (!Resolve(woundId, ref wound, false)) + if (!Resolve(woundableId, ref woundable, false)) return false; - var wounds = _containers.EnsureContainer(woundable.Owner, WoundContainerId); - return wounds.Insert(woundId); + var woundId = Spawn(woundPrototypeId, woundableId.ToCoordinates()); + var wound = Comp(woundId); + + var wounds = _containers.EnsureContainer(woundableId, WoundContainerId); + if (!wounds.Insert(woundId)) + return false; + + var healthDamage = wound.HealthDamage; + var integrityDamage = wound.IntegrityDamage; + var overflowDamage = FixedPoint2.Zero; + + if (healthDamage > woundable.Health) + { + integrityDamage += healthDamage - woundable.Health; + healthDamage = woundable.Health; + } + + if (integrityDamage > woundable.Integrity) + { + overflowDamage += integrityDamage - woundable.Integrity; + integrityDamage = woundable.Integrity; + } + + woundable.Health -= healthDamage; + woundable.Integrity -= integrityDamage; + + wound.HealthDamageDealt = healthDamage; + wound.IntegrityDamageDealt = integrityDamage; + wound.OverflowDamageDealt = overflowDamage; + + if (woundable.Health + woundable.Integrity <= FixedPoint2.Zero) + DestroyWoundable(woundableId); + + return true; + } + + private void DestroyWoundable(EntityUid woundableId, WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return; + + if (woundable.DestroyWoundId != null && + _body.TryGetPartParent(woundableId, out var parent)) + { + AddWound(parent, woundable.DestroyWoundId); + } + + _body.DropPart(woundableId); + + var ev = new WoundableDestroyedEvent(); + RaiseLocalEvent(woundableId, ev); } private (EntityUid Target, WoundableComponent Woundable)? GetValidWoundable(EntityUid target, string traumaType) @@ -118,30 +168,22 @@ private bool AddWound(WoundableComponent woundable, EntityUid woundId, WoundComp return adjacentWoundable; } - private bool TryCreateWound( - EntityUid woundableId, - string traumaType, - FixedPoint2 traumaDamage, - out (EntityUid Id, WoundComponent Component) wound) + private bool TryChooseWound(string traumaType, FixedPoint2 traumaDamage, [NotNullWhen(true)] out string? woundId) { foreach (var woundData in _cachedWounds[traumaType].HighestToLowest()) { if (woundData.Key > traumaDamage) continue; - // TODO wounds before merge dont spawn on the client - var woundId = Spawn(woundData.Value, woundableId.ToCoordinates()); - var woundComponent = Comp(woundId); - wound = (woundId, woundComponent); + woundId = woundData.Value; return true; } - wound = default; + woundId = null; return false; } - private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacent(EntityUid target, - string traumaType) + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacent(EntityUid target, string traumaType) { //search all the children in the body for a part that accepts the wound we want to apply //checks organs first, then recursively checks child parts and their organs. diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml index d56fb67c16f9a7..ef9e953188083c 100644 --- a/Resources/Prototypes/Body/Parts/human.yml +++ b/Resources/Prototypes/Body/Parts/human.yml @@ -18,10 +18,8 @@ - type: Woundable # TODO wounds before merge allowedTraumaTypes: - Slash - maxHealth: 100 health: 100 - maxStructure: 100 - structure: 100 + integrity: 100 - type: entity id: TorsoHuman diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 514a67e94b000a..9c7c0731e62542 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -23,7 +23,7 @@ - type: TraumaInflicter # TODO wounds before merge traumas: Slash: - damage: 10 + damage: 20 penChance: 0.3 penModifier: 0.2 From 444dfabb7c8a6912013bc02fb66c7322f14dc9a0 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 19 Nov 2022 06:31:30 +0100 Subject: [PATCH 035/148] Gib root parts on destroy --- .../Body/Systems/SharedBodySystem.Body.cs | 3 +-- .../Body/Systems/SharedBodySystem.Parts.cs | 9 +++++---- .../Medical/Wounds/Systems/WoundSystem.cs | 15 ++++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 60a87396833003..44edc2573299e6 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -161,8 +161,7 @@ public IEnumerable GetBodyAllSlots(EntityUid? bodyId, BodyComponen } } - public virtual HashSet GibBody(EntityUid? partId, bool gibOrgans = false, - BodyComponent? body = null) + public virtual HashSet GibBody(EntityUid? partId, bool gibOrgans = false, BodyComponent? body = null) { if (partId == null || !Resolve(partId.Value, ref body, false)) return new HashSet(); diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index ace1fd403f07d8..e5574fc1f0b88d 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -125,17 +125,18 @@ public bool TryCreatePartSlotAndAttach( return TryCreatePartSlot(parentId, id, out var slot, parent) && AttachPart(childId, slot, child); } - public EntityUid? GetPartParent(EntityUid? id, BodyPartComponent? part = null) + public EntityUid? GetPartParentPart(EntityUid? id, BodyPartComponent? part = null) { if (id == null || !Resolve(id.Value, ref part, false)) return null; - return part.ParentSlot?.Parent; + var parent = part.ParentSlot?.Parent; + return parent == part.Body ? null : parent; } - public bool TryGetPartParent(EntityUid? id, out EntityUid parent, BodyPartComponent? part = null) + public bool TryGetPartParentPart(EntityUid? id, out EntityUid parent, BodyPartComponent? part = null) { - return (parent = GetPartParent(id, part).GetValueOrDefault()) != default; + return (parent = GetPartParentPart(id, part).GetValueOrDefault()) != default; } public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetPartChildren(EntityUid? id, BodyPartComponent? part = null) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index b9f855225fc573..e0a3eacc57b4b2 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -144,14 +144,19 @@ private void DestroyWoundable(EntityUid woundableId, WoundableComponent? woundab if (!Resolve(woundableId, ref woundable, false)) return; - if (woundable.DestroyWoundId != null && - _body.TryGetPartParent(woundableId, out var parent)) + if (woundable.DestroyWoundId != null) { - AddWound(parent, woundable.DestroyWoundId); + if (_body.TryGetPartParentPart(woundableId, out var parent)) + { + AddWound(parent, woundable.DestroyWoundId); + _body.DropPart(woundableId); + } + else + { + _body.GibBody(woundableId); + } } - _body.DropPart(woundableId); - var ev = new WoundableDestroyedEvent(); RaiseLocalEvent(woundableId, ev); } From 9d9ab5324fcb2bd1d756fa1a11154ac807d72c8a Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 19 Nov 2022 06:33:46 +0100 Subject: [PATCH 036/148] Add todo for gibbing body parts --- Content.Shared/Medical/Wounds/Systems/WoundSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index e0a3eacc57b4b2..e4eb41090991ec 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -149,6 +149,7 @@ private void DestroyWoundable(EntityUid woundableId, WoundableComponent? woundab if (_body.TryGetPartParentPart(woundableId, out var parent)) { AddWound(parent, woundable.DestroyWoundId); + // TODO wounds before merge gib _body.DropPart(woundableId); } else From 34ff0aa4e6907854a44989f8bbe87c971f146ad5 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 23 Nov 2022 08:46:07 +0100 Subject: [PATCH 037/148] Fix integrity destroy check --- Content.Shared/Medical/Wounds/Systems/WoundSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index e4eb41090991ec..ef2ef720744aaa 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -133,7 +133,7 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC wound.IntegrityDamageDealt = integrityDamage; wound.OverflowDamageDealt = overflowDamage; - if (woundable.Health + woundable.Integrity <= FixedPoint2.Zero) + if (woundable.Integrity <= FixedPoint2.Zero) DestroyWoundable(woundableId); return true; From a4a8c784240a5fda951df2c03db9e6da80508b19 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 18:31:33 -0800 Subject: [PATCH 038/148] Implemented healthcap-based wounding Also did some naming cleanup --- .../Wounds/Components/WoundComponent.cs | 8 +- .../Wounds/Components/WoundableComponent.cs | 11 +- .../Medical/Wounds/Systems/WoundSystem.cs | 114 +++++++++++++----- 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index a33289d49d1d0f..87c614c8b2f89e 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -8,13 +8,9 @@ namespace Content.Shared.Medical.Wounds.Components; [Access(typeof(WoundSystem))] public sealed class WoundComponent : Component { - [DataField("healthDamage")] public FixedPoint2 HealthDamage; + [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; - [DataField("healthDamageDealt")] public FixedPoint2 HealthDamageDealt; - - [DataField("integrityDamageDealt")] public FixedPoint2 IntegrityDamageDealt; - - [DataField("overflowDamageDealt")] public FixedPoint2 OverflowDamageDealt; + [DataField("severityPercentage")] public FixedPoint2 SeverityPercentage = 1.0; } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index b640bf5fa0ab97..bf9e82a4ebc58f 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -20,7 +20,16 @@ public sealed class WoundableComponent : Component [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("health")] public FixedPoint2 Health; + [DataField("health")] public FixedPoint2 Health = -1; + + //The maximum health this part can have + [DataField("healthCap", required: true)] + public FixedPoint2 HealthCap; + + // public FixedPoint2 HealthCap => _healthCap - HealthCapDamage < 0 ? 0 : _healthCap - HealthCapDamage; + + //The amount maximum health is decreased by, this is affected by wounds + [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! [DataField("integrity", required: true)] diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index ef2ef720744aaa..14f5f484a68fe0 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -31,6 +31,16 @@ public override void Initialize() _prototypeManager.PrototypesReloaded += _ => CacheData(); SubscribeLocalEvent(OnBodyAttacked); + SubscribeLocalEvent(OnWoundableCreated); + } + + //TODO: Smug will this break networking? - Jez + private void OnWoundableCreated(EntityUid uid, WoundableComponent component, ComponentInit args) + { + if (component.Health < 0)//if initial woundable health isn't defined default to the woundCap. + { + component.Health = component.HealthCap; + } } private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEvent args) @@ -40,7 +50,7 @@ private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEven var parts = _body.GetBodyChildren(uid, component).ToList(); var part = _random.Pick(parts); - TryApplyWound(part.Id, inflicter); + TryApplyTrauma(part.Id, inflicter); } private void CacheData() @@ -53,12 +63,12 @@ private void CacheData() } } - public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) + public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) { var success = false; foreach (var (traumaType, trauma) in inflicter.Traumas) { - success |= AddWoundToPart(target, traumaType, trauma); + success |= ApplyTraumaToPart(target, traumaType, trauma); // TODO wounds before merge add support for non penetrating traumas var type = trauma.PenTraumaType ?? traumaType; @@ -66,22 +76,21 @@ public bool TryApplyWound(EntityUid target, TraumaInflicterComponent inflicter) if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) continue; - success |= AddWoundToPart(target, type, trauma); + success |= ApplyTraumaToPart(target, type, trauma); } return success; } - public bool AddWoundToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) + public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) { // TODO wounds before merge turn into tryget if (GetValidWoundable(target, traumaType) is not {Target: var woundableId, Woundable: var woundable}) return false; var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); - return TryChooseWound(traumaType, modifiedDamage, out var woundId) && - AddWound(woundableId, woundId, woundable); + AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage, woundable); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -109,33 +118,84 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC var wounds = _containers.EnsureContainer(woundableId, WoundContainerId); if (!wounds.Insert(woundId)) return false; + ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); + return true; + } - var healthDamage = wound.HealthDamage; - var integrityDamage = wound.IntegrityDamage; - var overflowDamage = FixedPoint2.Zero; + public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage,WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + woundable.HealthCapDamage += woundDamage; + if (woundable.HealthCapDamage < FixedPoint2.Zero) + ApplyRawIntegrityDamage(woundableId, -woundable.HealthCapDamage, woundable); + woundable.HealthCapDamage = 0; + return true; + } - if (healthDamage > woundable.Health) - { - integrityDamage += healthDamage - woundable.Health; - healthDamage = woundable.Health; - } + public bool ApplyRawIntegrityDamage(EntityUid woundableId, FixedPoint2 integrityDamage, WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + woundable.Integrity -= integrityDamage; + if (woundable.Integrity <= FixedPoint2.Zero) + DestroyWoundable(woundableId); + return true; + } - if (integrityDamage > woundable.Integrity) - { - overflowDamage += integrityDamage - woundable.Integrity; - integrityDamage = woundable.Integrity; - } + public bool IncreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityIncrease,WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + ApplyWoundSeverityDelta(woundable, wound,wound.SeverityPercentage + severityIncrease); + return true; + } - woundable.Health -= healthDamage; - woundable.Integrity -= integrityDamage; + public bool DecreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityDecrease,WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + ApplyWoundSeverityDelta(woundable, wound,wound.SeverityPercentage - severityDecrease); + return true; + } - wound.HealthDamageDealt = healthDamage; - wound.IntegrityDamageDealt = integrityDamage; - wound.OverflowDamageDealt = overflowDamage; + public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityAmount,WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + ApplyWoundSeverityDelta(woundable, wound,severityAmount); + return true; + } - if (woundable.Integrity <= FixedPoint2.Zero) - DestroyWoundable(woundableId); + private void ApplyWoundSeverityDelta(WoundableComponent woundable, WoundComponent wound, FixedPoint2 newSeverity) + { + newSeverity = Math.Clamp(newSeverity.Float(), 0.0f, 1.0f); + if (wound.SeverityPercentage == newSeverity) + return; + var severityDelta = newSeverity - wound.SeverityPercentage; + var healthCapDamageDelta = severityDelta * wound.HealthCapDamage; + woundable.HealthCapDamage += healthCapDamageDelta; + } + public bool RemoveWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + var woundContainer = _containers.GetContainer(woundableId, WoundContainerId); + if (!woundContainer.Remove(woundId)) + return false; + ApplyWoundSeverityDelta(woundable, wound, 0); return true; } From 8f13cbb13e5505a094699685821f8f2454a2618e Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 18:53:54 -0800 Subject: [PATCH 039/148] Split WoundSystem into a partial. Converted severity to use floats --- .../Wounds/Components/WoundComponent.cs | 8 +- .../Wounds/Components/WoundableComponent.cs | 4 +- .../Wounds/Systems/WoundSystem.Healing.cs | 6 + .../Wounds/Systems/WoundSystem.Wounding.cs | 265 +++++++++++++++++ .../Medical/Wounds/Systems/WoundSystem.cs | 275 +----------------- 5 files changed, 284 insertions(+), 274 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs create mode 100644 Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index 87c614c8b2f89e..3eb2686fadefef 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -12,5 +12,11 @@ public sealed class WoundComponent : Component [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; - [DataField("severityPercentage")] public FixedPoint2 SeverityPercentage = 1.0; + [DataField("severityPercentage")] public float SeverityPercentage = 1.0f; + + //How many severity points per woundTick does this part heal passively + [DataField("baseHealingRate")] public float BaseHealingRate = 0.05f; + + //How many severity points per woundTick does this part heal ontop of the base rate + [DataField("healingModifier")] public float HealingModifier; } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index bf9e82a4ebc58f..8769745ecbdfd1 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -26,9 +26,7 @@ public sealed class WoundableComponent : Component [DataField("healthCap", required: true)] public FixedPoint2 HealthCap; - // public FixedPoint2 HealthCap => _healthCap - HealthCapDamage < 0 ? 0 : _healthCap - HealthCapDamage; - - //The amount maximum health is decreased by, this is affected by wounds + //The amount maximum health is decreased by, this is affected by wounds [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs new file mode 100644 index 00000000000000..9eb597c1f4de28 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Medical.Wounds.Systems; + +public sealed partial class WoundSystem +{ + +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs new file mode 100644 index 00000000000000..38cd35c29410ea --- /dev/null +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -0,0 +1,265 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Coordinates; +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Components; +using Content.Shared.Medical.Wounds.Prototypes; +using Robust.Shared.Containers; +using Robust.Shared.Random; + +namespace Content.Shared.Medical.Wounds.Systems; + +public sealed partial class WoundSystem +{ + private readonly Dictionary _cachedWounds = new(); + + private void CacheWoundData() + { + _cachedWounds.Clear(); + + foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) + { + _cachedWounds.Add(traumaType.ID, new WoundTable(traumaType)); + } + } + + public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) + { + var success = false; + foreach (var (traumaType, trauma) in inflicter.Traumas) + { + success |= ApplyTraumaToPart(target, traumaType, trauma); + + // TODO wounds before merge add support for non penetrating traumas + var type = trauma.PenTraumaType ?? traumaType; + + if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) + continue; + + success |= ApplyTraumaToPart(target, type, trauma); + } + + return success; + } + + public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) + { + // TODO wounds before merge turn into tryget + if (GetValidWoundable(target, traumaType) is not {Target: var woundableId, Woundable: var woundable}) + return false; + + var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); + return TryChooseWound(traumaType, modifiedDamage, out var woundId) && + AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage, woundable); + } + + private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) + { + if (!modifiers.HasValue) + return damage; + + if (modifiers.Value.Coefficients.TryGetValue(traumaType, out var coefficient)) + damage *= coefficient; + + if (modifiers.Value.FlatReduction.TryGetValue(traumaType, out var reduction)) + damage -= reduction; + + return damage; + } + + private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + + var woundId = Spawn(woundPrototypeId, woundableId.ToCoordinates()); + var wound = Comp(woundId); + + var wounds = _containers.EnsureContainer(woundableId, WoundContainerId); + if (!wounds.Insert(woundId)) + return false; + SetWoundSeverity(woundableId, woundId, wound.SeverityPercentage, woundable, wound); + ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); + return true; + } + + public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage, + WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + woundable.HealthCapDamage += woundDamage; + if (woundable.HealthCapDamage < FixedPoint2.Zero) + ApplyRawIntegrityDamage(woundableId, -woundable.HealthCapDamage, woundable); + woundable.HealthCapDamage = 0; + return true; + } + + public bool ApplyRawIntegrityDamage(EntityUid woundableId, FixedPoint2 integrityDamage, + WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + woundable.Integrity -= integrityDamage; + if (woundable.Integrity <= FixedPoint2.Zero) + DestroyWoundable(woundableId); + return true; + } + + public bool IncreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityIncrease, + WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + ApplyWoundSeverityDelta(woundable, wound, wound.SeverityPercentage + severityIncrease); + return true; + } + + public bool DecreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityDecrease, + WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + ApplyWoundSeverityDelta(woundable, wound, wound.SeverityPercentage - severityDecrease); + return true; + } + + public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityAmount, + WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + ApplyWoundSeverityDelta(woundable, wound, severityAmount); + return true; + } + + private void ApplyWoundSeverityDelta(WoundableComponent woundable, WoundComponent wound, float newSeverity) + { + newSeverity = Math.Clamp(newSeverity, 0.0f, 1.0f); + var severityDelta = newSeverity - wound.SeverityPercentage; + var healthCapDamageDelta = severityDelta * wound.HealthCapDamage; + woundable.HealthCapDamage += healthCapDamageDelta; + } + + public bool RemoveWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + if (!Resolve(woundId, ref wound, false)) + return false; + var woundContainer = _containers.GetContainer(woundableId, WoundContainerId); + if (!woundContainer.Remove(woundId)) + return false; + ApplyWoundSeverityDelta(woundable, wound, 0); + return true; + } + + private void DestroyWoundable(EntityUid woundableId, WoundableComponent? woundable = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return; + + if (woundable.DestroyWoundId != null) + { + if (_body.TryGetPartParentPart(woundableId, out var parent)) + { + AddWound(parent, woundable.DestroyWoundId); + // TODO wounds before merge gib + _body.DropPart(woundableId); + } + else + { + _body.GibBody(woundableId); + } + } + + var ev = new WoundableDestroyedEvent(); + RaiseLocalEvent(woundableId, ev); + } + + private (EntityUid Target, WoundableComponent Woundable)? GetValidWoundable(EntityUid target, string traumaType) + { + if (!TryComp(target, out var woundable)) + return null; + + if (woundable.AllowedTraumaTypes?.Contains(traumaType) ?? false) + return (target, woundable); + + var adjacentWoundable = FindValidWoundableInAdjacent(target, traumaType); + return adjacentWoundable; + } + + private bool TryChooseWound(string traumaType, FixedPoint2 traumaDamage, [NotNullWhen(true)] out string? woundId) + { + foreach (var woundData in _cachedWounds[traumaType].HighestToLowest()) + { + if (woundData.Key > traumaDamage) + continue; + + woundId = woundData.Value; + return true; + } + + woundId = null; + return false; + } + + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacent(EntityUid target, + string traumaType) + { + //search all the children in the body for a part that accepts the wound we want to apply + //checks organs first, then recursively checks child parts and their organs. + var foundContainer = FindValidWoundableInOrgans(target, traumaType) ?? + FindValidWoundableInAdjacentParts(target, traumaType); + return foundContainer; + } + + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacentParts(EntityUid target, + string traumaType) + { + foreach (var data in _body.GetBodyPartAdjacentPartsComponents(target)) + { + if (data.Component.AllowedTraumaTypes?.Contains(traumaType) ?? false) + return (target, data.Component); + } + + return null; + } + + private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInOrgans(EntityUid target, + string traumaType) + { + foreach (var data in _body.GetBodyPartOrganComponents(target)) + { + if (data.Comp.AllowedTraumaTypes?.Contains(traumaType) ?? false) + return (target, data.Comp); + } + + return null; + } + + private readonly struct WoundTable + { + private readonly SortedDictionary _wounds; + + public WoundTable(TraumaPrototype trauma) + { + _wounds = trauma.Wounds; + } + + public IEnumerable> HighestToLowest() + { + return _wounds.Reverse(); + } + } +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 14f5f484a68fe0..9c7def77679e3a 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,11 +1,7 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; +using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; -using Content.Shared.Coordinates; -using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; -using Content.Shared.Medical.Wounds.Prototypes; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Containers; using Robust.Shared.Prototypes; @@ -13,7 +9,7 @@ namespace Content.Shared.Medical.Wounds.Systems; -public sealed class WoundSystem : EntitySystem +public sealed partial class WoundSystem : EntitySystem { private const string WoundContainerId = "WoundSystemWounds"; @@ -23,12 +19,10 @@ public sealed class WoundSystem : EntitySystem [Dependency] private readonly SharedBodySystem _body = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; - private readonly Dictionary _cachedWounds = new(); - public override void Initialize() { - CacheData(); - _prototypeManager.PrototypesReloaded += _ => CacheData(); + CacheWoundData(); + _prototypeManager.PrototypesReloaded += _ => CacheWoundData(); SubscribeLocalEvent(OnBodyAttacked); SubscribeLocalEvent(OnWoundableCreated); @@ -37,7 +31,7 @@ public override void Initialize() //TODO: Smug will this break networking? - Jez private void OnWoundableCreated(EntityUid uid, WoundableComponent component, ComponentInit args) { - if (component.Health < 0)//if initial woundable health isn't defined default to the woundCap. + if (component.Health < 0) //if initial woundable health isn't defined default to the woundCap. { component.Health = component.HealthCap; } @@ -52,263 +46,4 @@ private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEven var part = _random.Pick(parts); TryApplyTrauma(part.Id, inflicter); } - - private void CacheData() - { - _cachedWounds.Clear(); - - foreach (var traumaType in _prototypeManager.EnumeratePrototypes()) - { - _cachedWounds.Add(traumaType.ID, new WoundTable(traumaType)); - } - } - - public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) - { - var success = false; - foreach (var (traumaType, trauma) in inflicter.Traumas) - { - success |= ApplyTraumaToPart(target, traumaType, trauma); - - // TODO wounds before merge add support for non penetrating traumas - var type = trauma.PenTraumaType ?? traumaType; - - if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) - continue; - - success |= ApplyTraumaToPart(target, type, trauma); - } - - return success; - } - - public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) - { - // TODO wounds before merge turn into tryget - if (GetValidWoundable(target, traumaType) is not {Target: var woundableId, Woundable: var woundable}) - return false; - - var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); - return TryChooseWound(traumaType, modifiedDamage, out var woundId) && - AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage, woundable); - } - - private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) - { - if (!modifiers.HasValue) - return damage; - - if (modifiers.Value.Coefficients.TryGetValue(traumaType, out var coefficient)) - damage *= coefficient; - - if (modifiers.Value.FlatReduction.TryGetValue(traumaType, out var reduction)) - damage -= reduction; - - return damage; - } - - private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableComponent? woundable = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - - var woundId = Spawn(woundPrototypeId, woundableId.ToCoordinates()); - var wound = Comp(woundId); - - var wounds = _containers.EnsureContainer(woundableId, WoundContainerId); - if (!wounds.Insert(woundId)) - return false; - ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); - return true; - } - - public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage,WoundableComponent? woundable = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - woundable.HealthCapDamage += woundDamage; - if (woundable.HealthCapDamage < FixedPoint2.Zero) - ApplyRawIntegrityDamage(woundableId, -woundable.HealthCapDamage, woundable); - woundable.HealthCapDamage = 0; - return true; - } - - public bool ApplyRawIntegrityDamage(EntityUid woundableId, FixedPoint2 integrityDamage, WoundableComponent? woundable = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - woundable.Integrity -= integrityDamage; - if (woundable.Integrity <= FixedPoint2.Zero) - DestroyWoundable(woundableId); - return true; - } - - public bool IncreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityIncrease,WoundableComponent? woundable = null, - WoundComponent? wound = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - if (!Resolve(woundId, ref wound, false)) - return false; - ApplyWoundSeverityDelta(woundable, wound,wound.SeverityPercentage + severityIncrease); - return true; - } - - public bool DecreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityDecrease,WoundableComponent? woundable = null, - WoundComponent? wound = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - if (!Resolve(woundId, ref wound, false)) - return false; - ApplyWoundSeverityDelta(woundable, wound,wound.SeverityPercentage - severityDecrease); - return true; - } - - public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityAmount,WoundableComponent? woundable = null, - WoundComponent? wound = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - if (!Resolve(woundId, ref wound, false)) - return false; - ApplyWoundSeverityDelta(woundable, wound,severityAmount); - return true; - } - - private void ApplyWoundSeverityDelta(WoundableComponent woundable, WoundComponent wound, FixedPoint2 newSeverity) - { - newSeverity = Math.Clamp(newSeverity.Float(), 0.0f, 1.0f); - if (wound.SeverityPercentage == newSeverity) - return; - var severityDelta = newSeverity - wound.SeverityPercentage; - var healthCapDamageDelta = severityDelta * wound.HealthCapDamage; - woundable.HealthCapDamage += healthCapDamageDelta; - } - - public bool RemoveWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, WoundComponent? wound = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - if (!Resolve(woundId, ref wound, false)) - return false; - var woundContainer = _containers.GetContainer(woundableId, WoundContainerId); - if (!woundContainer.Remove(woundId)) - return false; - ApplyWoundSeverityDelta(woundable, wound, 0); - return true; - } - - private void DestroyWoundable(EntityUid woundableId, WoundableComponent? woundable = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return; - - if (woundable.DestroyWoundId != null) - { - if (_body.TryGetPartParentPart(woundableId, out var parent)) - { - AddWound(parent, woundable.DestroyWoundId); - // TODO wounds before merge gib - _body.DropPart(woundableId); - } - else - { - _body.GibBody(woundableId); - } - } - - var ev = new WoundableDestroyedEvent(); - RaiseLocalEvent(woundableId, ev); - } - - private (EntityUid Target, WoundableComponent Woundable)? GetValidWoundable(EntityUid target, string traumaType) - { - if (!TryComp(target, out var woundable)) - return null; - - if (woundable.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (target, woundable); - - var adjacentWoundable = FindValidWoundableInAdjacent(target, traumaType); - return adjacentWoundable; - } - - private bool TryChooseWound(string traumaType, FixedPoint2 traumaDamage, [NotNullWhen(true)] out string? woundId) - { - foreach (var woundData in _cachedWounds[traumaType].HighestToLowest()) - { - if (woundData.Key > traumaDamage) - continue; - - woundId = woundData.Value; - return true; - } - - woundId = null; - return false; - } - - private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacent(EntityUid target, string traumaType) - { - //search all the children in the body for a part that accepts the wound we want to apply - //checks organs first, then recursively checks child parts and their organs. - var foundContainer = FindValidWoundableInOrgans(target, traumaType) ?? - FindValidWoundableInAdjacentParts(target, traumaType); - return foundContainer; - } - - private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInAdjacentParts(EntityUid target, - string traumaType) - { - foreach (var data in _body.GetBodyPartAdjacentPartsComponents(target)) - { - if (data.Component.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (target, data.Component); - } - - return null; - } - - private (EntityUid Target, WoundableComponent Woundable)? FindValidWoundableInOrgans(EntityUid target, - string traumaType) - { - foreach (var data in _body.GetBodyPartOrganComponents(target)) - { - if (data.Comp.AllowedTraumaTypes?.Contains(traumaType) ?? false) - return (target, data.Comp); - } - - return null; - } - - public FixedPoint2 ApplyModifiers(string type, FixedPoint2 damage, TraumaModifierSet modifiers) - { - if (modifiers.Coefficients.TryGetValue(type, out var coefficient)) - { - damage *= coefficient; - } - - if (modifiers.FlatReduction.TryGetValue(type, out var flat)) - { - damage -= flat; - } - - return FixedPoint2.Max(damage, FixedPoint2.Zero); - } - - private readonly struct WoundTable - { - private readonly SortedDictionary _wounds; - - public WoundTable(TraumaPrototype trauma) - { - _wounds = trauma.Wounds; - } - - public IEnumerable> HighestToLowest() - { - return _wounds.Reverse(); - } - } } From bbe5e9e06e98804d9fdafedf366b24aad7256c24 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 20:11:15 -0800 Subject: [PATCH 040/148] implemented wound healing --- .../Wounds/Components/WoundComponent.cs | 12 ++++ .../Wounds/Systems/WoundSystem.Healing.cs | 68 ++++++++++++++++++- .../Wounds/Systems/WoundSystem.Wounding.cs | 30 ++++---- .../Medical/Wounds/Systems/WoundSystem.cs | 8 ++- 4 files changed, 98 insertions(+), 20 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index 3eb2686fadefef..362828f5d150bf 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -1,6 +1,8 @@ using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Medical.Wounds.Components; @@ -8,6 +10,13 @@ namespace Content.Shared.Medical.Wounds.Components; [Access(typeof(WoundSystem))] public sealed class WoundComponent : Component { + //this is used for caching the parent woundable for use inside and entity query. + //wounds should NEVER exist without a parent so this will always have a value + public WoundableComponent Parent = default!; + + [DataField("scarWound", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string? ScarWound; + [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; @@ -19,4 +28,7 @@ public sealed class WoundComponent : Component //How many severity points per woundTick does this part heal ontop of the base rate [DataField("healingModifier")] public float HealingModifier; + + //How much to multiply the Healing modifier + [DataField("healingMultiplier")] public float HealingMultiplier = 1.0f; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index 9eb597c1f4de28..e9e856c0520b59 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -1,6 +1,72 @@ -namespace Content.Shared.Medical.Wounds.Systems; +using Content.Shared.Medical.Wounds.Components; + +namespace Content.Shared.Medical.Wounds.Systems; public sealed partial class WoundSystem { + private const float HealingTickRate = 1f; + private float _healingTimer; + + private void UpdateHealing(float frameTime) + { + _healingTimer += frameTime; + if (_healingTimer < 3f) + return; + _healingTimer -= HealingTickRate; + + foreach (var wound in EntityQuery()) + { + HealWound(wound); + } + } + + private void HealWound(WoundComponent wound) + { + if (wound.BaseHealingRate + wound.HealingModifier == 0) + return; //if the wound doesn't heal, do nothing + //we want to decrease severity so we need to invert the healing rate to become the severity delta. + var severityDecrease = -(wound.BaseHealingRate + wound.HealingMultiplier * wound.HealingModifier); + AddWoundSeverity(wound.Parent.Owner, wound.Owner, severityDecrease, wound.Parent, wound); + if (wound.SeverityPercentage <= 0.0f) + FullyHealWound(wound.Parent.Owner, wound.Owner, wound.Parent, wound); + } + public bool FullyHealWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, + WoundComponent? wound = null) + { + if (!Resolve(woundableId, ref woundable, false)) + return false; + return Resolve(woundId, ref wound, false) && RemoveWound(woundableId, woundId, true, woundable, wound); + } + + public bool AddHealingModifier (EntityUid woundId, float additionalHealing, WoundComponent? wound = null) + { + if (!Resolve(woundId, ref wound, false)) + return false; + wound.HealingModifier += additionalHealing; + return true; + } + + public bool SetHealingModifier (EntityUid woundId, float newHealingModifier, WoundComponent? wound = null) + { + if (!Resolve(woundId, ref wound, false)) + return false; + wound.HealingModifier = newHealingModifier; + return true; + } + + public bool AddHealingMultipler (EntityUid woundId, float multiplier, WoundComponent? wound = null) + { + if (!Resolve(woundId, ref wound, false)) + return false; + wound.HealingMultiplier += multiplier; + return true; + } + public bool SetHealingMultiplier (EntityUid woundId, float multiplier, WoundComponent? wound = null) + { + if (!Resolve(woundId, ref wound, false)) + return false; + wound.HealingMultiplier = multiplier; + return true; + } } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 38cd35c29410ea..e79e15b799724e 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -9,6 +9,7 @@ namespace Content.Shared.Medical.Wounds.Systems; +//TODO: Convert to use entity hierarchies instead of containers to store wounds public sealed partial class WoundSystem { private readonly Dictionary _cachedWounds = new(); @@ -78,6 +79,7 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC var wounds = _containers.EnsureContainer(woundableId, WoundContainerId); if (!wounds.Insert(woundId)) return false; + wound.Parent = woundable; SetWoundSeverity(woundableId, woundId, wound.SeverityPercentage, woundable, wound); ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); return true; @@ -106,7 +108,7 @@ public bool ApplyRawIntegrityDamage(EntityUid woundableId, FixedPoint2 integrity return true; } - public bool IncreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityIncrease, + public bool AddWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityIncrease, WoundableComponent? woundable = null, WoundComponent? wound = null) { @@ -114,19 +116,7 @@ public bool IncreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, floa return false; if (!Resolve(woundId, ref wound, false)) return false; - ApplyWoundSeverityDelta(woundable, wound, wound.SeverityPercentage + severityIncrease); - return true; - } - - public bool DecreaseWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityDecrease, - WoundableComponent? woundable = null, - WoundComponent? wound = null) - { - if (!Resolve(woundableId, ref woundable, false)) - return false; - if (!Resolve(woundId, ref wound, false)) - return false; - ApplyWoundSeverityDelta(woundable, wound, wound.SeverityPercentage - severityDecrease); + UpdateWoundSeverity(woundable, wound, wound.SeverityPercentage + severityIncrease); return true; } @@ -138,11 +128,11 @@ public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, float sev return false; if (!Resolve(woundId, ref wound, false)) return false; - ApplyWoundSeverityDelta(woundable, wound, severityAmount); + UpdateWoundSeverity(woundable, wound, severityAmount); return true; } - private void ApplyWoundSeverityDelta(WoundableComponent woundable, WoundComponent wound, float newSeverity) + private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wound, float newSeverity) { newSeverity = Math.Clamp(newSeverity, 0.0f, 1.0f); var severityDelta = newSeverity - wound.SeverityPercentage; @@ -150,7 +140,7 @@ private void ApplyWoundSeverityDelta(WoundableComponent woundable, WoundComponen woundable.HealthCapDamage += healthCapDamageDelta; } - public bool RemoveWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, + public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = false, WoundableComponent? woundable = null, WoundComponent? wound = null) { if (!Resolve(woundableId, ref woundable, false)) @@ -160,7 +150,11 @@ public bool RemoveWound(EntityUid woundableId, EntityUid woundId, WoundableCompo var woundContainer = _containers.GetContainer(woundableId, WoundContainerId); if (!woundContainer.Remove(woundId)) return false; - ApplyWoundSeverityDelta(woundable, wound, 0); + UpdateWoundSeverity(woundable, wound, 0); + if (makeScar && wound.ScarWound != null) + { + AddWound(woundableId, wound.ScarWound, woundable); + } return true; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 9c7def77679e3a..3987f9143ef9af 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -12,13 +12,14 @@ namespace Content.Shared.Medical.Wounds.Systems; public sealed partial class WoundSystem : EntitySystem { private const string WoundContainerId = "WoundSystemWounds"; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedBodySystem _body = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; + + public override void Initialize() { CacheWoundData(); @@ -28,6 +29,11 @@ public override void Initialize() SubscribeLocalEvent(OnWoundableCreated); } + public override void Update(float frameTime) + { + UpdateHealing(frameTime); + } + //TODO: Smug will this break networking? - Jez private void OnWoundableCreated(EntityUid uid, WoundableComponent component, ComponentInit args) { From c6121761662a1b8bfa51e658109ef195bc62ea21 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 21:19:19 -0800 Subject: [PATCH 041/148] Interm commit --- .../Medical/Wounds/Components/WoundComponent.cs | 4 ++-- .../Medical/Wounds/Components/WoundableComponent.cs | 6 ++---- .../Medical/Wounds/Systems/WoundSystem.Healing.cs | 11 +++++++---- .../Medical/Wounds/Systems/WoundSystem.Wounding.cs | 9 +++++++-- Content.Shared/Medical/Wounds/Systems/WoundSystem.cs | 10 ---------- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index 362828f5d150bf..f29c4d3df7deae 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -12,12 +12,12 @@ public sealed class WoundComponent : Component { //this is used for caching the parent woundable for use inside and entity query. //wounds should NEVER exist without a parent so this will always have a value - public WoundableComponent Parent = default!; + public EntityUid Parent = default; [DataField("scarWound", customTypeSerializer:typeof(PrototypeIdSerializer))] public string? ScarWound; - [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; + [DataField("healthDamage")] public FixedPoint2 HealthCapDamage; [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 8769745ecbdfd1..73f39e0566d52d 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -20,12 +20,10 @@ public sealed class WoundableComponent : Component [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("health")] public FixedPoint2 Health = -1; + [DataField("startingHealth")] public FixedPoint2 Health = -1; //The maximum health this part can have - [DataField("healthCap", required: true)] - public FixedPoint2 HealthCap; - + [DataField("health", required: true)] public FixedPoint2 HealthCap; //The amount maximum health is decreased by, this is affected by wounds [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index e9e856c0520b59..b18fcc4a310757 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -22,20 +22,23 @@ private void UpdateHealing(float frameTime) private void HealWound(WoundComponent wound) { - if (wound.BaseHealingRate + wound.HealingModifier == 0) + if (wound.Parent == EntityUid.Invalid || wound.BaseHealingRate + wound.HealingModifier == 0) return; //if the wound doesn't heal, do nothing //we want to decrease severity so we need to invert the healing rate to become the severity delta. var severityDecrease = -(wound.BaseHealingRate + wound.HealingMultiplier * wound.HealingModifier); - AddWoundSeverity(wound.Parent.Owner, wound.Owner, severityDecrease, wound.Parent, wound); + AddWoundSeverity(wound.Parent, wound.Owner, severityDecrease,null , wound); if (wound.SeverityPercentage <= 0.0f) - FullyHealWound(wound.Parent.Owner, wound.Owner, wound.Parent, wound); + FullyHealWound(wound.Parent, wound.Owner, null, wound); } public bool FullyHealWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, WoundComponent? wound = null) { if (!Resolve(woundableId, ref woundable, false)) return false; - return Resolve(woundId, ref wound, false) && RemoveWound(woundableId, woundId, true, woundable, wound); + if (!Resolve(woundId, ref wound, false)) + return false; + Logger.Log(LogLevel.Info,"Wound "+ woundId + " Fully Healed!"); + return RemoveWound(woundableId, woundId, true, woundable, wound); } public bool AddHealingModifier (EntityUid woundId, float additionalHealing, WoundComponent? wound = null) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index e79e15b799724e..441f4325305f11 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -79,8 +79,8 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC var wounds = _containers.EnsureContainer(woundableId, WoundContainerId); if (!wounds.Insert(woundId)) return false; - wound.Parent = woundable; - SetWoundSeverity(woundableId, woundId, wound.SeverityPercentage, woundable, wound); + wound.Parent = woundableId; + woundable.HealthCapDamage += wound.SeverityPercentage * wound.HealthCapDamage; ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); return true; } @@ -136,8 +136,11 @@ private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wo { newSeverity = Math.Clamp(newSeverity, 0.0f, 1.0f); var severityDelta = newSeverity - wound.SeverityPercentage; + if (severityDelta == 0) + return; var healthCapDamageDelta = severityDelta * wound.HealthCapDamage; woundable.HealthCapDamage += healthCapDamageDelta; + wound.SeverityPercentage = newSeverity; } public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = false, WoundableComponent? woundable = null, @@ -150,11 +153,13 @@ public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = var woundContainer = _containers.GetContainer(woundableId, WoundContainerId); if (!woundContainer.Remove(woundId)) return false; + wound.Parent = EntityUid.Invalid; UpdateWoundSeverity(woundable, wound, 0); if (makeScar && wound.ScarWound != null) { AddWound(woundableId, wound.ScarWound, woundable); } + EntityManager.DeleteEntity(woundId); return true; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 3987f9143ef9af..de6eeecf78cfe3 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -26,7 +26,6 @@ public override void Initialize() _prototypeManager.PrototypesReloaded += _ => CacheWoundData(); SubscribeLocalEvent(OnBodyAttacked); - SubscribeLocalEvent(OnWoundableCreated); } public override void Update(float frameTime) @@ -34,15 +33,6 @@ public override void Update(float frameTime) UpdateHealing(frameTime); } - //TODO: Smug will this break networking? - Jez - private void OnWoundableCreated(EntityUid uid, WoundableComponent component, ComponentInit args) - { - if (component.Health < 0) //if initial woundable health isn't defined default to the woundCap. - { - component.Health = component.HealthCap; - } - } - private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEvent args) { if (!TryComp(args.Used, out TraumaInflicterComponent? inflicter)) From 96c37f344eb5f56a17ac1518f0730114f8af824b Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 21:52:46 -0800 Subject: [PATCH 042/148] implement woundable healing --- .../Wounds/Components/WoundComponent.cs | 1 + .../Wounds/Components/WoundableComponent.cs | 16 +++++++++-- .../Wounds/Systems/WoundSystem.Healing.cs | 28 +++++++++++++++---- .../Wounds/Systems/WoundSystem.Wounding.cs | 27 +++++++++++++++--- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index f29c4d3df7deae..a3acd193db0084 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -17,6 +17,7 @@ public sealed class WoundComponent : Component [DataField("scarWound", customTypeSerializer:typeof(PrototypeIdSerializer))] public string? ScarWound; + //TODO: implement fixedpoint4 [DataField("healthDamage")] public FixedPoint2 HealthCapDamage; [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 73f39e0566d52d..4cebac9cb90ae9 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -19,13 +19,23 @@ public sealed class WoundableComponent : Component [DataField("traumaPenResistance")] public TraumaModifierSet? TraumaPenResistance; + //TODO: implement FixedPoint4 //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("startingHealth")] public FixedPoint2 Health = -1; + [DataField("startingHealth")] public float Health = -1; //The maximum health this part can have - [DataField("health", required: true)] public FixedPoint2 HealthCap; + [DataField("health", required: true)] public float HealthCap; //The amount maximum health is decreased by, this is affected by wounds - [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; + [DataField("healthCapDamage")] public float HealthCapDamage; + + //How much health per woundTick does this part heal passively + [DataField("baseHealingRate")] public float BaseHealingRate = 0.1f; + + //How much health per woundTick does this part heal ontop of the base rate + [DataField("healingModifier")] public float HealingModifier; + + //How much multiply the Healing modifier + [DataField("healingMultiplier")] public float HealingMultiplier = 1.0f; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! [DataField("integrity", required: true)] diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index b18fcc4a310757..c5e606ce0d9773 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -14,21 +14,37 @@ private void UpdateHealing(float frameTime) return; _healingTimer -= HealingTickRate; - foreach (var wound in EntityQuery()) + foreach (var woundable in EntityQuery()) { - HealWound(wound); + foreach (var wound in GetAllWoundComponents(woundable.Owner)) + { + HealWound(woundable,wound); + } + HealWoundable(woundable); } } - private void HealWound(WoundComponent wound) + private void HealWoundable(WoundableComponent woundable) { - if (wound.Parent == EntityUid.Invalid || wound.BaseHealingRate + wound.HealingModifier == 0) + var healthCap = woundable.HealthCap - woundable.HealthCapDamage; + if (woundable.BaseHealingRate + woundable.HealingModifier == 0 || healthCap <= 0) + return; //if the woundable doesn't heal, do nothing + + var healing = woundable.BaseHealingRate + woundable.HealingMultiplier * woundable.HealingModifier; + if (woundable.Health < healthCap) + woundable.Health = Math.Clamp(woundable.Health+healing, 0.0f, healthCap); + + } + + private void HealWound(WoundableComponent woundable, WoundComponent wound) + { + if (wound.BaseHealingRate + wound.HealingModifier == 0) return; //if the wound doesn't heal, do nothing //we want to decrease severity so we need to invert the healing rate to become the severity delta. var severityDecrease = -(wound.BaseHealingRate + wound.HealingMultiplier * wound.HealingModifier); - AddWoundSeverity(wound.Parent, wound.Owner, severityDecrease,null , wound); + AddWoundSeverity(woundable.Owner, wound.Owner, severityDecrease,woundable , wound); if (wound.SeverityPercentage <= 0.0f) - FullyHealWound(wound.Parent, wound.Owner, null, wound); + FullyHealWound(woundable.Owner, wound.Owner, woundable, wound); } public bool FullyHealWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, WoundComponent? wound = null) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 441f4325305f11..1cc67da3f42aa9 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -43,6 +43,25 @@ public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) return success; } + public IReadOnlyList GetAllWoundEntities(EntityUid woundableId) + { + return _containers.GetContainer(woundableId, WoundContainerId).ContainedEntities; + } + + public IReadOnlyList GetAllWoundComponents(EntityUid woundableId) + { + List components = new(); + foreach (var entityUid in GetAllWoundEntities(woundableId)) + { + if (TryComp(entityUid, out var woundComp)) + { + components.Add((woundComp)); + } + } + return components; + } + + public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) { // TODO wounds before merge turn into tryget @@ -51,7 +70,7 @@ public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); return TryChooseWound(traumaType, modifiedDamage, out var woundId) && - AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage, woundable); + AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage.Float(), woundable); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -80,12 +99,12 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC if (!wounds.Insert(woundId)) return false; wound.Parent = woundableId; - woundable.HealthCapDamage += wound.SeverityPercentage * wound.HealthCapDamage; + woundable.HealthCapDamage += wound.SeverityPercentage * wound.HealthCapDamage.Float(); ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); return true; } - public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage, + public bool ApplyRawWoundDamage(EntityUid woundableId, float woundDamage, WoundableComponent? woundable = null) { if (!Resolve(woundableId, ref woundable, false)) @@ -138,7 +157,7 @@ private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wo var severityDelta = newSeverity - wound.SeverityPercentage; if (severityDelta == 0) return; - var healthCapDamageDelta = severityDelta * wound.HealthCapDamage; + var healthCapDamageDelta = severityDelta * wound.HealthCapDamage.Float(); woundable.HealthCapDamage += healthCapDamageDelta; wound.SeverityPercentage = newSeverity; } From 58e491f996eb8f9f708b585763400e749ae8c1b0 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sun, 27 Nov 2022 06:56:07 +0100 Subject: [PATCH 043/148] Fix errors on adding and removing wounds --- .../Wounds/Components/WoundComponentState.cs | 14 +++++++ .../Wounds/Systems/WoundSystem.Wounding.cs | 39 ++++++++++++++----- .../Medical/Wounds/Systems/WoundSystem.cs | 2 + 3 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Components/WoundComponentState.cs diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs b/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs new file mode 100644 index 00000000000000..b2c66d359fae72 --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Medical.Wounds.Components; + +[Serializable, NetSerializable] +public sealed class WoundComponentState : ComponentState +{ + public EntityUid Parent; + + public WoundComponentState(EntityUid parent) + { + Parent = parent; + } +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 441f4325305f11..1e0e577b056097 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -5,6 +5,7 @@ using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Containers; +using Robust.Shared.GameStates; using Robust.Shared.Random; namespace Content.Shared.Medical.Wounds.Systems; @@ -22,6 +23,22 @@ private void CacheWoundData() { _cachedWounds.Add(traumaType.ID, new WoundTable(traumaType)); } + + SubscribeLocalEvent(OnWoundGetState); + SubscribeLocalEvent(OnWoundHandleState); + } + + private void OnWoundGetState(EntityUid uid, WoundComponent wound, ref ComponentGetState args) + { + args.State = new WoundComponentState(wound.Parent); + } + + private void OnWoundHandleState(EntityUid uid, WoundComponent wound, ref ComponentHandleState args) + { + if (args.Current is not WoundComponentState state) + return; + + wound.Parent = state.Parent; } public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) @@ -70,7 +87,7 @@ private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? m private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableComponent? woundable = null) { - if (!Resolve(woundableId, ref woundable, false)) + if (!Resolve(woundableId, ref woundable, false) || _net.IsClient) return false; var woundId = Spawn(woundPrototypeId, woundableId.ToCoordinates()); @@ -80,6 +97,7 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC if (!wounds.Insert(woundId)) return false; wound.Parent = woundableId; + Dirty(wound); woundable.HealthCapDamage += wound.SeverityPercentage * wound.HealthCapDamage; ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); return true; @@ -146,20 +164,21 @@ private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wo public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = false, WoundableComponent? woundable = null, WoundComponent? wound = null) { - if (!Resolve(woundableId, ref woundable, false)) - return false; - if (!Resolve(woundId, ref wound, false)) - return false; - var woundContainer = _containers.GetContainer(woundableId, WoundContainerId); - if (!woundContainer.Remove(woundId)) + if (!Resolve(woundableId, ref woundable, false) || + !Resolve(woundId, ref wound, false)) return false; + + _containers.RemoveEntity(woundableId, woundId); wound.Parent = EntityUid.Invalid; + Dirty(wound); UpdateWoundSeverity(woundable, wound, 0); + if (makeScar && wound.ScarWound != null) - { AddWound(woundableId, wound.ScarWound, woundable); - } - EntityManager.DeleteEntity(woundId); + + if (_net.IsServer) + EntityManager.DeleteEntity(woundId); + return true; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index de6eeecf78cfe3..a5a75f6b133cfa 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Medical.Wounds.Components; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Containers; +using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -12,6 +13,7 @@ namespace Content.Shared.Medical.Wounds.Systems; public sealed partial class WoundSystem : EntitySystem { private const string WoundContainerId = "WoundSystemWounds"; + [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; From 73592ab291daa442bf44d2babfb961121d859710 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 21:56:51 -0800 Subject: [PATCH 044/148] Fixing Typo --- Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index c5e606ce0d9773..89f4d9f7a1c661 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -10,7 +10,7 @@ public sealed partial class WoundSystem private void UpdateHealing(float frameTime) { _healingTimer += frameTime; - if (_healingTimer < 3f) + if (_healingTimer < HealingTickRate) return; _healingTimer -= HealingTickRate; From 04797b2847c1072c8934e66e1919bea1ff2858e7 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 22:09:27 -0800 Subject: [PATCH 045/148] Fix for healing math --- Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index 89f4d9f7a1c661..8757c60fe6e219 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -30,7 +30,7 @@ private void HealWoundable(WoundableComponent woundable) if (woundable.BaseHealingRate + woundable.HealingModifier == 0 || healthCap <= 0) return; //if the woundable doesn't heal, do nothing - var healing = woundable.BaseHealingRate + woundable.HealingMultiplier * woundable.HealingModifier; + var healing = (woundable.BaseHealingRate + woundable.HealingModifier) * woundable.HealingMultiplier; if (woundable.Health < healthCap) woundable.Health = Math.Clamp(woundable.Health+healing, 0.0f, healthCap); @@ -41,7 +41,7 @@ private void HealWound(WoundableComponent woundable, WoundComponent wound) if (wound.BaseHealingRate + wound.HealingModifier == 0) return; //if the wound doesn't heal, do nothing //we want to decrease severity so we need to invert the healing rate to become the severity delta. - var severityDecrease = -(wound.BaseHealingRate + wound.HealingMultiplier * wound.HealingModifier); + var severityDecrease = -((wound.BaseHealingRate + wound.HealingModifier) * wound.HealingMultiplier); AddWoundSeverity(woundable.Owner, wound.Owner, severityDecrease,woundable , wound); if (wound.SeverityPercentage <= 0.0f) FullyHealWound(woundable.Owner, wound.Owner, woundable, wound); From 9402037a8347bbf028d6fe00540f20bbc64fd546 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 22:12:07 -0800 Subject: [PATCH 046/148] adjust wound application order --- Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 7cfc04889c674b..2e2cf035ec70b1 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -87,7 +87,7 @@ public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); return TryChooseWound(traumaType, modifiedDamage, out var woundId) && - AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage.Float(), woundable); + ApplyRawWoundDamage(woundableId, modifiedDamage.Float(), woundable) && AddWound(woundableId, woundId, woundable); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) From 0a2018fb8dfb7ed3f7e5d3416321ebf932beee43 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sun, 27 Nov 2022 07:33:12 +0100 Subject: [PATCH 047/148] Change floats with fixedpoint2 --- .../Wounds/Components/WoundComponent.cs | 9 +++---- .../Wounds/Components/WoundableComponent.cs | 12 ++++----- .../Wounds/Systems/WoundSystem.Healing.cs | 16 ++++++------ .../Wounds/Systems/WoundSystem.Wounding.cs | 25 +++++++++---------- .../Medical/Wounds/Systems/WoundSystem.cs | 3 +-- 5 files changed, 31 insertions(+), 34 deletions(-) diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs index a3acd193db0084..79ebb240b48363 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponent.cs @@ -17,19 +17,18 @@ public sealed class WoundComponent : Component [DataField("scarWound", customTypeSerializer:typeof(PrototypeIdSerializer))] public string? ScarWound; - //TODO: implement fixedpoint4 [DataField("healthDamage")] public FixedPoint2 HealthCapDamage; [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; - [DataField("severityPercentage")] public float SeverityPercentage = 1.0f; + [DataField("severityPercentage")] public FixedPoint2 Severity = 100; //How many severity points per woundTick does this part heal passively - [DataField("baseHealingRate")] public float BaseHealingRate = 0.05f; + [DataField("baseHealingRate")] public FixedPoint2 BaseHealingRate = 0.05f; //How many severity points per woundTick does this part heal ontop of the base rate - [DataField("healingModifier")] public float HealingModifier; + [DataField("healingModifier")] public FixedPoint2 HealingModifier; //How much to multiply the Healing modifier - [DataField("healingMultiplier")] public float HealingMultiplier = 1.0f; + [DataField("healingMultiplier")] public FixedPoint2 HealingMultiplier = 1.0f; } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs index 4cebac9cb90ae9..7f058d0fcf8ee0 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponent.cs @@ -21,21 +21,21 @@ public sealed class WoundableComponent : Component //TODO: implement FixedPoint4 //How much health does this woundable have, when this reaches 0, it starts taking structural damage - [DataField("startingHealth")] public float Health = -1; + [DataField("startingHealth")] public FixedPoint2 Health = -1; //The maximum health this part can have - [DataField("health", required: true)] public float HealthCap; + [DataField("health", required: true)] public FixedPoint2 HealthCap; //The amount maximum health is decreased by, this is affected by wounds - [DataField("healthCapDamage")] public float HealthCapDamage; + [DataField("healthCapDamage")] public FixedPoint2 HealthCapDamage; //How much health per woundTick does this part heal passively - [DataField("baseHealingRate")] public float BaseHealingRate = 0.1f; + [DataField("baseHealingRate")] public FixedPoint2 BaseHealingRate = 0.1f; //How much health per woundTick does this part heal ontop of the base rate - [DataField("healingModifier")] public float HealingModifier; + [DataField("healingModifier")] public FixedPoint2 HealingModifier; //How much multiply the Healing modifier - [DataField("healingMultiplier")] public float HealingMultiplier = 1.0f; + [DataField("healingMultiplier")] public FixedPoint2 HealingMultiplier = 1.0f; //How well is this woundable holding up, when this reaches 0 the entity is destroyed/gibbed! [DataField("integrity", required: true)] diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index 89f4d9f7a1c661..a4894dc6140637 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -1,4 +1,5 @@ -using Content.Shared.Medical.Wounds.Components; +using Content.Shared.FixedPoint; +using Content.Shared.Medical.Wounds.Components; namespace Content.Shared.Medical.Wounds.Systems; @@ -32,8 +33,7 @@ private void HealWoundable(WoundableComponent woundable) var healing = woundable.BaseHealingRate + woundable.HealingMultiplier * woundable.HealingModifier; if (woundable.Health < healthCap) - woundable.Health = Math.Clamp(woundable.Health+healing, 0.0f, healthCap); - + woundable.Health = FixedPoint2.Clamp(woundable.Health+healing, FixedPoint2.Zero, healthCap); } private void HealWound(WoundableComponent woundable, WoundComponent wound) @@ -43,7 +43,7 @@ private void HealWound(WoundableComponent woundable, WoundComponent wound) //we want to decrease severity so we need to invert the healing rate to become the severity delta. var severityDecrease = -(wound.BaseHealingRate + wound.HealingMultiplier * wound.HealingModifier); AddWoundSeverity(woundable.Owner, wound.Owner, severityDecrease,woundable , wound); - if (wound.SeverityPercentage <= 0.0f) + if (wound.Severity <= 0.0f) FullyHealWound(woundable.Owner, wound.Owner, woundable, wound); } public bool FullyHealWound(EntityUid woundableId, EntityUid woundId, WoundableComponent? woundable = null, @@ -57,7 +57,7 @@ public bool FullyHealWound(EntityUid woundableId, EntityUid woundId, WoundableCo return RemoveWound(woundableId, woundId, true, woundable, wound); } - public bool AddHealingModifier (EntityUid woundId, float additionalHealing, WoundComponent? wound = null) + public bool AddHealingModifier (EntityUid woundId, FixedPoint2 additionalHealing, WoundComponent? wound = null) { if (!Resolve(woundId, ref wound, false)) return false; @@ -65,7 +65,7 @@ public bool AddHealingModifier (EntityUid woundId, float additionalHealing, Woun return true; } - public bool SetHealingModifier (EntityUid woundId, float newHealingModifier, WoundComponent? wound = null) + public bool SetHealingModifier (EntityUid woundId, FixedPoint2 newHealingModifier, WoundComponent? wound = null) { if (!Resolve(woundId, ref wound, false)) return false; @@ -73,7 +73,7 @@ public bool SetHealingModifier (EntityUid woundId, float newHealingModifier, Wou return true; } - public bool AddHealingMultipler (EntityUid woundId, float multiplier, WoundComponent? wound = null) + public bool AddHealingMultipler (EntityUid woundId, FixedPoint2 multiplier, WoundComponent? wound = null) { if (!Resolve(woundId, ref wound, false)) return false; @@ -81,7 +81,7 @@ public bool AddHealingMultipler (EntityUid woundId, float multiplier, WoundCompo return true; } - public bool SetHealingMultiplier (EntityUid woundId, float multiplier, WoundComponent? wound = null) + public bool SetHealingMultiplier (EntityUid woundId, FixedPoint2 multiplier, WoundComponent? wound = null) { if (!Resolve(woundId, ref wound, false)) return false; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 7cfc04889c674b..6fd3b31c5c41ee 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -87,7 +87,7 @@ public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); return TryChooseWound(traumaType, modifiedDamage, out var woundId) && - AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage.Float(), woundable); + AddWound(woundableId, woundId, woundable) && ApplyRawWoundDamage(woundableId, modifiedDamage, woundable); } private FixedPoint2 ApplyTraumaModifiers(string traumaType, TraumaModifierSet? modifiers, FixedPoint2 damage) @@ -117,13 +117,12 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC return false; wound.Parent = woundableId; Dirty(wound); - woundable.HealthCapDamage += wound.SeverityPercentage * wound.HealthCapDamage.Float(); + woundable.HealthCapDamage += wound.Severity * wound.HealthCapDamage; ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); return true; } - public bool ApplyRawWoundDamage(EntityUid woundableId, float woundDamage, - WoundableComponent? woundable = null) + public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage, WoundableComponent? woundable = null) { if (!Resolve(woundableId, ref woundable, false)) return false; @@ -145,7 +144,7 @@ public bool ApplyRawIntegrityDamage(EntityUid woundableId, FixedPoint2 integrity return true; } - public bool AddWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityIncrease, + public bool AddWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityIncrease, WoundableComponent? woundable = null, WoundComponent? wound = null) { @@ -153,11 +152,11 @@ public bool AddWoundSeverity(EntityUid woundableId, EntityUid woundId, float sev return false; if (!Resolve(woundId, ref wound, false)) return false; - UpdateWoundSeverity(woundable, wound, wound.SeverityPercentage + severityIncrease); + UpdateWoundSeverity(woundable, wound, wound.Severity + severityIncrease); return true; } - public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, float severityAmount, + public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, FixedPoint2 severityAmount, WoundableComponent? woundable = null, WoundComponent? wound = null) { @@ -169,15 +168,15 @@ public bool SetWoundSeverity(EntityUid woundableId, EntityUid woundId, float sev return true; } - private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wound, float newSeverity) + private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wound, FixedPoint2 newSeverity) { - newSeverity = Math.Clamp(newSeverity, 0.0f, 1.0f); - var severityDelta = newSeverity - wound.SeverityPercentage; + newSeverity = FixedPoint2.Clamp(newSeverity, FixedPoint2.Zero, 100); + var severityDelta = newSeverity - wound.Severity; if (severityDelta == 0) return; - var healthCapDamageDelta = severityDelta * wound.HealthCapDamage.Float(); + var healthCapDamageDelta = severityDelta * wound.HealthCapDamage; woundable.HealthCapDamage += healthCapDamageDelta; - wound.SeverityPercentage = newSeverity; + wound.Severity = newSeverity; } public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = false, WoundableComponent? woundable = null, @@ -190,7 +189,7 @@ public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = _containers.RemoveEntity(woundableId, woundId); wound.Parent = EntityUid.Invalid; Dirty(wound); - UpdateWoundSeverity(woundable, wound, 0); + UpdateWoundSeverity(woundable, wound, FixedPoint2.Zero); if (makeScar && wound.ScarWound != null) AddWound(woundableId, wound.ScarWound, woundable); diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index a5a75f6b133cfa..76fe2d95fed6ef 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -13,6 +13,7 @@ namespace Content.Shared.Medical.Wounds.Systems; public sealed partial class WoundSystem : EntitySystem { private const string WoundContainerId = "WoundSystemWounds"; + [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; @@ -20,8 +21,6 @@ public sealed partial class WoundSystem : EntitySystem [Dependency] private readonly SharedBodySystem _body = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; - - public override void Initialize() { CacheWoundData(); From fe42976d415db4f7963cf39584a79b531c3cfe7d Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sun, 27 Nov 2022 07:41:23 +0100 Subject: [PATCH 048/148] Add component states --- .../Wounds/Components/WoundComponentState.cs | 19 ++++- .../Components/WoundableComponentState.cs | 46 ++++++++++++ .../Wounds/Systems/WoundSystem.Wounding.cs | 17 ----- .../Medical/Wounds/Systems/WoundSystem.cs | 71 +++++++++++++++++++ 4 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 Content.Shared/Medical/Wounds/Components/WoundableComponentState.cs diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs b/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs index b2c66d359fae72..d096284efbac26 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Serialization; +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; namespace Content.Shared.Medical.Wounds.Components; @@ -6,9 +7,23 @@ namespace Content.Shared.Medical.Wounds.Components; public sealed class WoundComponentState : ComponentState { public EntityUid Parent; + public string? ScarWound; + public FixedPoint2 HealthCapDamage; + public FixedPoint2 IntegrityDamage; + public FixedPoint2 Severity; + public FixedPoint2 BaseHealingRate; + public FixedPoint2 HealingModifier; + public FixedPoint2 HealingMultiplier; - public WoundComponentState(EntityUid parent) + public WoundComponentState(EntityUid parent, string? scarWound, FixedPoint2 healthCapDamage, FixedPoint2 integrityDamage, FixedPoint2 severity, FixedPoint2 baseHealingRate, FixedPoint2 healingModifier, FixedPoint2 healingMultiplier) { Parent = parent; + ScarWound = scarWound; + HealthCapDamage = healthCapDamage; + IntegrityDamage = integrityDamage; + Severity = severity; + BaseHealingRate = baseHealingRate; + HealingModifier = healingModifier; + HealingMultiplier = healingMultiplier; } } diff --git a/Content.Shared/Medical/Wounds/Components/WoundableComponentState.cs b/Content.Shared/Medical/Wounds/Components/WoundableComponentState.cs new file mode 100644 index 00000000000000..bacaff8134200f --- /dev/null +++ b/Content.Shared/Medical/Wounds/Components/WoundableComponentState.cs @@ -0,0 +1,46 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared.Medical.Wounds.Components; + +[Serializable, NetSerializable] +public sealed class WoundableComponentState : ComponentState +{ + public HashSet? AllowedTraumaTypes; + public TraumaModifierSet? TraumaResistance; + public TraumaModifierSet? TraumaPenResistance; + public FixedPoint2 Health; + public FixedPoint2 HealthCap; + public FixedPoint2 HealthCapDamage; + public FixedPoint2 BaseHealingRate; + public FixedPoint2 HealingModifier; + public FixedPoint2 HealingMultiplier; + public FixedPoint2 Integrity; + public string? DestroyWoundId; + + public WoundableComponentState( + HashSet? allowedTraumaTypes, + TraumaModifierSet? traumaResistance, + TraumaModifierSet? traumaPenResistance, + FixedPoint2 health, + FixedPoint2 healthCap, + FixedPoint2 healthCapDamage, + FixedPoint2 baseHealingRate, + FixedPoint2 healingModifier, + FixedPoint2 healingMultiplier, + FixedPoint2 integrity, + string? destroyWoundId) + { + AllowedTraumaTypes = allowedTraumaTypes; + TraumaResistance = traumaResistance; + TraumaPenResistance = traumaPenResistance; + Health = health; + HealthCap = healthCap; + HealthCapDamage = healthCapDamage; + BaseHealingRate = baseHealingRate; + HealingModifier = healingModifier; + HealingMultiplier = healingMultiplier; + Integrity = integrity; + DestroyWoundId = destroyWoundId; + } +} diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 1dcf5de9ec1f4c..9b213cd79f92a3 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -5,7 +5,6 @@ using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Containers; -using Robust.Shared.GameStates; using Robust.Shared.Random; namespace Content.Shared.Medical.Wounds.Systems; @@ -23,22 +22,6 @@ private void CacheWoundData() { _cachedWounds.Add(traumaType.ID, new WoundTable(traumaType)); } - - SubscribeLocalEvent(OnWoundGetState); - SubscribeLocalEvent(OnWoundHandleState); - } - - private void OnWoundGetState(EntityUid uid, WoundComponent wound, ref ComponentGetState args) - { - args.State = new WoundComponentState(wound.Parent); - } - - private void OnWoundHandleState(EntityUid uid, WoundComponent wound, ref ComponentHandleState args) - { - if (args.Current is not WoundComponentState state) - return; - - wound.Parent = state.Parent; } public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 76fe2d95fed6ef..f2a7e584c0dc82 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Medical.Wounds.Components; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Containers; +using Robust.Shared.GameStates; using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -26,9 +27,79 @@ public override void Initialize() CacheWoundData(); _prototypeManager.PrototypesReloaded += _ => CacheWoundData(); + SubscribeLocalEvent(OnWoundableGetState); + SubscribeLocalEvent(OnWoundableHandleState); + + SubscribeLocalEvent(OnWoundGetState); + SubscribeLocalEvent(OnWoundHandleState); + SubscribeLocalEvent(OnBodyAttacked); } + private void OnWoundableGetState(EntityUid uid, WoundableComponent component, ref ComponentGetState args) + { + args.State = new WoundableComponentState( + component.AllowedTraumaTypes, + component.TraumaResistance, + component.TraumaPenResistance, + component.Health, + component.HealthCap, + component.HealthCapDamage, + component.BaseHealingRate, + component.HealingModifier, + component.HealingMultiplier, + component.Integrity, + component.DestroyWoundId + ); + } + + private void OnWoundableHandleState(EntityUid uid, WoundableComponent component, ref ComponentHandleState args) + { + if (args.Current is not WoundableComponentState state) + return; + + component.AllowedTraumaTypes = state.AllowedTraumaTypes; + component.TraumaResistance = state.TraumaResistance; + component.TraumaPenResistance = state.TraumaPenResistance; + component.Health = state.Health; + component.HealthCap = state.HealthCap; + component.HealthCapDamage = state.HealthCapDamage; + component.BaseHealingRate = state.BaseHealingRate; + component.HealingModifier = state.HealingModifier; + component.HealingMultiplier = state.HealingMultiplier; + component.Integrity = state.Integrity; + component.DestroyWoundId = state.DestroyWoundId; + } + + private void OnWoundGetState(EntityUid uid, WoundComponent wound, ref ComponentGetState args) + { + args.State = new WoundComponentState( + wound.Parent, + wound.ScarWound, + wound.HealthCapDamage, + wound.IntegrityDamage, + wound.Severity, + wound.BaseHealingRate, + wound.HealingModifier, + wound.HealingMultiplier + ); + } + + private void OnWoundHandleState(EntityUid uid, WoundComponent wound, ref ComponentHandleState args) + { + if (args.Current is not WoundComponentState state) + return; + + wound.Parent = state.Parent; + wound.ScarWound = state.ScarWound; + wound.HealthCapDamage = state.HealthCapDamage; + wound.IntegrityDamage = state.IntegrityDamage; + wound.Severity = state.Severity; + wound.BaseHealingRate = state.BaseHealingRate; + wound.HealingModifier = state.HealingModifier; + wound.HealingMultiplier = state.HealingMultiplier; + } + public override void Update(float frameTime) { UpdateHealing(frameTime); From 611583d4f023e3f11e51c80808137cdd33ddea8b Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sun, 27 Nov 2022 08:42:12 +0100 Subject: [PATCH 049/148] Move traumas inflicted to damage type prototype --- .../Damage/Prototypes/DamageTypePrototype.cs | 9 +++ .../Damage/Systems/DamageableSystem.cs | 1 - .../Wounds/Prototypes/TraumaPrototype.cs | 2 - .../Wounds/Systems/WoundSystem.Healing.cs | 2 + .../Wounds/Systems/WoundSystem.Wounding.cs | 61 +++++++++++++------ .../Medical/Wounds/Systems/WoundSystem.cs | 10 +-- .../Medical/Wounds/TraumaSpecifier.cs | 11 ++-- Resources/Prototypes/Damage/types.yml | 3 +- .../Entities/Objects/Weapons/Melee/sword.yml | 3 +- 9 files changed, 64 insertions(+), 38 deletions(-) diff --git a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index c4fe397e015473..91054dff10e7be 100644 --- a/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -1,4 +1,6 @@ +using Content.Shared.Medical.Wounds.Prototypes; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Damage.Prototypes { @@ -21,5 +23,12 @@ public sealed class DamageTypePrototype : IPrototype /// [DataField("armorFlatPrice")] public double ArmorPriceFlat { get; set; } + + /// + /// The trauma to apply for wounds when this damage is applied + /// + /// + [DataField("trauma", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? Trauma; } } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index bd6b0c1da7a669..ffba3bf30f8bd8 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -144,7 +144,6 @@ public void DamageChanged(DamageableComponent component, DamageSpecifier? damage { if (!uid.HasValue || !Resolve(uid.Value, ref damageable, false)) { - // TODO BODY SYSTEM pass damage onto body system return null; } diff --git a/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs index 23bdb68946f6e4..210cbe80344550 100644 --- a/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs +++ b/Content.Shared/Medical/Wounds/Prototypes/TraumaPrototype.cs @@ -9,8 +9,6 @@ public sealed class TraumaPrototype : IPrototype { [IdDataField] public string ID { get; init; } = string.Empty; - //Note: these should be defined in order of severity! - //list of possible wounds sorted by their trauma cutoffs [DataField("wounds", required: true, customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] public SortedDictionary Wounds { get; init; } = new(); diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs index f13cb900906aee..30d2750ff60dc7 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Healing.cs @@ -17,10 +17,12 @@ private void UpdateHealing(float frameTime) foreach (var woundable in EntityQuery()) { + // TODO wounds before merge iterate wounds separately foreach (var wound in GetAllWoundComponents(woundable.Owner)) { HealWound(woundable,wound); } + HealWoundable(woundable); } } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index 9b213cd79f92a3..a8dee520c04fa5 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -1,6 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Coordinates; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Prototypes; @@ -24,51 +26,70 @@ private void CacheWoundData() } } - public bool TryApplyTrauma(EntityUid target, TraumaInflicterComponent inflicter) + public bool TryApplyTrauma(EntityUid? origin, EntityUid target, DamageSpecifier damage) { var success = false; - foreach (var (traumaType, trauma) in inflicter.Traumas) + + // TODO wounds before merge use the weapon's id, not the user's (origin is the player who used the sword) + var inflicter = CompOrNull(origin); + + foreach (var (type, amount) in damage.DamageDict) { - success |= ApplyTraumaToPart(target, traumaType, trauma); + if (amount <= 0 || + _prototypeManager.Index(type).Trauma is not { } traumaId) + { + continue; + } + + success |= ApplyTraumaToPart(target, traumaId, amount); // TODO wounds before merge add support for non penetrating traumas - var type = trauma.PenTraumaType ?? traumaType; + if (inflicter == null || !inflicter.Traumas.TryGetValue(traumaId, out var trauma)) + continue; - if (trauma.PenetrationChance > 0 && !_random.Prob(trauma.PenetrationChance.Float())) + var penType = trauma.PenType ?? traumaId; + + if (trauma.PenChance > 0 && !_random.Prob(trauma.PenChance.Float())) continue; - success |= ApplyTraumaToPart(target, type, trauma); + success |= ApplyTraumaToPart(target, penType, amount); } return success; } - public IReadOnlyList GetAllWoundEntities(EntityUid woundableId) + public bool TryGetAllWoundEntities(EntityUid woundableId, [NotNullWhen(true)] out IReadOnlyList? wounds) { - return _containers.GetContainer(woundableId, WoundContainerId).ContainedEntities; + if (!_containers.TryGetContainer(woundableId, WoundContainerId, out var container) || + container.ContainedEntities.Count == 0) + { + wounds = null; + return false; + } + + wounds = container.ContainedEntities; + return true; } - public IReadOnlyList GetAllWoundComponents(EntityUid woundableId) + public IEnumerable GetAllWoundComponents(EntityUid woundableId) { - List components = new(); - foreach (var entityUid in GetAllWoundEntities(woundableId)) + if (!TryGetAllWoundEntities(woundableId, out var wounds)) + yield break; + + foreach (var woundId in wounds) { - if (TryComp(entityUid, out var woundComp)) - { - components.Add((woundComp)); - } + if (TryComp(woundId, out var woundComp)) + yield return woundComp; } - return components; } - - public bool ApplyTraumaToPart(EntityUid target, string traumaType, TraumaDamage traumaDamage) + public bool ApplyTraumaToPart(EntityUid target, string traumaType, FixedPoint2 damage) { // TODO wounds before merge turn into tryget if (GetValidWoundable(target, traumaType) is not {Target: var woundableId, Woundable: var woundable}) return false; - var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, traumaDamage.Damage); + var modifiedDamage = ApplyTraumaModifiers(traumaType, woundable.TraumaResistance, damage); return TryChooseWound(traumaType, modifiedDamage, out var woundId) && ApplyRawWoundDamage(woundableId, modifiedDamage, woundable) && AddWound(woundableId, woundId, woundable); } @@ -112,7 +133,7 @@ public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage, woundable.HealthCapDamage += woundDamage; if (woundable.HealthCapDamage < FixedPoint2.Zero) ApplyRawIntegrityDamage(woundableId, -woundable.HealthCapDamage, woundable); - woundable.HealthCapDamage = 0; + woundable.HealthCapDamage = FixedPoint2.Zero; return true; } diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index f2a7e584c0dc82..59d5382ca9c1b7 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -1,8 +1,8 @@ using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; +using Content.Shared.Damage; using Content.Shared.Medical.Wounds.Components; -using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Network; @@ -33,7 +33,7 @@ public override void Initialize() SubscribeLocalEvent(OnWoundGetState); SubscribeLocalEvent(OnWoundHandleState); - SubscribeLocalEvent(OnBodyAttacked); + SubscribeLocalEvent(OnBodyDamaged); } private void OnWoundableGetState(EntityUid uid, WoundableComponent component, ref ComponentGetState args) @@ -105,13 +105,13 @@ public override void Update(float frameTime) UpdateHealing(frameTime); } - private void OnBodyAttacked(EntityUid uid, BodyComponent component, AttackedEvent args) + private void OnBodyDamaged(EntityUid uid, BodyComponent component, DamageChangedEvent args) { - if (!TryComp(args.Used, out TraumaInflicterComponent? inflicter)) + if (!args.DamageIncreased || args.DamageDelta == null) return; var parts = _body.GetBodyChildren(uid, component).ToList(); var part = _random.Pick(parts); - TryApplyTrauma(part.Id, inflicter); + TryApplyTrauma(args.Origin, part.Id, args.DamageDelta); } } diff --git a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs index 5fb1d46d0bb053..28e55abdb3fedc 100644 --- a/Content.Shared/Medical/Wounds/TraumaSpecifier.cs +++ b/Content.Shared/Medical/Wounds/TraumaSpecifier.cs @@ -7,10 +7,7 @@ namespace Content.Shared.Medical.Wounds; [DataRecord, Serializable, NetSerializable] public record struct TraumaDamage( - [field: DataField("damage", required: true)] - FixedPoint2 Damage, //Damage represents the amount of trauma dealt - [field: DataField("penChance", required: false)] - FixedPoint2 PenetrationChance, - [field: DataField("penType", required: false, customTypeSerializer: typeof(PrototypeIdSerializer))] - string? PenTraumaType = null -); //Penetration represents how much this damage penetrates to hit child parts + FixedPoint2 PenChance, + [field: DataField("penType", customTypeSerializer: typeof(PrototypeIdSerializer))] + string? PenType = null +); diff --git a/Resources/Prototypes/Damage/types.yml b/Resources/Prototypes/Damage/types.yml index 642f7e3440245d..d3c71a96bb1ead 100644 --- a/Resources/Prototypes/Damage/types.yml +++ b/Resources/Prototypes/Damage/types.yml @@ -18,7 +18,7 @@ id: Blunt armorCoefficientPrice: 2 armorFlatPrice: 10 - + - type: damageType id: Cellular armorCoefficientPrice: 5 @@ -59,6 +59,7 @@ id: Slash armorCoefficientPrice: 2 armorFlatPrice: 10 + trauma: Slash # Damage represent structures internal integrity. # Exclusive for structures such as walls, airlocks and others. diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 9c7c0731e62542..87e4f5f311b23a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -21,9 +21,8 @@ - CaptainSabre - type: DisarmMalus - type: TraumaInflicter # TODO wounds before merge - traumas: + damage: Slash: - damage: 20 penChance: 0.3 penModifier: 0.2 From 9c89c037b9047bd7dcbb6ebe348a285c39cd9691 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 23:45:17 -0800 Subject: [PATCH 050/148] Started creating Medical Hud --- .../Medical/Controls/BodyPartEntry.xaml | 23 +++++++++++ .../Medical/Controls/BodyPartEntry.xaml.cs | 10 +++++ .../Systems/Medical/Controls/MedicalDoll.cs | 39 +++++++++++++++++++ .../Systems/Medical/Controls/WoundEntry.xaml | 12 ++++++ .../Medical/Controls/WoundEntry.xaml.cs | 10 +++++ .../Systems/Medical/MedicalUiController.cs | 30 ++++++++++++++ .../Medical/Windows/MedicalWindow.xaml | 28 +++++++++++++ .../Medical/Windows/MedicalWindow.xaml.cs | 16 ++++++++ 8 files changed, 168 insertions(+) create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml.cs create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml.cs create mode 100644 Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs create mode 100644 Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml new file mode 100644 index 00000000000000..9a8ca1d4204819 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml @@ -0,0 +1,23 @@ + + diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml.cs new file mode 100644 index 00000000000000..f83baf18a6c915 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/BodyPartEntry.xaml.cs @@ -0,0 +1,10 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +[GenerateTypedNameReferences] +public sealed partial class BodyPartEntry : BoxContainer +{ +} diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs new file mode 100644 index 00000000000000..a7231fcd201839 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs @@ -0,0 +1,39 @@ +using Robust.Client.GameObjects; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +public sealed class MedicalDoll : Control +{ + public readonly SpriteView TargetDummy; + public readonly Label TargetName; + + + public MedicalDoll() + { + TargetDummy = new SpriteView() + { + HorizontalAlignment = HAlignment.Center, + }; + TargetName = new Label() + { + Text = "Self", + Align = Label.AlignMode.Center, + HorizontalAlignment = HAlignment.Center, + }; + AddChild(TargetName); + AddChild(TargetDummy); + } + + public void UpdateUI(SpriteComponent? targetSprite, string? identity) + { + TargetDummy.Sprite = targetSprite; + if (targetSprite == null) + { + TargetName.Text = "Error No Target"; + return; + } + TargetName.Text = identity != null ? "Self" : identity; + } +} diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml new file mode 100644 index 00000000000000..590ee9d3dd8d37 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml @@ -0,0 +1,12 @@ + + diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml.cs new file mode 100644 index 00000000000000..da26b8071d44aa --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/WoundEntry.xaml.cs @@ -0,0 +1,10 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +[GenerateTypedNameReferences] +public sealed partial class WoundEntry : BoxContainer +{ +} diff --git a/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs b/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs new file mode 100644 index 00000000000000..6f76ebee3b8010 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs @@ -0,0 +1,30 @@ +using Content.Client.Gameplay; +using Content.Shared.Medical.Wounds.Systems; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controllers; + +namespace Content.Client.UserInterface.Systems.Medical; + +public sealed class MedicalUIController : UIController, IOnStateEntered, IOnStateExited, + IOnSystemChanged +{ + [Dependency] private readonly IEntityManager _entities = default!; + [UISystemDependency] private readonly WoundSystem _woundSystem = default!; + + + public void OnStateEntered(GameplayState state) + { + } + + public void OnStateExited(GameplayState state) + { + } + + public void OnSystemLoaded(WoundSystem system) + { + } + + public void OnSystemUnloaded(WoundSystem system) + { + } +} diff --git a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml new file mode 100644 index 00000000000000..37ce0f1f14c892 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs new file mode 100644 index 00000000000000..5557075369cb40 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs @@ -0,0 +1,16 @@ +using Content.Client.UserInterface.Controls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.UserInterface.Systems.Medical.Windows; + +[GenerateTypedNameReferences] +public sealed partial class MedicalWindow : FancyWindow +{ + public MedicalWindow() + { + RobustXamlLoader.Load(this); + LayoutContainer.SetAnchorAndMarginPreset(this, LayoutContainer.LayoutPreset.Center); + } +} From 54490a336a6c8301fa57f8fc12b4a4234a5da4c5 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 26 Nov 2022 23:48:13 -0800 Subject: [PATCH 051/148] Moved Medical status into it's own control for tabbing --- .../Medical/Controls/MedicalStatus.xaml | 23 +++++++++++++++++++ .../Medical/Controls/MedicalStatus.xaml.cs | 10 ++++++++ .../Medical/Windows/MedicalWindow.xaml | 19 --------------- 3 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml new file mode 100644 index 00000000000000..b2a9ec8569a66d --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs new file mode 100644 index 00000000000000..9c0984ace8a00c --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs @@ -0,0 +1,10 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +[GenerateTypedNameReferences] +public sealed partial class MedicalStatus : BoxContainer +{ +} diff --git a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml index 37ce0f1f14c892..9c8ab55cfead53 100644 --- a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml +++ b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml @@ -6,23 +6,4 @@ HorizontalExpand="True" Title="Medical" VerticalExpand="True"> - - - - - - - From 6638c20e5a869f2f97736414344b0403768f88c2 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Fri, 16 Dec 2022 20:49:55 -0800 Subject: [PATCH 052/148] Implemented UI frontend for Firstaid/treatment window --- .../Systems/Medical/Controls/BasicStatus.xaml | 32 ++++++++ .../Medical/Controls/BasicStatus.xaml.cs | 9 +++ .../Systems/Medical/Controls/MedicalDoll.cs | 39 --------- .../Systems/Medical/Controls/MedicalDoll.xaml | 12 +++ .../Medical/Controls/MedicalDoll.xaml.cs | 21 +++++ .../Medical/Controls/MedicalStatus.xaml | 38 +++++---- .../Medical/Controls/MedicalStatus.xaml.cs | 7 +- .../Systems/Medical/Controls/WoundList.xaml | 7 ++ .../Medical/Controls/WoundList.xaml.cs | 36 +++++++++ .../Systems/Medical/MedicalUiController.cs | 79 +++++++++++++++++++ .../Medical/Windows/MedicalWindow.xaml | 15 ++++ .../Medical/Windows/MedicalWindow.xaml.cs | 8 +- .../MenuBar/GameTopMenuBarUIController.cs | 5 +- .../MenuBar/Widgets/GameTopMenuBar.xaml | 10 +++ .../Wounds/Components/WoundComponent.cs | 6 +- .../Wounds/Components/WoundComponentState.cs | 6 +- .../Wounds/Systems/WoundSystem.Wounding.cs | 7 +- .../Medical/Wounds/Systems/WoundSystem.cs | 1 + 18 files changed, 272 insertions(+), 66 deletions(-) create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml.cs delete mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml.cs create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml create mode 100644 Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml.cs diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml new file mode 100644 index 00000000000000..813b3b91822384 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml.cs new file mode 100644 index 00000000000000..a026b2cf8a2655 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml.cs @@ -0,0 +1,9 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +[GenerateTypedNameReferences] +public sealed partial class BasicStatus : BoxContainer +{ +} diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs deleted file mode 100644 index a7231fcd201839..00000000000000 --- a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Robust.Client.GameObjects; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; - -namespace Content.Client.UserInterface.Systems.Medical.Controls; - -public sealed class MedicalDoll : Control -{ - public readonly SpriteView TargetDummy; - public readonly Label TargetName; - - - public MedicalDoll() - { - TargetDummy = new SpriteView() - { - HorizontalAlignment = HAlignment.Center, - }; - TargetName = new Label() - { - Text = "Self", - Align = Label.AlignMode.Center, - HorizontalAlignment = HAlignment.Center, - }; - AddChild(TargetName); - AddChild(TargetDummy); - } - - public void UpdateUI(SpriteComponent? targetSprite, string? identity) - { - TargetDummy.Sprite = targetSprite; - if (targetSprite == null) - { - TargetName.Text = "Error No Target"; - return; - } - TargetName.Text = identity != null ? "Self" : identity; - } -} diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml new file mode 100644 index 00000000000000..82116f8a588837 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml @@ -0,0 +1,12 @@ + + + + + + diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml.cs new file mode 100644 index 00000000000000..d1e533488d95fd --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalDoll.xaml.cs @@ -0,0 +1,21 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +[GenerateTypedNameReferences] +public sealed partial class MedicalDoll : BoxContainer +{ + public MedicalDoll() + { + RobustXamlLoader.Load(this); + } + + public void UpdateUI(SpriteComponent? frontDummy, SpriteComponent? backDummy) + { + FrontDummy.Sprite = frontDummy; + BackDummy.Sprite = backDummy; + } +} diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml index b2a9ec8569a66d..6979a8230df563 100644 --- a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml @@ -1,23 +1,29 @@  - - - - - diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs index 9c0984ace8a00c..504fab6cb1e9c2 100644 --- a/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs +++ b/Content.Client/UserInterface/Systems/Medical/Controls/MedicalStatus.xaml.cs @@ -1,5 +1,5 @@ using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface; +using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; namespace Content.Client.UserInterface.Systems.Medical.Controls; @@ -7,4 +7,9 @@ namespace Content.Client.UserInterface.Systems.Medical.Controls; [GenerateTypedNameReferences] public sealed partial class MedicalStatus : BoxContainer { + public void UpdateUI(SpriteComponent? frontDummy, SpriteComponent? backDummy, string? targetName) + { + PreviewDoll.UpdateUI(frontDummy, backDummy); + TargetName.Text = targetName ?? "Error No Target"; + } } diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml new file mode 100644 index 00000000000000..f266a427ec55e5 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml @@ -0,0 +1,7 @@ + + diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml.cs new file mode 100644 index 00000000000000..730ad45131a813 --- /dev/null +++ b/Content.Client/UserInterface/Systems/Medical/Controls/WoundList.xaml.cs @@ -0,0 +1,36 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.UserInterface.Systems.Medical.Controls; + +[GenerateTypedNameReferences] +public sealed partial class WoundList : ScrollContainer +{ + private SortedDictionary _wounds = new(); + + public WoundList() + { + RobustXamlLoader.Load(this); + } + + public bool AddWound(EntityUid woundEntity, string woundName, string severityString) + { + var woundEntry = new WoundEntry(); + woundEntry.WoundName.Text = woundName; + woundEntry.WoundSeverity.Text = severityString; + if (!_wounds.TryAdd(woundEntity, woundEntry)) + return false; + Children.Add(woundEntry); + return true; + } + + public bool RemoveWound(EntityUid woundEntity) + { + if (!_wounds.TryGetValue(woundEntity, out var woundEntry)) + return false; + RemoveChild(woundEntry); + _wounds.Remove(woundEntity); + return true; + } +} diff --git a/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs b/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs index 6f76ebee3b8010..2df277bf159893 100644 --- a/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs +++ b/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs @@ -1,23 +1,39 @@ using Content.Client.Gameplay; +using Content.Client.UserInterface.Controls; +using Content.Client.UserInterface.Systems.Medical.Windows; using Content.Shared.Medical.Wounds.Systems; +using JetBrains.Annotations; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; +using Robust.Client.UserInterface.Controls; namespace Content.Client.UserInterface.Systems.Medical; +[UsedImplicitly] public sealed class MedicalUIController : UIController, IOnStateEntered, IOnStateExited, IOnSystemChanged { [Dependency] private readonly IEntityManager _entities = default!; [UISystemDependency] private readonly WoundSystem _woundSystem = default!; + private MenuButton? MedicalButton => + UIManager.GetActiveUIWidgetOrNull()?.MedicalButton; + + private MedicalWindow? _window; public void OnStateEntered(GameplayState state) { + _window = UIManager.CreateWindow(); + LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop); } public void OnStateExited(GameplayState state) { + if (_window != null) + { + _window.Dispose(); + _window = null; + } } public void OnSystemLoaded(WoundSystem system) @@ -27,4 +43,67 @@ public void OnSystemLoaded(WoundSystem system) public void OnSystemUnloaded(WoundSystem system) { } + + public void UnloadButton() + { + if (MedicalButton == null) + { + return; + } + + MedicalButton.Pressed = false; + MedicalButton.OnPressed -= MedicalButtonOnPressed; + } + + public void LoadButton() + { + if (MedicalButton == null) + { + return; + } + + MedicalButton.OnPressed += MedicalButtonOnPressed; + + if (_window == null) + { + return; + } + + _window.OnClose += DeactivateButton; + _window.OnOpen += ActivateButton; + } + + private void DeactivateButton() => MedicalButton!.Pressed = false; + private void ActivateButton() => MedicalButton!.Pressed = true; + + private void MedicalButtonOnPressed(BaseButton.ButtonEventArgs args) + { + ToggleWindow(); + } + + private void CloseWindow() + { + _window?.Close(); + } + + private void ToggleWindow() + { + if (_window == null) + return; + + if (MedicalButton != null) + { + MedicalButton.Pressed = !_window.IsOpen; + } + + if (_window.IsOpen) + { + CloseWindow(); + } + else + { + //_characterInfo.RequestCharacterInfo(); + _window.Open(); + } + } } diff --git a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml index 9c8ab55cfead53..90dc3c98b04463 100644 --- a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml +++ b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml @@ -2,8 +2,23 @@ xmlns="https://spacestation14.io" xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Medical.Controls" xmlns:windows="clr-namespace:Content.Client.UserInterface.Systems.Medical.Windows" + xmlns:cc="clr-namespace:Content.Client.UserInterface.Controls" Name="MedicalDisplay" HorizontalExpand="True" Title="Medical" VerticalExpand="True"> + + + + + + + + diff --git a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs index 5557075369cb40..b6ef0baa14c084 100644 --- a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs +++ b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml.cs @@ -1,16 +1,14 @@ -using Content.Client.UserInterface.Controls; -using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; namespace Content.Client.UserInterface.Systems.Medical.Windows; [GenerateTypedNameReferences] -public sealed partial class MedicalWindow : FancyWindow +public sealed partial class MedicalWindow : DefaultWindow { public MedicalWindow() { RobustXamlLoader.Load(this); - LayoutContainer.SetAnchorAndMarginPreset(this, LayoutContainer.LayoutPreset.Center); } } diff --git a/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs b/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs index 6f3c7344a4a509..640845575737a2 100644 --- a/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs +++ b/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs @@ -1,4 +1,3 @@ -using Content.Client.Gameplay; using Content.Client.UserInterface.Systems.Actions; using Content.Client.UserInterface.Systems.Admin; using Content.Client.UserInterface.Systems.Bwoink; @@ -6,6 +5,7 @@ using Content.Client.UserInterface.Systems.Crafting; using Content.Client.UserInterface.Systems.EscapeMenu; using Content.Client.UserInterface.Systems.Inventory; +using Content.Client.UserInterface.Systems.Medical; using Content.Client.UserInterface.Systems.MenuBar.Widgets; using Content.Client.UserInterface.Systems.Sandbox; using Robust.Client.UserInterface.Controllers; @@ -22,6 +22,7 @@ public sealed class GameTopMenuBarUIController : UIController [Dependency] private readonly AHelpUIController _ahelp = default!; [Dependency] private readonly ActionUIController _action = default!; [Dependency] private readonly SandboxUIController _sandbox = default!; + [Dependency] private readonly MedicalUIController _medical = default!; private GameTopMenuBar? GameTopMenuBar => UIManager.GetActiveUIWidgetOrNull(); @@ -32,6 +33,7 @@ public void UnloadButtons() _admin.UnloadButton(); _character.UnloadButton(); _crafting.UnloadButton(); + _medical.UnloadButton(); _ahelp.UnloadButton(); _action.UnloadButton(); _sandbox.UnloadButton(); @@ -44,6 +46,7 @@ public void LoadButtons() _admin.LoadButton(); _character.LoadButton(); _crafting.LoadButton(); + _medical.LoadButton(); _ahelp.LoadButton(); _action.LoadButton(); _sandbox.LoadButton(); diff --git a/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml b/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml index 9ba11fec4500b3..a69acb919c191d 100644 --- a/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml +++ b/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml @@ -63,6 +63,16 @@ HorizontalExpand="True" AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}" /> + ))] + //what wound should be created if this wound is healed normally? + [DataField("scarWound", customTypeSerializer: typeof(PrototypeIdSerializer))] public string? ScarWound; + //This is the prototypeId of a wound, this should be populated by the wounding system when the wound is created! + [ViewVariables] public string prototypeId = string.Empty; + [DataField("healthDamage")] public FixedPoint2 HealthCapDamage; [DataField("integrityDamage")] public FixedPoint2 IntegrityDamage; diff --git a/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs b/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs index d096284efbac26..a3b74d67902a02 100644 --- a/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs +++ b/Content.Shared/Medical/Wounds/Components/WoundComponentState.cs @@ -8,6 +8,7 @@ public sealed class WoundComponentState : ComponentState { public EntityUid Parent; public string? ScarWound; + public string PrototypeId; public FixedPoint2 HealthCapDamage; public FixedPoint2 IntegrityDamage; public FixedPoint2 Severity; @@ -15,13 +16,16 @@ public sealed class WoundComponentState : ComponentState public FixedPoint2 HealingModifier; public FixedPoint2 HealingMultiplier; - public WoundComponentState(EntityUid parent, string? scarWound, FixedPoint2 healthCapDamage, FixedPoint2 integrityDamage, FixedPoint2 severity, FixedPoint2 baseHealingRate, FixedPoint2 healingModifier, FixedPoint2 healingMultiplier) + public WoundComponentState(EntityUid parent, string? scarWound, string prototypeId, FixedPoint2 healthCapDamage, + FixedPoint2 integrityDamage, FixedPoint2 severity, FixedPoint2 baseHealingRate, FixedPoint2 healingModifier, + FixedPoint2 healingMultiplier) { Parent = parent; ScarWound = scarWound; HealthCapDamage = healthCapDamage; IntegrityDamage = integrityDamage; Severity = severity; + PrototypeId = prototypeId; BaseHealingRate = baseHealingRate; HealingModifier = healingModifier; HealingMultiplier = healingMultiplier; diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs index a8dee520c04fa5..9782a55ba8f6e0 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.Wounding.cs @@ -120,13 +120,15 @@ private bool AddWound(EntityUid woundableId, string woundPrototypeId, WoundableC if (!wounds.Insert(woundId)) return false; wound.Parent = woundableId; + wound.prototypeId = woundPrototypeId; Dirty(wound); woundable.HealthCapDamage += wound.Severity * wound.HealthCapDamage; ApplyRawIntegrityDamage(woundableId, wound.IntegrityDamage, woundable); return true; } - public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage, WoundableComponent? woundable = null) + public bool ApplyRawWoundDamage(EntityUid woundableId, FixedPoint2 woundDamage, + WoundableComponent? woundable = null) { if (!Resolve(woundableId, ref woundable, false)) return false; @@ -183,7 +185,8 @@ private void UpdateWoundSeverity(WoundableComponent woundable, WoundComponent wo wound.Severity = newSeverity; } - public bool RemoveWound(EntityUid woundableId, EntityUid woundId,bool makeScar = false, WoundableComponent? woundable = null, + public bool RemoveWound(EntityUid woundableId, EntityUid woundId, bool makeScar = false, + WoundableComponent? woundable = null, WoundComponent? wound = null) { if (!Resolve(woundableId, ref woundable, false) || diff --git a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs index 59d5382ca9c1b7..09d98b05d1e245 100644 --- a/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs +++ b/Content.Shared/Medical/Wounds/Systems/WoundSystem.cs @@ -76,6 +76,7 @@ private void OnWoundGetState(EntityUid uid, WoundComponent wound, ref ComponentG args.State = new WoundComponentState( wound.Parent, wound.ScarWound, + wound.prototypeId, wound.HealthCapDamage, wound.IntegrityDamage, wound.Severity, From fee488db418d2192de15a280bf2977fdafe93436 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sat, 17 Dec 2022 17:38:34 -0800 Subject: [PATCH 053/148] started implementing medical ui menu --- .../Systems/Medical/MedicalUiController.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs b/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs index 2df277bf159893..f9c7e29c67fcfd 100644 --- a/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs +++ b/Content.Client/UserInterface/Systems/Medical/MedicalUiController.cs @@ -1,8 +1,14 @@ using Content.Client.Gameplay; +using Content.Client.Humanoid; using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Systems.Medical.Windows; +using Content.Shared.Body.Components; +using Content.Shared.IdentityManagement; +using Content.Shared.Medical.Wounds.Components; using Content.Shared.Medical.Wounds.Systems; using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controls; @@ -14,8 +20,11 @@ public sealed class MedicalUIController : UIController, IOnStateEntered { [Dependency] private readonly IEntityManager _entities = default!; + [Dependency] private readonly PlayerManager _playerManager = default!; [UISystemDependency] private readonly WoundSystem _woundSystem = default!; + private EntityUid? _medicalFocus; + private MenuButton? MedicalButton => UIManager.GetActiveUIWidgetOrNull()?.MedicalButton; @@ -44,6 +53,51 @@ public void OnSystemUnloaded(WoundSystem system) { } + //returns true if the target has medical options + private bool HasMedicalOptions(EntityUid target, out BodyComponent? bodyComp, out WoundableComponent? woundableComp) + { + woundableComp = null; + return _entities.TryGetComponent(target, out bodyComp) || + _entities.TryGetComponent(target, out woundableComp); + } + + public string GetMedicalFocusName(EntityUid target) + { + return Identity.Name(target, _entities); + } + + public void SetMedicalFocus(EntityUid target) + { + if (!HasMedicalOptions(target, out var bodyComp, out var woundableComp)) + return; //if the target does not have a body or woundable components do not allow it to be set as a medical focus + UpdateMedicalFocus(target); + } + + private bool TryGetDummySprites(EntityUid focusedEntity, out SpriteComponent? frontDummy, + out SpriteComponent? backDummy) + { + frontDummy = null; + backDummy = null; + + if (_entities.TryGetComponent(focusedEntity, out HumanoidComponent? characterComp)) + { + return true; + } + + return false; + } + + private void UpdateMedicalFocus(EntityUid newFocus) + { + _medicalFocus = newFocus; + //TODO: refetch wounds for the focused entity + if (_window == null) + return; + TryGetDummySprites(newFocus, out var frontDummy, out var backDummy); + _window.StatusDisplay.UpdateUI(frontDummy, backDummy, GetMedicalFocusName(newFocus)); + } + + public void UnloadButton() { if (MedicalButton == null) From e819bd10606045a72fe7eaf0949b924634b52070 Mon Sep 17 00:00:00 2001 From: Jezithyr Date: Sun, 18 Dec 2022 18:46:10 -0800 Subject: [PATCH 054/148] continued implementing medial ui --- .../Systems/Medical/Controls/BasicStatus.xaml | 4 +- .../Medical/Controls/MedicalDoll.xaml.cs | 11 ++-- .../Medical/Controls/MedicalStatus.xaml.cs | 5 +- .../Systems/Medical/MedicalUiController.cs | 59 ++++++++++++++----- .../Medical/Windows/MedicalWindow.xaml | 9 ++- 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml b/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml index 813b3b91822384..ffadfafe894530 100644 --- a/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml +++ b/Content.Client/UserInterface/Systems/Medical/Controls/BasicStatus.xaml @@ -7,8 +7,8 @@ - { [Dependency] private readonly IEntityManager _entities = default!; - [Dependency] private readonly PlayerManager _playerManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly ISerializationManager _serialization = default!; [UISystemDependency] private readonly WoundSystem _woundSystem = default!; - private EntityUid? _medicalFocus; + + private EntityUid medicalDummyEntity = EntityUid.Invalid; + private SpriteView? medicalDummy; + private EntityUid _medicalFocus = EntityUid.Invalid; private MenuButton? MedicalButton => UIManager.GetActiveUIWidgetOrNull()?.MedicalButton; @@ -32,8 +37,14 @@ public sealed class MedicalUIController : UIController, IOnStateEntered(); LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop); + + //TODO: TEMP: Set the player as the focused entity + var player = _playerManager.LocalPlayer?.ControlledEntity; + if (player != null) + SetMedicalFocus(player.Value); } public void OnStateExited(GameplayState state) @@ -43,6 +54,8 @@ public void OnStateExited(GameplayState state) _window.Dispose(); _window = null; } + + ClearDummyEntities(); } public void OnSystemLoaded(WoundSystem system) @@ -66,25 +79,40 @@ public string GetMedicalFocusName(EntityUid target) return Identity.Name(target, _entities); } - public void SetMedicalFocus(EntityUid target) + public void SetMedicalFocus(EntityUid? target) { - if (!HasMedicalOptions(target, out var bodyComp, out var woundableComp)) + if (!target.HasValue) + { + _medicalFocus = EntityUid.Invalid; + UpdateMedicalFocus(EntityUid.Invalid); + return; + } + + if (!HasMedicalOptions(target.Value, out var bodyComp, out var woundableComp)) return; //if the target does not have a body or woundable components do not allow it to be set as a medical focus - UpdateMedicalFocus(target); + UpdateMedicalFocus(target.Value); } - private bool TryGetDummySprites(EntityUid focusedEntity, out SpriteComponent? frontDummy, - out SpriteComponent? backDummy) + private void CreateDummyEntities() { - frontDummy = null; - backDummy = null; + medicalDummyEntity = _entities.SpawnEntity(null, MapCoordinates.Nullspace); + _entities.EnsureComponent(medicalDummyEntity); + } - if (_entities.TryGetComponent(focusedEntity, out HumanoidComponent? characterComp)) + private void ClearDummyEntities() + { + if (medicalDummyEntity.Valid) { - return true; + _entities.DeleteEntity(medicalDummyEntity); } + } - return false; + private void UpdateMedicalDummy(EntityUid focus) + { + if (!medicalDummyEntity.Valid || !_entities.TryGetComponent(focus, out SpriteComponent? spriteComp)) + return; + var dummySprite = _entities.GetComponent(medicalDummyEntity); + _serialization.CopyTo(spriteComp, ref dummySprite); } private void UpdateMedicalFocus(EntityUid newFocus) @@ -93,8 +121,9 @@ private void UpdateMedicalFocus(EntityUid newFocus) //TODO: refetch wounds for the focused entity if (_window == null) return; - TryGetDummySprites(newFocus, out var frontDummy, out var backDummy); - _window.StatusDisplay.UpdateUI(frontDummy, backDummy, GetMedicalFocusName(newFocus)); + + UpdateMedicalDummy(newFocus); + _window.StatusDisplay.UpdateUI(medicalDummy, GetMedicalFocusName(newFocus)); } diff --git a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml index 90dc3c98b04463..15f0132986ee23 100644 --- a/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml +++ b/Content.Client/UserInterface/Systems/Medical/Windows/MedicalWindow.xaml @@ -13,9 +13,12 @@ Orientation="Vertical" > - +