CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
10.33k stars 4.14k forks source link

Rework armor durability damage chance #55271

Closed PlutusPleion closed 2 years ago

PlutusPleion commented 2 years ago

Is your feature request related to a problem? Please describe.

The current system is if the damage done is higher than the armor's resistance there is a 25% chance of damaging the item on the low end with 50% chance of damaging on the high end.

current:

  1. There is no gradient to the system, only a true/false a. The system does not take into consideration the resistance of the armor(at this step) for its chance to get damaged b. The system does not scale the damage done for its chance to get damaged or that it's currently too logarithmic.
  2. The upper and lower limits are unrealistic

1.a. Without getting into shear fracture and elasticity, if an item gives more resistance it would make more sense that the item has an inherent less chance of getting damaged in comparison to an item with less resistance. There is already a "STURDY" tag but this is not used when the damage does exceed and does not account for the precision we want. 1.b. The damage done should also be nuanced and incorporated into the equation because logically a t-shirt with almost no protection should have much less chance to get ripped from a rock than from say a .50 cal bullet.

  1. The lower limit chance to be damaged should be lowered further than 25% because a barrage of 10 2-damage pebbles for example have a 94.3% chance of damaging anything (1-(0.75^10)). On the higher end as well using the .50 cal bullet example if a shirt is hit with it there should be a near 100% chance to damage it.

Solution you would like.

Implement an algorithm that incorporates how much overdamage(raw minus resistance) is done, make the relation between overdamage and chance to damage less logarithmic and change the lower and upper bounds to 10% and 90%(?) respectively. Arguments for the lower and upper end could be made, the main point being they get a look over.

Example calculation: 0.1+(1-(1/(overdamage^(1/2.4))))^2.7

overdamage | chance to be damaged -- | -- 1 | 0.100 2 | 0.124 3 | 0.167 4 | 0.208 5 | 0.245 6 | 0.276 7 | 0.304 8 | 0.329 9 | 0.351 10 | 0.371 20 | 0.501 25 | 0.541 40 | 0.620 60 | 0.682 150 | 0.800 436 | 0.900 100% is reached at 2518.35 overdamage but I don't think that would ever be an actual case outside of debugging.

To give examples for the effect of the changes:

Scenario old damage chance new damage chance
1 raw dmg, 0 resist armor 0% 10%
21 raw dmg, 20 resist armor 47.6% 10%
2 raw dmg, 0 resist armor 25% 12.4%
23 raw dmg, 20 resist armor 47.8% 16.7%
8 raw dmg, 1 resist armor 42.9% 30.4%
23 raw dmg, 10 resist armor 47.8% 42.1%
23 raw dmg, 1 resist armor 47.8% 51.8%
50 raw dmg, 20 resist armor 49% 57.3%
2 raw dmg* 10 instances, 0 resist armor 94.3% 73.4%
150 raw dmg, 0 resist armor 49.7% 80%
436 raw dmg, 0 resist armor 49.9% 90%

As you can see it scales better and is more realistic. A great benefit in terms of immersion for the player and IMO a great change for the game.

Describe alternatives you have considered.

meme: embrace the suffering of constant ripped clothing from a hail of pebbles while enjoy .50 cal semi-resistant clothing

Additional context

Current:

currently works out to 0.5 * (1-(1/dmg)) chance to be damaged

    const float raw_dmg = du.amount;
    if( raw_dmg > armors_own_resist ) {
        // If damage is above armor value, the chance to avoid armor damage is
        // 50% + 50% * 1/dmg
        if( one_in( raw_dmg ) || one_in( 2 ) ) {
            return armor_status::UNDAMAGED;
        }
    } 

New proposed:

    const float raw_dmg = du.amount;
    if( raw_dmg > armors_own_resist ) {
        // If damage is above armor value, the chance to avoid armor damage is
        // 1 in 1/(1 - chance to damage) for chance to be undamaged
       if( one_in(   1 / ( 1 - ( 0.1+(1-(1/(armors_own_resist-raw_dmg)^(1/2.4)))^2.7))) {
            return armor_status::UNDAMAGED;
        }
    } 

Could probably do a PR myself and test but I'm not set up for it currently.

Inspiration from here: https://www.reddit.com/r/cataclysmdda/comments/sltcx1/single_rock_throw_from_feral_human_clothes_damaged/

Also a character who got an arm blown off by a .50 cal but all gear covering it was fine. lol

Drew4484 commented 2 years ago

I like the proposal, though it might work better to scale the overdamage to the armor's defensive values, I.E. 80% chance of damage at 2x the defense of the armor.

bombasticSlacks commented 2 years ago

this looks very good. I've been thinking about that reddit post too.

I'd be happy to PR this but I'm a little hesitant about the exponentials. It might be worth building a function to do a faster calc with linear interpolation between known points. @kevingranade what's your opinion on performance for something like this?

PlutusPleion commented 2 years ago

I like the proposal, though it might work better to scale the overdamage to the armor's defensive values, I.E. 80% chance of damage at 2x the defense of the armor.

Would be open to suggestions for the formula. One side effect of this could be that the already strong reduced chance is double dipped again by using the armor value a second time.

I think the clothing/armor damage system is enjoyable but just needs a bit of tweaking. Because this is a game, I don't think normal(realistic) gear should be impervious to damage, nor should there be cases where it's always damaged. There should be a fine balance in between.

I'd be happy to PR this but I'm a little hesitant about the exponentials. It might be worth building a function to do a faster calc with linear interpolation between known points.

Could go with 0.005*(overdamage)+0.095 as well for linear. Either way seems fine, just wanted it to scale better and incorporate resistances as well.

Would need a way to handle going over 100% of course in either case by adding another if statement or something.

like :

const float over_dmg = armors_own_resist-raw_dmg;

if( over_dmg >= 181 ) {
    return armor_status::DAMAGED;
}
martosss commented 2 years ago

1) Since the game doesn't have damage like 2000(or 150?), you should lower the percentages required for 100% damaging. I'm looking at the enemy list in the wiki, and if it's not terribly out of date, the max damage you can get is something like 35(7d5)-36(6d6) ... so you should aim this value to be fairly high - 85%+ to damage armor?

both formulas that are posted above seem to lower the chance at lower damage, but don't address the upper bound problem - zombies with high damage should actually damage your armor.

2) You can think about scaling the chance based on the ratio (extra) dmg / armor :

I think the 1st armor should have a lower chance to break, because the extra dmg is less in proportion to how much the armor can protect against.

That should simulate better the "bazooka vs T-shirt" case - if dmg / armor ratio is very high, then you should damage that armor with very, very high probability.

PlutusPleion commented 2 years ago

@martosss

I wanted to stray away from reaching near 100% damage chance because the current one is capped at 50% and I imagine there would be some people not too happy with a change like this. CROWS II .50cal variant I think does 131(base) damage which is what I accounted my top limit for.

The 2nd suggested linear equation I gave is more lenient, but my initial equation is optimized for the the 1-35 range which is the more common occurrence for the player. For example 35 damage would be 59.8% chance to get damaged which is nearly 10% higher already from the cap of the current equation the game uses.

You can think about scaling the chance based on the ratio (extra) dmg / armor :

armor with protection "10" gets hit with 15 dmg armor with protection 0.1 gets hit with 5 dmg

I think the 1st armor should have a lower chance to break, because the extra dmg is less in proportion to how much the armor can protect against.

We would already account for the armor in the overdamage, and then we propose to further compare the overdamage again with the armor. Meaning the armor would now have an exponential effect.

Drew4484 mentioned this as well so perhaps we should have further thought and consideration to it. I'm open to it as well, but don't want to get into scenarios where high armor gear almost never get damaged.

Drew4484 commented 2 years ago

@martosss

I wanted to stray away from reaching near 100% damage chance because the current one is capped at 50% and I imagine there would be some people not too happy with a change like this. CROWS II .50cal variant I think does 150+ damage which is what I accounted my top limit for.

The 2nd suggested linear equation I gave is more lenient, but my initial equation is optimized for the the 1-35 range which is the more common occurrence for the player. For example 35 damage would be 59.8% chance to get damaged which is nearly 10% higher already from the cap of the current equation the game uses.

You can think about scaling the chance based on the ratio (extra) dmg / armor :

armor with protection "10" gets hit with 15 dmg armor with protection 0.1 gets hit with 5 dmg

I think the 1st armor should have a lower chance to break, because the extra dmg is less in proportion to how much the armor can protect against.

We would already account for the armor in the overdamage, and then we propose to further compare the overdamage again with the armor. Meaning the armor would now have an exponential effect.

Drew4484 mentioned this as well so perhaps we should have further thought and consideration to it. I'm open to it as well, but don't want to get into scenarios where high armor gear almost never get damaged.

Ideally that just makes the tougher armor shrug off birdshot while still being threatened by slugs. We also have ablative plates like the EASPI, they have a 1/3 chance to break when they would normally take damage.

Alex-Folts commented 2 years ago

How many .50cal hits will need to destroy say a t-shirt with this rework?

PlutusPleion commented 2 years ago

How many .50cal hits will need to destroy say a t-shirt with this rework?

Considering base damage 131, not wearing anything else over the t-shirt, and the shirt succeeded in the coverage roll

shots chance
1 78.4%
2 95.3%
3 99%
4 99.7%

Pretty good chance with 1 shot to damage, nearly guaranteed to damage with 2 shots. To destroy? probably around 10. Still less than old/current system but yeah you're probably dead well before that.

Good point bringing up that there should probably be thresholds where multiple levels of damage should be applied to the item so that a t-shit for example should be completely destroyed after like 2 .50cal hits. But that's going to be a more in-depth and complex change outside the scope of this one I think.

martosss commented 2 years ago

Drew4484 mentioned this as well so perhaps we should have further thought and consideration to it. I'm open to it as well, but don't want to get into scenarios where high armor gear almost never get damaged. Oh, I didn't see that, sorry, you're right. I hope my post still adds a little bit to that, showing examples of why this idea makes sense.

We would already account for the armor in the overdamage,

but you account in a linear fashion (damage - armor, there's no ratio here)

and then we compare the overdamage again with the armor.

Ugh, in your formula you just plugin overdamage, there's no overdamage-armor comparison? So only the difference between armor and damage will matter, not the base armor value?

https://www.wolframalpha.com/input?i=abs%28%281.5*x%29%5E%281%2F2.5%29-1%29%2F2%2C+x+from+1+to+10 Here's an example: Here x = damage / armor, so that's how many times the damage is more than the armor protection.

So, 10 - 97.7% means

P.S. Feel free to tweak the formula in the site, or remove the "x from to" part to get a general plot of the funtion.

Alex-Folts commented 2 years ago

I think you should take into account damage type.

martosss commented 2 years ago

I think you should take into acount damage type.

How will damage type make a difference? Suppose the vest can protect against 1, 5, 10, 25 of 4 different damage types, now imagine you get hit with 30 dmg of each type. How should the formulas for each damage type be different?

PlutusPleion commented 2 years ago

@martosss

Makes sense and it could work. My only concern would be the extreme cases. Like in that case, your t-shirt is always ripping from any normal zombie hit. That would also mean really high armor will hardly ever exceed say 2x their threshold in most scenarios. I wouldn't mind the exponential, linear, or your comparative equations. Whichever case, it would be an improvement from the current system which has a logarithmic scale, doesn't consider resistances at all and is more severely capped on both high and low ends.

I think you should take into account damage type.

I think the game already does:

item::armor_status item::damage_armor_durability( damage_unit &du, const bodypart_id &bp )
{
    // We want armor's own resistance to this type, not the resistance it grants
    const float armors_own_resist = damage_resist( du.type, true, bp );
martosss commented 2 years ago

My only concern would be the extreme cases.

We should probably ask ourselves questions for the corner cases:

Like in that case, your t-shirt is always ripping from any normal zombie hit.

If I don't have any armor to protect me against that zombie hit, or my armor consists of 3 stacked T-shirts, shouldn't they break? If not, is the T-shirt protection value of "0.2" realistic?

That would also mean really high armor will hardly ever exceed say 2x their threshold in most scenarios.

So in that case make x2 the threshold for breaking. But is that realistic? If you have SWAT armor, isn't it supposed to protect you basically 90% against those zombie hits? It should break only when you shoot it with guns, right?

I wouldn't mind the exponential, linear, or your comparative equations.

Probably a linear equation would be best since the relationship is almost linear.

https://www.wolframalpha.com/input?i=plot+f%28x%29+%3D+0.083*x+%2B+0.16+from+0+to+11 Here's an example of a linerized formula - tear percentage = 0.083*(damage/ armor) + 0.16 (just make sure armor isn't 0) That's roughly 17% for (damage==armor) and rises from there.

- P.S. oops, not 17%, but 25%, because you'd have 0.16+0.083. I realized this after writing my next comment

The difference with the previous funky-rooty formula that I had is that here when (damage == armor) there's a higher percentage for gear to get damaged - 17% vs 1-2 % for the older formula. But this can be easily changed by tweaking the "0.16" in the formula to something like "0.1".

- again, not 17, but 25%

Whichever case, it would be an improvement from the current system which has a logarithmic scale, doesn't consider resistances at all and is more severely capped on both high and low ends.

I agree, we need a more nuanced approach to armor tearing based on dmg, not coin flips

bombasticSlacks commented 2 years ago

Something I think is being missed in all this conversation is that in damage_armor_durability the damage that is passed in has already been reduced by the armor itself and all armor from above layers of protection.

I liked the original suggestion and think this is getting pretty in the weeds of edge cases especially when no one seems intent on writing any of the tests to actually evaluate any of it πŸ€“

martosss commented 2 years ago

I admit I'm not very familiar with the code base. I really tried to go through all the functions, but I just read "du = damage unit" and trusted that "raw" dmg means incoming before armor mitigation. Either way, this is just a technical difference. It will shift the whole line slightly, so you'll have

+ tear percentage =

0.08*( damage(raw)                    / armor) + 0.17 = 0.08*((damage(raw)   - armor + armor) / armor) + 0.17 = 0.08*((damage(mitigated)     + armor) / armor) + 0.17 = 0.08*( damage(mitigated)              / armor) + 0.25 =

+ 0.08(dmg(new)/armor) + 0.25

And this becomes your new equation. Here dmg(new) = dmg(mitigated) = damage(raw) - armor (we pass this as a parameter, as you kindly noted) and you know the armor from your body part.

So you can clearly see the meaning of "0.25" and "0.08" in the equation, right? Now, I dont claim that 25% initial value (armor = dmg) and 8% increment (per dmg/armor) are the perfect values, those are a matter of armor balancing discussion, hell, the whole formula is. But this formula should be more intuitive than the old one?

For me the hard questions are

If we can come up with several typical scenarios and fix the expected behavior, we can use those fixed points to create a "good" function that interpolates all values in-between.

For this function I took only the beginning and the end - when dmg = armor and when dmg = 10 * armor.

However, we can take more points(by point I mean ratio between damage and armor) in-between and make a more complicated function that describes them. So what are the "good" points to choose and what are good values for the chance to decrease durability at those points?

PlutusPleion commented 2 years ago

Thought about it some more and based it off of @martosss' equation

I'm currently at

0.11*(overdmg/(resistance+2))+0.1

The reasoning for the resistance+2 ensures very low or no resistance items have a bit of leeway and buffer. This has the added benefit of preventing dividing by zero. This is with the assumption that the previous 25% lower and 50% upper had reasons behind it and I want to respect that. Meaning while we want to lower and increase both ends of the limit, I don't think it's best to have 10% and 100% to be so easily reached other than edge cases.

The base +0.1 is so that items will always have a chance to get damaged(if the damage is over resistance).

The 0.11* is so that the damage aspect is toned up a bit to compensate for the buffer we added for super low resistance armors.

In regards to what @bombasticSlacks mentioned, the raw_dmg is indeed already post mitigation damage.

So we end up with:

const float post_mitigated_dmg = du.amount;
const float damaged_chance = 0.11*(post_mitigated_dmg/(armors_own_resist+2))+0.1

if( post_mitigated_dmg > armors_own_resist ) {
    if (damaged_chance >= 1) {
        return armor_status::DAMAGED;
    }
    if( one_in(   1 / ( 1 - damaged_chance)) {
        return armor_status::UNDAMAGED;
    }
} 

And here are the updated scenarios using V2 of the equation:

Scenario old damage chance new damage chance damage chance V2
1 raw dmg, 0 resist armor 0% 10% 15.5%
21 raw dmg, 20 resist armor 47.6% 10% 10.5%
2 raw dmg, 0 resist armor 25% 12.4% 21%
23 raw dmg, 20 resist armor 47.8% 16.7% 11.5%
8 raw dmg, 1 resist armor 42.9% 30.4% 35.7%
23 raw dmg, 10 resist armor 47.8% 42.1% 21.9%
23 raw dmg, 1 resist armor 47.8% 51.8% 90.7%
50 raw dmg, 20 resist armor 49% 57.3% 25%
2 raw dmg* 10 instances, 0 resist armor 94.3% 73.4% 90.5%
150 raw dmg, 0 resist armor 49.7% 80% 100%
436 raw dmg, 0 resist armor 49.9% 90% 100%
bombasticSlacks commented 2 years ago

As I said I'll PR this if you would like, if you don't have a dev environment set up.

I'll link back to this issue for the relevant info.

PlutusPleion commented 2 years ago

As I said I'll PR this if you would like, if you don't have a dev environment set up.

I'll link back to this issue for the relevant info.

That would be great. Yeah I don't have the compiler and such setup right now.

Also I thought about it further and this part doesn't make much sense either:

if( !one_in( num_parts_covered ) ) {
        return armor_status::UNDAMAGED;
    }

It arbitrarily makes multi-bodypart clothing unrealistically more resilient. Resilience should solely be based on damage and resistance and not on how big the clothing is.

It's not like the blow from an attack is somehow distributed evenly across the article(at least in most cases).

LeahLuong commented 2 years ago

I think that line is there for clothing/armor that falls into the "suit" category. Getting a tick of dmg on a chainmail suit is equivalent to getting damage on each of the chainmail parts. If that line weren't there, suits would be strictly worse than simply wearing each of their parts individually. I kind of feel like the suits should be done away w/ along w/ this idiosyncrasy but it does make for less clutter on the inv screen & simpler repairs.

PlutusPleion commented 2 years ago

I think that line is there for clothing/armor that falls into the "suit" category. Getting a tick of dmg on a chainmail suit is equivalent to getting damage on each of the chainmail parts. If that line weren't there, suits would be strictly worse than simply wearing each of their parts individually. I kind of feel like the suits should be done away w/ along w/ this idiosyncrasy but it does make for less clutter on the inv screen & simpler repairs.

Perhaps we can make an item flag to account for this like "DISTRIBUTES_DAMAGE"? We could add the flag to those cases and then check for the flag to determine if it gets the chance to be undamaged. Then possibly also reduce the bonus instead of using !one_in( num_parts_covered ) maybe to something like one_in((1/num_parts_covered )*8)

This means each part covered gives a 12.5% chance to be undamaged

num_parts_covered undamaged old undamaged proposed
1 0% 12.5%
2 50% 25%
3 67% 37.5%
4 75% 50%
5 80% 62.5%
6 83% 75%

So suits will have their benefits over individual pieces separately but not to the insanely high benefit it current has.

martosss commented 2 years ago

Also I thought about it further and this part doesn't make much sense either:

if( !one_in( num_parts_covered ) ) {
        return armor_status::UNDAMAGED;
    }

It arbitrarily makes multi-bodypart clothing unrealistically more resilient. Resilience should solely be based on damage and resistance and not on how big the clothing is.

It's not like the blow from an attack is somehow distributed evenly across the article(at least in most cases).

I was looking at this as well and thought about how much better a full body armor is .... until I encountered a hulking terror(hulk dog) ... That has a full-body slam attack that hits all body parts for a lot of damage.

Now, imagine you're wearing a hazmat suit(full body thing, can't be separated), you get hit by 1 such attack and the suit is gone... in 1 hit. That's kind of bad.

The same thing happened to me, to some extent - Under my armor I was wearing a Kimono(warmth + storage space + 1 encumbrance = awesome). It covers torso + arms + legs, so 3 body parts. It took 2 hits from the hulking terror slam attack.

So, to sum up, Area attacks would destroy multi-body-part gear if it wasn't for this check. This ensures that the armor is damaged roughly 1-nth of the time, depending on how many parts it covers. This simulates the idea that each armor part should be damaged for the whole armor to be damaged.

1) The problem: repair of 1 durability multi-part-armor costs the same as repairing a single-part-armor once. Maybe that should be changed - multi-coverage should be more expensive to fix, as it provides more protection?

2) However, nerfing the armor durability would lead to armors breaking very quickly vs acid or full-body attacks.

3) In your new formula

So ... your formula is not only nerfing multi-part-armor, but it's nerfing it unequally. Why?

I'd assume that multi-body armor takes the same amount of damage before breaking as 2 separate pieces of armor. You would make bulti-body armor 33-50% weaker than single body armor ... so then why would anyone want multi-body armor?

Compare to the old formula - it's exactly 1 hit / body part to be damaged, always.

PlutusPleion commented 2 years ago

How rare is multi-part damage though? I was of the idea that like 95% of attacks all roll for a bp then subpart once.

I believe acid currently doesn't damage armor:

float item::acid_resist( bool to_self, int base_env_resist, const bodypart_id &bp ) const
{
    if( to_self ) {
        // Currently no items are damaged by acid
        return std::numeric_limits<float>::max();
}

I understand not wanting to have to repair multi-part clothing so often but we should also remember the inherent benefit they give in that you don't have to find another piece to cover the part it already covers and that all your repairs are centralized to one item.

An armor that covers 2 parts vs 2 armors than cover 1 part each. If they receive the same single instance damage rolls and roll equally for coverage and chance to get damaged, the total repairs you would have to do realistically should be equal. The current system means the armor has an invulnerability chance because you combined 2 items together.

The previous suggested is an attempt to find a middle-ground between.

Again, the realistic case where multi-part armor is more resistant is if the build and material itself accounts for distributing the force of the blow evenly across it's area and volume. For a majority of armor, the point of failure is usually due to the materials' resistances and the damage in a local area. Getting hit in the head in full plate armor doesn't make it more resistant to getting damaged just because it has greaves and a chest plate attached to it.

To make an example let's consider Chainmail armor. Just because we "join" them together it is now invulnerable/undamaged 83% of the time, to ALL damage, regardless of how much damage was done, and regardless of how much resistance it has. Another example, a full body thermal electric suit that's just a piece of cloth with wires is also undamaged 83% of the time regardless of damage/resistances. Just IMO that seems absurd.

If everyone wants to keep it as it is, no problem. Just wanted to bring attention to it.

Alternatively, we could add durability "ticks" in proportion to how many parts an armor covers. Currently there's 6(?) durability HP for each armor before it's completely destroyed. Maybe we can multiply that by the number of parts it covers. So say armor that covers 2 parts will have 12 durability ticks and then remove the current invulnerability check.

bombasticSlacks commented 2 years ago

The math for multi body parts is exactly right for what it does currently. Without it if I'm wearing torso armor and two separate arm armor pieces (3 items total) it has 3 times as much effective HP as just wearing 1 piece that covers the torso and arms. Also the armor damage ticks you take are distributed so the split armor gets weaker in more discrete steps than a single piece as well which is also a huge bonus.

Adding an involved shared HP pool or HP increment could fix this system but as it stands the math works to make them equal: "Something covering two body parts should get damaged half as much, three parts a third as much, four parts a quarter, etc." though it wont work out perfect in practice this is a way to scale their effective HP to be the same.

Wearing more separate armor pieces already has a bunch of benefits, like the above mentioned distributed weakening, as well as more customization and control I don't think people that want to wear easy full body solutions should be punished further.

PlutusPleion commented 2 years ago

I agree multi-part armor should have more effective HP, but currently it's not even effective HP, it's effective invulnerability.

Let's say 1 armor covers 2 bp vs 1 armor that covers 1 bp. They take 2 instances of damage, first is above resistance, second is below resistance. If the first armor successfully rolls for invulnerability on the first, and not on the second. Okay so you get your intended benefit. Now let's apply the scenario again. Well now because the damage/undamaged is not actually saved for multi-part armor, now your 2bp armor is still undamaged, while your 1bp armor is damaged twice. Repeat this a thousand times and the difference is huge.

The current equation only makes sense if there is no check for damage or resistance after to determine if armor is damaged. Then yes 1 parts have 100% to be damaged, 2 parts have 50% chance to be damaged, 3 parts have 33% change to be damaged and so on.

Giving multi-part armor more durability points would make more sense in simulating more effective HP.

edit: nvm after doing the math it does work out that way

bombasticSlacks commented 2 years ago

Giving multi-part armor more durability points would make more sense in simulating more effective HP.

I agree but that's the kind of nitty gritty work that requires real, hands dirty dev work, of which only a few of us do. Your above suggestions for damage chance linearity were easy to implement so I could put a PR up for that in 30 minutes. A good solution for this would take a lot more work and given a large sample size currently it is close enough to working correctly as is. If you wanted to implement a more nuanced HP system for armor I don't think anyone would stop you but I think it would have to be that complex of a solution.

EDIT: To make it clear I've looked at the code and as far as I understand it, it's not as simple as just setting hp = 6bps. There are a lot of hard coded bits and edge cases that would need to be manually modified. For example someone a few weeks ago on the Discord was talking about how right now ANYTHING* but 0-4 for damage is counted as the "++" hp state.

martosss commented 2 years ago

For me the problem is that with lower durability, the armor also weakens and allows more damage to get through, or outright gets destroyed and disappears. And if the armor is destroyed you spend a ton more materials and time to rebuild it from scratch. Effective HP matters because it gives you longer survivability in combat = More hits before

+ you die
+ your armor is destroyed completely

If your multi-armor breaks faster than 2 individual pieces, then it also starts letting damage through faster. This is a big problem, because once you get damaged enough things snowball - you get pain, less strength, less everything, you can't hit, you're slow .... and it's over. So the longevity of armor matters more for the actual length of the battle, not for the repair cost.

To make an example let's consider Chainmail armor. Just because we "join" them together it is now invulnerable/undamaged 83% of the time, to ALL damage, regardless of how much damage was done, and regardless of how much resistance it has. Another example, a full body thermal electric suit that's just a piece of cloth with wires is also undamaged 83% of the time regardless of damage/resistances. Just IMO that seems absurd.

So try to count how many hits it takes to damage full chainmail compared to its components:

The corresponding chance also applies to the thermal suit - it takes 1 damage every 6 hits, so it's like damaging all of its parts once.

Now, you make an interesting argument regarding the damage amount that is received. I was almost ready to admit defeat(that the current system is unfair) there, but I gave it some thought...

I think having this invulnerability is kind of gambling. case 1 - 3 sniper shots, then 1 average hit (so that might actually evade the damage chance - you're safe unfairly). case 2 - 3 average hits, then 1 sniper shot (so that might actually damage your armor - you're not safe unfairly).

Giving armor ticks in addition to durability, i.e. extending durability to 5 * body parts covered, would make durability calculation more accurate - it will decrease the "luck factor" if you remove the invulnerability check, so I'd agree that this is a good change.

However, this change will mostly preserve the current chance(due to the 2 cases above, which will even out in the long run). And you'll need to spend some time implementing it(I'm not sure how hard that will be). It would be a good thing to do though, so I'd support it.

LeahLuong commented 2 years ago

I think a good, "simple" fix for now would be to scale up the repair cost for suits based on their coverage of BP's. Important to keep in mind this may complicate future efforts to make armor durability more nuanced & might need to be walked back @ that time.

Still, all this discussion of suits is kind of derailing the thread. I think the OP got pretty close to the mark & the amendments proposed by bomb, Drew, & mart have merit. We still have yet to hear from kevin here.

bombasticSlacks commented 2 years ago

I already PRd the suggested final formula

bombasticSlacks commented 2 years ago

closing this as it was resolved already