Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Medical - Rework wound handling #8278

Merged
merged 41 commits into from
Feb 17, 2022

Conversation

pterolatypus
Copy link
Contributor

When merged this pull request will:

  1. Alter how damage types and their relationships to wound types are configured.
  2. Allow different damage types to influence wound properties (such as size or bleeding rate) or override wound handling completely
  3. Change handling of non-selection-specific damage to be closer to vanilla (makes explosives much more powerful)

Ok, but why?
Mostly just because the current system feels weird to me. Apart from that, this should make it simpler to extend the medical system (such as implementing less-lethal munitions) while keeping compatibility.
In addition it's a recurring complaint that explosives are weak; these changes should address that while making it easier to adjust such things in the future.

1. Alter how damage types and their relationships to wound types are configured.
Instead of having each wound type define a list of damage types that are allowed to cause that wound, I have each damage type contain a class for each wound type it can cause.
2. Allow different damage types to influence wound properties (such as size or bleeding rate)
These classes can then contain multipliers for the incoming damage, "size" of the wound, bleeding, pain and fracture chance allowing these values to be different between different damage types. A bruise caused by a relatively weak bullet hit may be more painful than a bruise caused by a fairly large collision.

On top of that, the type of wound created is now given by configurable weighted random which can be adjusted based on the incoming damage.
For example, wound type bullet can produce avulsions, contusions and velocity wounds:

  • Avulsion has a weight of 1 as long as the damage is above 0.01, 0 otherwise.
  • Contusion is weight 1 if the damage is below 0.35, 0 otherwise.
  • Velocity wound is weight 1 is the damage is above 0.35, 0 otherwise.

Therefore if the incoming damage is 0.6, the weights are avulsion=1, contusion=0, velocity=1 so it has an equal chance to produce a velocity wound or avulsion, and no chance to produce a contusion.
These weights need not be equal, and can also be interpolated between values: e.g. a wound may produce mostly bruises and scrapes when the damage value is low, and mostly lacerations when damage is high, but keep a sliding scale of randomness in between.
...or override wound handling completely
The wound handler function to use is now read from config and can be overridden for each damage type. This allows other mods to extend wound handling by wrapping the existing function or replace it entirely, or add new damage types with custom handling (such as fire or CBRN threats) without interfering with existing systems.

3. Change handling of non-selection-specific damage to be closer to vanilla (makes explosives much more powerful)
In the current implementation, only the hitpoint that took the most damage is actually passed on to the wound system - all other damage is discarded. This damage is then spread between all damaged hitpoints somewhat randomly, resulting in very inconsistent incapacitation/lethality (it's common for the head and torso to be untouched) and overall low damage numbers from indirect hits, collisions and falling damage.
Instead I pass all incoming damage to the wound event and have the wound handler make that decision only for "selection-specific" damage, e.g. bullets & shrapnel which should only affect one body part at a time, whereas explosions etc. will do separate damage to each part.

Here are some images to demonstrate the difference.
All show a 155mm shell with a number of B_Soldier_F units standing between 30m and 120m from the impact point (3m increments).

With no mods Instant kill out to about 90m, serious wounds to 110-ish.
With just CBA and current ACE (workshop) You can see the inconsistency - units as close as 50m weren't even knocked out, very few instant kills, generally not very effective. Remember this is a direct vertical impact with targets standing in the open - basically optimal conditions.
With this PR Lethal effects out to about 70m, KO out to 110m. A second image taken a couple of minutes later Most of the unconscious guys wouldn't have been saved by a medic, and the ones that weren't knocked out tended to pass out from wounds before they were able to bandage.
Needs a bit of tweaking definitely and personally I think the vanilla damage is already a bit over the top, but it's much more consistent and with the other changes should be easy to adjust. I should also note that I've set all the thresholds and wound weights to be the same as current release for comparison purposes; these should be re-balanced before merging.

This change does come with a performance cost as the wound handler function is being called 6 times instead of just 1 - in my SP testing this roughly tripled the weight of the whole damage handling routine (from ~150us per handleDamage call to ~450) and didn't produce any noticeable lag (unless you have logging on) but this was in a fairly sterile environment so I'd like to test it in the wild.
I'll be looking at optimisation, but for now I'm most interested in feedback - if you think it's a terrible idea please say so and I can avoid wasting my time :P

@unhappytroll
Copy link

unhappytroll commented Jun 8, 2021

I think the vanilla damage is already a bit over the top

High Explosive (HE)
(M-107 NC/DC): Explosive Composition B material packed into a thick, internally scored shell which causes a large blast and sends razor-sharp fragments at extreme velocities (5,000–6,000 meters per second). The kill zone is approximately a radius of 50 meters and casualty radius is 100 meters. The Marine Corps and US Army also uses the M795 High Explosive round.

I'd say, it's perfectly OK.
I'd also say, that right now explosives (bombs especially) may be somewhat under-powered even.
Mk82 for example is nowhere near as dangerous here as it should be.

@BaerMitUmlaut
Copy link
Member

The wound handler function to use is now read from config and can be overridden for each damage type. This allows other mods to extend wound handling by wrapping the existing function or replace it entirely, or add new damage types with custom handling (such as fire or CBRN threats) without interfering with existing systems.

I like this.

Instead I pass all incoming damage to the wound event and have the wound handler make that decision only for "selection-specific" damage, e.g. bullets & shrapnel which should only affect one body part at a time, whereas explosions etc. will do separate damage to each part.

Why not keep the existing method for selection specific damage, such as bullets or shrapnel, and sum the damage for structural damage? This might also solve the performance concerns?

I think the vanilla damage is already a bit over the top

Since we can assume that other mods are "balanced" around vanilla damage, I'd prefer if we would get close to vanilla results, at least for explosion damage, treating unrecoverable unconsciousness as death when doing comparisons (as you've already done). I would also like to add concussions sooner or later.


Just like the other damage handling PR rewrite, I would have liked to see a discussion before an implementation. And if we touch this now, we should also consider the caliber based damage that RHS seems to use a lot and we don't handle well right now.

@commy2
Copy link
Contributor

commy2 commented Jun 9, 2021

I was skeptical when I saw that the PR edits HandleDamage, however the way you represent data with those snail images is really cool and changes to how explosions are handled are badly needed. I really like this (without having tried it out).

Any chance the script used to make these could be put in ACE as test / debug script?

@pterolatypus
Copy link
Contributor Author

pterolatypus commented Jun 9, 2021

Why not keep the existing method for selection specific damage

It essentially does, it's just that it's being done within the woundReceived EH instead of in handleDamage. The potential performance impact is only with damage that hits a lot of hitpoints (like explosions) as the actual handler function is now called for each body part instead of just once.

sum the damage for structural damage

It seems pointless to add all the damage together only to then split it up again.
I could restructure it a bit so that all of the damage is passed to one call of the handler function - basically move the loop inside the handler function, which might cut the overhead a bit.
Also the thresholds need to be tweaked. Currently where a shell (for example) was creating 5-7 wounds total it's instead creating up to 7 wounds per part hit; bringing this down to 2-3 per part should help a bit.

assume that other mods are "balanced" around vanilla damage

That was my logic - use vanilla behaviour as the baseline and then tweak around it.

I would also like to add concussions

My future plans include a custom handler for explosives that would handle blast and fragmentation effects separately (the latter could integrate with ace fragmentation somehow) and a kind of "stun" quantity that would decouple unconsciousness from lethal damage somewhat - but I feel like these deserve separate PRs.

we should also consider the caliber based damage that RHS seems to use

I'm not familiar with that, but it sounds interesting.
Another thing I wanted to write off the back of this was better handling of body armour, since the current system doesn't line up with vanilla for direct hits because of ignoring passthrough, and I'd like to use caliber to model penetration for that.

I was skeptical when I saw that the PR edits HandleDamage

You're gonna love my latest commit then XD. But yeah, part of the motivation for this was being able to clean up HD a bit and move some of the logic (like handling for burning & fall damage) out of medical_engine so that we can tweak numbers and balance without having to be reminded of that horror.

Any chance the script used to make these could be put in ACE

At first I tried just dropping a bomb on a long line of guys, but I discovered that they do in fact shield each other from the blast which I never knew before. I think I stole the spiral idea from someone else but I can't remember who.
It's currently just this mission file but yeah, I don't see why not. If I made it spawn the ammo too it could be used for grenades or other things.

@kymckay
Copy link
Member

kymckay commented Jun 9, 2021

@pterolatypus I'd just first like to say a massive thanks for your continued work on reworking medical, it's clear to me you've taken the time to understand the systems as they are and the intentions behind them in order to see where to improve.

I haven't tested this PR (it's been quite some time since I truly booted up Arma), but I like all of your reasoning, justifications and the results presented.

Instead of having each wound type define a list of damage types that are allowed to cause that wound, I have each damage type contain a class for each wound type it can cause.

Honestly I think this this makes more sense semantically and in terms of coupling too (the cause specifies the effect).

These classes can then contain multipliers for the incoming damage, "size" of the wound, bleeding, pain and fracture chance allowing these values to be different between different damage types. A bruise caused by a relatively weak bullet hit may be more painful than a bruise caused by a fairly large collision.

On top of that, the type of wound created is now given by configurable weighted random which can be adjusted based on the incoming damage.

Very nice, more logically driven variance.

Instead I pass all incoming damage to the wound event and have the wound handler make that decision only for "selection-specific" damage

Again, very logical. I've suspected for a long while that our filtering down to the maximum damage hitpoint is what's been causing some of the weirdness with the more general (as opposed to regional/local) damage types. Glad to see it being addressed.


I do agree with @commy2 it would've been nice to see some discussion before implementation, but I also understand discussion isn't as active as it once was.

If we get to the stage where we're going to merge this. I think we should be proactive and get documentation of the system (config changes for addon authors, but also some developer documentation on how the system now works) before merging. Something we've not been strict about in the past, leading to loss of established design intent/system knowledge and having to reverse engineer it from the source.

@pterolatypus
Copy link
Contributor Author

Thanks for all the positive feedback!

I've made a change to how burning is handled - now instead of waiting for a certain amount of damage before creating wounds, it waits a certain amount of time (currently 1s which works reasonably). It's also a good example of a custom damage handler in action.
The result of this is that the number of wounds created is proportional to time spent in contact with the fire and the size of the wounds is determined by the intensity, which I think makes sense. I'd like a better wound type to give (instead of cuts and scrapes) but I think that's out of scope for this PR.

The system seems to be basically functional now, so I've started cleaning up and hunting the less-obvious bugs. I'll also implement the testing function and start working on documentation (boooo). If anyone has time to test it out and report any errors or unexpected behaviour that'd be appreciated, as I tend to miss stuff. Should be ready for a proper review soon.

@pterolatypus pterolatypus changed the title WIP: Medical - rework wound handling Medical - rework wound handling Jun 18, 2021
@pterolatypus pterolatypus force-pushed the feature/betterDamage branch from 15086f5 to 695dbbf Compare June 18, 2021 18:21
@pterolatypus
Copy link
Contributor Author

pterolatypus commented Jun 19, 2021

Testing function and first attempt at documentation are in - I've created a new framework page for this as it's information for developers rather than end-users. I could also add sections for some of the stuff in medical_treatment, but that might be a job for another PR.

I think we're review-ready, at least on a systems-level.

One thing I do want to change is the thresholds - as mentioned, because explosives now typically hit all body parts this leads to 5-6x as many wounds being created which is bad for both performance and balance reasons (makes them waaay harder to treat). This needs some discussion though. An idea I had was something like 1-2 wounds at high damage values and 0-1 at lower values, which would create some satisfying randomness rather than everyone in a 150m radius being covered in bruises.

@xfunnypigx
Copy link

One thing I do want to change is the thresholds - as mentioned, because explosives now typically hit all body parts this leads to 5-6x as many wounds being created which is bad for both performance and balance reasons (makes them waaay harder to treat). This needs some discussion though. An idea I had was something like 1-2 wounds at high damage values and 0-1 at lower values, which would create some satisfying randomness rather than everyone in a 150m radius being covered in bruises.

I believe that it is fair to have damage all over the body. However, I do think that the way it could be balanced is that higher surface area body parts (read: chest) and potentially the head could see higher chances for higher damage while limbs could see lower chances for that, as well as generally smaller wounds. This would make explosions still extremely dangerous and costly to deal with, while not just outright applying 5 wounds on every body part and thus virtually killing a soldier by themselves.
In other words: Heavier wounds for the chest and head, lighter and fewer wounds for the limbs.

@kymckay
Copy link
Member

kymckay commented Jun 19, 2021

Just read closely through the documentation and content-wise it lgtm 👍 Also makes reviewing the design intent of this PR easier for those less familiar with these systems which is nice. Everything described still sounds like an improvement to me

Nice job and thanks for doing that

Comment on lines +43 to +71
// Drowning doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
// Damage occurs in consistent increments
if (
_hitPoint isEqualTo "#structural" &&
{getOxygenRemaining _unit <= 0.5} &&
{_damage isEqualTo (_oldDamage + 0.005)}
) exitWith {
TRACE_5("Drowning",_unit,_shooter,_instigator,_damage,_newDamage);
[QEGVAR(medical,woundReceived), [_unit, [[_newDamage, "Body", _newDamage]], _unit, "drowning"]] call CBA_fnc_localEvent;

0
};

// Crashing a vehicle doesn't fire the EH for each hitpoint so the "ace_hdbracket" code never runs
// It does fire the EH multiple times, but this seems to scale with the intensity of the crash
private _vehicle = vehicle _unit;
if (
EGVAR(medical,enableVehicleCrashes) &&
{_hitPoint isEqualTo "#structural"} &&
{_ammo isEqualTo ""} &&
{_vehicle != _unit} &&
{vectorMagnitude (velocity _vehicle) > 5}
// todo: no way to detect if stationary and another vehicle hits you
) exitWith {
TRACE_5("Crash",_unit,_shooter,_instigator,_damage,_newDamage);
[QEGVAR(medical,woundReceived), [_unit, [[_newDamage, _hitPoint, _newDamage]], _unit, "vehiclecrash"]] call CBA_fnc_localEvent;

0
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These "special" damage sources are now evaluated first so their damage doesn't get saved to the unit - previously it was being saved and not cleared so it would be included in the next event that triggered ace_hdbracket. It didn't cause any problems because it's only #structural damage which isn't used anywhere, but could have in the future.

Saving of damage is now done last, so it gets skipped any time wounds are actually applied.

@jonpas jonpas changed the title Medical - rework wound handling Medical - Rework wound handling Jun 29, 2021
@pterolatypus
Copy link
Contributor Author

Bump. I don't think anything else needs adding to this (I have more ideas but they're best left to other PRs) so I mostly need more input and reviews.

@jonpas jonpas added kind/enhancement Release Notes: **IMPROVED:** status/review-pending labels Jul 23, 2021
@jonpas jonpas added this to the 3.14.0 milestone Jul 23, 2021
@kymckay
Copy link
Member

kymckay commented Aug 5, 2021

I think a PR of this scale would benefit from some community playtesting alongside individual testing. @jonpas You're pretty up to date, do we still have communities that would be interested in playtesting system changes like this?

@kymckay

This comment has been minimized.

kymckay
kymckay previously requested changes Aug 5, 2021
Copy link
Member

@kymckay kymckay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above, script error under certain conditions results in no damage to unit

@severgun
Copy link
Contributor

severgun commented Jan 10, 2022

This is old code but...

  1. Why this exist here and used once while can be used twice? Maybe eliminate it and just use QGVAR?

    #define HIT_STRUCTURAL QGVAR($#structural)

  2. Maybe it should be [0,0]?

    private _damageStructural = _unit getVariable [HIT_STRUCTURAL, 0];

    Otherwise it will fail at _damageStructural + [PRIORITY_STRUCTURAL, "#structural"] with SCALAR + ARRAY

Maybe it should be separated as PR to master

@severgun
Copy link
Contributor

severgun commented Jan 25, 2022

Bullets with high hit values generate alot of structural damage when hit limbs. So, because of sorting,
This:

if (_selectionSpecific > 0) then {
_allDamages = [_allDamages select 0];
};

And this:
// silently ignore structural damage
if (toLower _bodyPart isEqualTo "#structural") then {
continue
};

Ends up in 0 damage

STEPS TO REPRODUCE

  • Place 12.7 HMG.
  • Place bots.
  • Shoot full mag in legs or arms.
  • 0 damage.

Maybe something like that would be ok?

if (_selectionSpecific > 0) then {
    private _mostDamagedPart = toLower (_allDamages select 0 select 1);
    if (_mostDamagedPart isEqualTo "#structural") then {
        _allDamages = [_allDamages select 1];
    } else {
        _allDamages = [_allDamages select 0];
    };
};

Or maybe handle structural instead of skipping.

@LinkIsGrim
Copy link
Contributor

LinkIsGrim commented Feb 4, 2022

Some thoughts from using this internally in my community:

  • While the creation of multiple/larger wounds from high damage events being more frequent is much appreciated and remniscent of pre-rewrite medical, it could probably use some toning down (for gameplay purposes if nothing else). Perhaps reintroducing the separation of the _worstDamage value for limbs/critical areas in woundsHandlerSQF? Limbs typically being unarmored can lead to some "interesting" interactions.

  • Even with the latest commit, small-arms fire with high hit values causing high structural damage is still an issue. Example from today: a headshot with B_127x108_APDS fired from srifle_GM6_F on an FIA rifleman ended up creating wounds in the unit's arms. This could be addressed by using the hitpoint's armor directly in the sorting.

  • For regular use cases (non-specialty weapons in infantry combat), lethality in general is increased, even against problematic factions like RHS AFRF, which is appreciated. High explosives, artillery, and grenades are also much more enjoyable to use, and closer to the vanilla game's balancing for them, in a good way.

EDIT: Made PR for the high hit value damage. Try it out?

@kymckay
Copy link
Member

kymckay commented Feb 4, 2022

Posted elsewhere, but I think if we can address the two feedback points (structural damage sounds most important) then I think we should aim to get this merged in and deal with any unseen consequences after the fact.

In an ideal world this would have taken place over multiple smaller PRs making it easier to manage, but I think enough eyes have crossed this to say the system itself is functionally sound. Plus the sooner the better as this really lays a foundation for further damage handling improvements thanks to the improved semantics and more consistent behaviour.

It's also large enough that I'd rather get it in before it becomes impossible to merge without reworking or that it blocks other PRs.

@pterolatypus
Copy link
Contributor Author

small-arms fire with high hit values

This isn't directly related to structural damage, it's just the same old splash damage issue. Your solution effectively flips the sorting which could possibly cause the opposite problem.
Instead I've just reordered the array to actually use priority (it was mostly being ignored before) which seems to improve the situation, but the underlying problem is still the fact that these vests are balanced around passthrough which we don't understand or have a good way of accounting for.

Limbs typically being unarmored

With this PR, unarmored limbs should actually take less damage due to the changes to the hitpoint macro - their base armor has gone from 1 to 3 which should more than offset the _worstDamage change.

lethality in general is increased

some "interesting" interactions

You're gonna have to be more specific. For small arms there shouldn't be any immediate difference because the underlying damage (i.e. instant-kill potential) is unchanged and bleed rates should be pretty similar, except for events that now create multiple wounds. Is that happening more often than it should?
Personally I find the bleeding rate in general to be excessive with default settings, so we could consider lowering that baseline.

@severgun
Copy link
Contributor

severgun commented Feb 9, 2022

Personally I find the bleeding rate in general to be excessive with default settings, so we could consider lowering that baseline.

I did not test PR with latest commits. But first expression month ago was:
Explosives, UGLs, grenades - wow I like it.
Disabled/destructed vehicles with AT - mehh, same as master. Units jumpout without any damage.
Bullets - cause a lot of contusions and small abrasion(so bleeding not a threat here) where on master branch that would be avulsion and velocity. So, it feels like it is harder to kill. After that, we find out issue with limbs. Maybe now it is better.
I also ran out of bandages more often. Maybe wounds chances should be tuned. (This can be done after merge)

@veteran29
Copy link
Member

veteran29 commented Feb 14, 2022

The code looks good to me.

I've done short testing and I'm seeing similar results as @severgun. Explosives are actually useful again, however, it looks like body armour got stronger or bullets, in general, do less damage, I'm seeing more bruises and avulsions instead of velocity wounds which makes it harder to kill.

So if this note is still valid:

I should also note that I've set all the thresholds and wound weights to be the same as current release for comparison purposes; these should be re-balanced before merging.

I guess it might be done in separate PR but it should be done.

.

Also I found out something weird which I'm not sure if it was happening earlier... while I was testing on AI with high armour vest (RHS) the unit was receiving only the bruises but they were able to kill the unit with Sum of Trauma enabled. Not sure if it was possible earlier. Not sure if bruises should be counted for Sum of Trauma as they can't be healed without PAK. However, if they would not be lethal it would be impossible to kill the tested unit with torso shots...

primaryWeapon player; // "arifle_MX_SW_pointer_F"
vest target; // "rhs_6b13_6sh92"

this was lethal with sum of trauma on default (1.0)
image

@kymckay
Copy link
Member

kymckay commented Feb 14, 2022

From what I can tell on quickly inspecting the sum of trauma setting, the bruises must already be contributing to that before this PR. Looking at the changes here again, it seems this behaviour is just more apparent because bruises can now cause more damage.

@Drofseh
Copy link
Contributor

Drofseh commented Feb 15, 2022

I don't think bruising contributing to sum of trauma is too unreasonable. It could account for things we don't stimulate like broken ribs.

@kymckay
Copy link
Member

kymckay commented Feb 15, 2022

After internal discussion, I plan to merge this PR on Thursday (to give time for anyone to object). So if you have a strong reason this shouldn't be merged, say it now 😁 🎉

To be clear, we think some balancing is needed, but are happy for that to take place after merge since this PR is more about the functionality. The bruising thing does make some sense, though will need to be considered in balance with treatment of bruising.

kymckay and others added 2 commits February 15, 2022 09:19
Co-authored-by: Filip Maciejewski <veteran29@users.noreply.github.com>
Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>
@pterolatypus
Copy link
Contributor Author

Glad to see that the explosives change is appreciated, I was concerned people would find it excessive. I do still think the damage could be reduced a little, but that can wait.

The thing about bruises/sum of trauma is the same on master, you might just not notice it as much because those small hits can also be avulsions whereas I have changed the minimum damage for avulsions to match velocity wounds (0.35) as this is described as the threshold for "penetrating" damage.
It's definitely worth looking into in relation to when trauma should be cleared, but not directly related to this PR. Side note; those small wounds are ignored when checking incapacitation (KO) which should also probably be discussed, doesn't make a sense to me.

I think I need to see it in the wild myself to get a feel for the balance, since the feedback thus far is a bit inconsistent (some of it could be placebo effect).
If nothing seems to be broken then yeah let's get it merged and you can all yell at me if there are problems :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/enhancement Release Notes: **IMPROVED:** target/next-milestone?
Projects
Development

Successfully merging this pull request may close these issues.