Showing posts with label armor. Show all posts
Showing posts with label armor. Show all posts

Saturday, October 2, 2021

How Indie Stone Fixed the Bandana 90% Protection Bug... Poorly

Edit 2021-10-27: Indie Stone released Project Zomboid build 41.56, and at first look I think they've fixed the bug correctly.  I'll write a post that goes into detail soon.

Two weeks ago, I posted about how, because of a bug, bandanas effectively gave you 90% protection from zombie attacks (from standing zombies attacking you from the front).

About a week after I posted about bandanas, Indie Stone posted a changelist for their upcoming (at the time) 41.55 Build, and they mentioned "Fixed bandanas providing far too much head protection"

Two days ago, they released 41.55. 

This bug was not fixed correctly - it was a band-aid.  If I'd seen this bug fix as a code review, I would have sent it back so they could get it right before release.

What Was the Bug?

I had actually filed a bug on the Indie Stone forums in August, but at the time I didn't realize the ramifications.  When I realized what was going on, I posted my sensational example, that bandanas were effectively giving 90% protection.

You can look at my posts about the bug, but here's the summary:

Sometimes, when a zombie successfully attacks you, a piece of clothing will fall off of you and nullify the attack.  These items of clothing are exclusively worn on the head, and are basically hats/helmets, glasses, and some masks.

The chance that a piece of clothing falls off during an attack is one of the attributes (ChanceToFall) of the clothing defined in the scripts.  Football helmets had a ChanceToFall of 3.  Bandanas had a ChanceToFall of 5.  Baseball caps had a ChanceToFall of 80.  Glasses had a ChanceToFall of 50.  The adjusted value was modified to be larger if the attack was directed at the head or neck.

This line from the code was the problem:

if (((Clothing)inventoryItem3).getChanceToFall() <= 0 || Rand.Next(100) < n) continue;

Where n is the adjusted chance to fall value, and the code that came after was the code that made the clothing fall.

It basically says if the random 1-100 number is less than the adjusted chance to fall, DON'T FALL.  It should  have been > n, not < n.  Easy mistake to make.

Note that it also says that if the clothing's defined ChanceToFall is 0, it will never fall, despite the flipped <.

So the result of this bug was that a bunch of pieces of clothing that should almost never fall off were falling off regularly (bandanas, motorcycle helmets, football helmets).  Also, a bunch of hats that should fall off most of the time weren't (tin foil hats, baseball caps, beanies).

How Did They Fix It?

1. They removed the feature of falling clothing protecting you from attacks

They went from 41.54

        if (Rand.Next(100) > n4) {
            n = 1;
            boolean bl4 = false;
            if (this.getParentChar().helmetFall(n3 == BodyPartType.ToIndex(BodyPartType.Neck) || n3 == BodyPartType.ToIndex(BodyPartType.Head))) {
                return false;

}

To 41.55

        if (Rand.Next(100) > n4) {
            n = 1;
            boolean bl4 = false;
            this.getParentChar().helmetFall(n3 == BodyPartType.ToIndex(BodyPartType.Neck) || n3 == BodyPartType.ToIndex(BodyPartType.Head)); 

They basically removed that helmets falling would return false, meaning that "helmets" falling (a poorly named method, because it covered glasses and masks as well) no longer nullified successful attacks.

2. They adjusted the ChanceToFall values to 0 for SOME of the items, allowing those items to catch the other condition that I mentioned in the code snippet earlier (and so, never fall).

Changed items (plus a few more) with 41.54 values:

Military Helmet                       10
Bandana (Head)                         5
Kentucky Baseball Helmet              10
Riverside Rangers Baseball Helmet     10
Z Hurricanes Baseball Helmet          10
Crash Helmet                          10
Motorcycle Helmet                      3
Police Motorcycle Helmet              10
USA Crash Helmet                      10
Firefighter Helmet                    20
Football Helmet                        3
Hard Hat                              20
Mining Helmet                         20
Hockey Helmet                         10
Jockey Helmet - 1                     10
Riding Helmet                         10
Riot Helmet                            1
Airforce Helmet                       10
Spiffo Suit Head                      10
Bandana (Tied)                         5
Bandana (Face)                         5

Changed items (plus a few more) with updated 41.55 values:

Military Helmet                       10
Bandana (Head)                         5
Kentucky Baseball Helmet               0
Riverside Rangers Baseball Helmet      0
Z Hurricanes Baseball Helmet           0
Crash Helmet                          10
Motorcycle Helmet                      0
Police Motorcycle Helmet               0
USA Crash Helmet                       0
Firefighter Helmet                    20
Football Helmet                        0
Hard Hat                              20
Mining Helmet                         20
Hockey Helmet                         10
Jockey Helmet - 1                     10
Riding Helmet                         10
Riot Helmet                            0
Airforce Helmet                        0
Spiffo Suit Head                      10
Bandana (Tied)                         5
Bandana (Face)                         5

(I'm glad I wrote a little python script to extract these values while researching my other post!)

So, in 41.55, falling clothes no longer protect you, and certain (inconsistently determined) helmets aren't falling anymore.

But they didn't fix the actual bug I had mentioned.  Certain helmets weren't falling anymore, but others were - the code that was causing helmets to fall but tin foil hats to not usually fall was untouched.

My Reaction?

This is a half-assed solution.

I thought it was a cute feature that sometimes an attack could be nullified by a piece of clothing falling off.  Someone at Indie Stone did too, otherwise they never would have implemented it.  If they wanted to remove it that is certainly their decision.  But based on the other part of the fix I don't think they thought it through.

Changing the ChanceToFall values for SOME of the items shows that they didn't actually understand or investigate the bug they were fixing.  Motorcycle helmets were falling off too easily - so the solution is to change the ChanceToFall from 3 to 0?  They changed the ChanceToFall vales to 0 for a handful of items.  What about all of the other items?  Army helmets were also falling off too easily, but they were left at 10.  Firefighter helmets and Hard hats, which I think are the most common of these very protective helmets, are still falling off way more than they should.  They should actually fix the bug.

Edit: What Would I Do?

This section was written half a day after I first published this post, based on a comment cool_fox made on Reddit.

What would have been the result if they'd done my suggested fix, which was to swap the < for a > or >= in the snip of code I referenced earlier.


                int n = ((Clothing)inventoryItem3).getChanceToFall();
                if (bl) {
                    n += 40;
                }
                if (inventoryItem3.getType().equals(string)) {
                    n = 100;
                }
                if (((Clothing)inventoryItem3).getChanceToFall() <= 0 || Rand.Next(100) < n) continue;

This is something I think they should still do.  It will fix the problem where secure hats/helmets are falling more often than hats that are not well secured on the head.

But some hats and helmets would still be falling off more than they should.  Baseball caps would be falling off 100% of the time when the neck or head was hit, and 80% of the time otherwise.

To maintain the spirit of the preexisting code, I'd let clothes continue to fall, but ONLY when the head or neck was targeted.  I'd also let them continue protecting the wearer when clothes were knocked off (because I thought it was a cute detail).  I'd remove this code, because the purpose of it is to make items fall off more easily when the head or neck is hit.

And in the BodyDamage class I'd basically change this 41.54 code:

        if (Rand.Next(100) > n4) {
            n = 1;
            if (this.getParentChar().helmetFall(n3 == BodyPartType.ToIndex(BodyPartType.Neck) || n3 == BodyPartType.ToIndex(BodyPartType.Head))) {
                return false;
            }

To something like this:

        if (Rand.Next(100) > n4) {
            n = 1;
            if (n3 == BodyPartType.ToIndex(BodyPartType.Neck) || n3 == BodyPartType.ToIndex(BodyPartType.Head)) {
                if (this.getParentChar().helmetFall(true)) {
                    return false;
                }
            }

The intent would be to ONLY allow for helmets (and glasses and masks) to fall if the attack targeted the head or neck.  At that point, baseball caps would fall off (and protect) you at 80%, and bandanas would fall off at 5%.  And I'd restore the old values of ChanceToFall that were changed from 41.54 to 41.55.  

I'd then re-evaluate the ChanceToFall values for items that should fall easily to make the fall a little less easily.  Should glasses fall off at 50%?  Should tinfoil hats fall off at 80%.  I'd first look at cutting all of the values (including the small ChanceToFall ones) in half.  I wouldn't want any item falling off and protecting you 50% of the time or more - that way, it would be a happy little accident rather than protection you could count on having.  Glasses would fall off and protect on 25% of head/neck hits, and baseball caps would fall off during only 40% of head/neck hits.  Reasonable enough.

And I'd look to see where else helmetFall was being called, because I wouldn't want to break the code elsewhere.

And then, since I'm not that familiar with Project Zomboid code (I'm not someone who looks at PZ's code professionally), and I don't own this piece of code, I'd get someone to take a look to confirm I'm seeing everything I think I should be seeing.  I'm sure if I owned the code, I'd be much more familiar with it and would have an easier time finding the consequences of the changes I'm proposing here.

I'd also look into refactoring helmetFall.  There's some duplicate code there, with half the code pertaining to zombies and the other half to players, but many of the lines are identical.  I suspect the reason this bug originated because a change was made to the zombie half of the code but wasn't copied to the human half of the code.  I can accept that at some companies, under some deadlines, you might accept a little sloppy duplication in your code, but if that code causes a bug because the code wasn't adjusted in all places, that is a strong argument to fix that duplication problem.

I'd also want to change the name of helmetFall, since the name doesn't quite match what it does.  helmetFall checks to see if certain headgear (hats/helmets, glasses, masks) falls, and then makes the headgear fall, and then returns true if it did fall.  If I wanted to continue keeping that as one method (for simplicity), I might change the name to "doesHeadgearFall".  I have a little grudge against the name "helmetFall", because the first time I examined this part of the code I assumed it only dealt with helmets/hats, and I initially thought it made the helmets fall instead of randomly determining if the helmets fell.  Now I have a distrust of method names in the rest of the Project Zomboid code.

Now, back to the rest of the original post.

Should They Fix Their Processes?

I don't know.  It depends.  How many bugs are they creating?  What is the cost of their bugs?  How much time are they spending fixing bugs rather than doing new development?

A Code Review probably would have helped.  A second set of eyes catches a lot of the things the first set misses.  But do they need code reviews for bugs like this?  Do they have the time?  It is a small team working on a game and they are almost desperately trying to get multiplayer out.  This is the kind of bug that hardly anybody notices, so the consequences of just giving it a quick shake and putting a band-aid on the most sensational part are small.  

Unit testing?  I like unit tests, but it probably wouldn't have helped here.  If you think the bug is that bandanas are giving too much protection and a handful of helmets are falling too easily, you are only going to write unit tests for bandana protection and checking if your motorcycle helmet stops falling.

But, if they missing things like this bug fix, what else are they missing?  

Epilogue

The Indie Stone isn't writing rocket guidance software or medical device software or even financial software, so the consequences of 99%+ of their bugs is very small.  And nobody expects their software to be 100% bug free.  But when they're fixing something they know they got wrong on their first pass (a bug), they should probably spend a little more time trying to do things right.  Because the people who discovered the bugs are watching closely.


Thanks for reading!  If you think I made a mistake or have any questions you'd like me to answer by looking through the code, let me know!

Saturday, September 18, 2021

Bandanas Deflect Zombie Attacks 90% of the Time! (Not anymore - fixed in 41.55)

Edit 2021-10-27: Indie Stone released Project Zomboid build 41.56, and at first look I think they've fixed the bug correctly.  I'll write a post that goes into detail soon.

Edit 2021-09-30: In 41.55 they have "fixed" this bug by changing the code so that falling clothing no longer protects you.  They also adjusted some of the chancetofall rates on some helmets so that they never fall.  They have not fixed the bug that essentially made chancetofall actually represent the chance that the item would stay on.  I've covered this in a new post.

Bandanas are the most OP protective clothing in all Project Zomboid!  They deflect almost 90% of front attacks from a single zombie!

Glasses deflect almost 45% of all front attacks from a single zombie, and you can START with glasses in vanilla Project Zomboid.  That's better than taking the (fixed in 41.54) Thick Skinned trait!

I'll have more examples of the total protection you get from some vanilla Project Zomboid clothing at the end, but let me explain how this works.

I covered head protection in another blog post, and at the time I thought that helmets/hats could only fall off during an attack to the head or neck.  I didn't look carefully enough at the code, because masks and glasses also can fall off, and all of those clothing items can fall off during ANY zombie attack.

When any piece of clothing falls off, that means that a zombie attack hit your character and the falling clothing nullified the attack.  

So at every opportunity, you should wear a hat/helmet, glasses, and a bandana mask.  Each of those has a chance of completely nullifying a zombie attack that would otherwise hit you!


BodyDamage Class

Let's go back to the BodyDamage class to demonstrate how hats/helmets/glasses/bandanas can fall and nullify "successful" zombie attacks:

        if (Rand.Next(100) > n4) {
            n = 1;
            boolean bl4 = false;
            if (this.getParentChar().helmetFall(n3 == BodyPartType.ToIndex(BodyPartType.Neck) || n3 == BodyPartType.ToIndex(BodyPartType.Head))) {
                return false;
            }

In the BodyDamage class, once your character is hit by a zombie attack one of the first things the code does is check getParentChar().helmetFall.

It sends TRUE if the target of the attack was either the neck or head.  In my earlier post, I misinterpreted this to mean that helmetFall was only called if the attack was the neck or head - I was wrong about that.

And you can see here that if helmetFall is true, this method returns false, meaning the attack is nullified and no damage is recorded on your character.


IsoGameCharacter Class

So let's take a closer look at helmetFall, in the IsoGameCharacter class:

    public boolean helmetFall(boolean bl, String string) {
        IsoPlayer isoPlayer = (IsoPlayer)Type.tryCastTo((Object)this, IsoPlayer.class);
        boolean bl2 = false;
        InventoryItem inventoryItem = null;
        IsoZombie isoZombie = (IsoZombie)Type.tryCastTo((Object)this, IsoZombie.class);
        if (isoZombie != null && !isoZombie.isUsingWornItems()) {
	    // Code skipped because this is only for Zombies
        } else if (this.getWornItems() != null && !this.getWornItems().isEmpty()) {
            for (int i = 0; i < this.getWornItems().size(); ++i) {
                WornItem wornItem = this.getWornItems().get(i);
                InventoryItem inventoryItem3 = wornItem.getItem();
                String string2 = wornItem.getLocation();
                if (!(inventoryItem3 instanceof Clothing)) continue;
                int n = ((Clothing)inventoryItem3).getChanceToFall();
                if (bl) {
                    n += 40;
                }
                if (inventoryItem3.getType().equals(string)) {
                    n = 100;
                }
                if (((Clothing)inventoryItem3).getChanceToFall() <= 0 || Rand.Next((int)100) < n) continue;
                IsoFallingClothing isoFallingClothing = new IsoFallingClothing(this.getCell(), this.getX(), this.getY(), PZMath.min((float)(this.getZ() + 0.4f), (float)((float)((int)this.getZ()) + 0.95f)), Rand.Next((float)-0.2f, (float)0.2f), Rand.Next((float)-0.2f, (float)0.2f), inventoryItem3);

Remember that bl was TRUE if the target was the head or neck.  But even attacks on other body parts make it to this part of the code (with bl set to FALSE).

We skip over some code because it only applies to zombies.  Maybe I'll go over that in another post.  It actually doesn't protect the zombies as much from attacks.

Then we go through every piece of clothing worn by the character.  This includes every piece of clothing, not just helmets and hats.

We skip the code if it isn't a piece of clothing, and continue to the next piece of clothing.

Then we get the ChanceToFall, and call that n.

(As an example, let's look at the Bandana Mask, which has a chance to fall of 5 - at the end of this entry I have a list of all vanilla items that can fall, with their ChanceToFall value).

Here we see bl used.  If the target was the head or neck, bl is true, and n is increased by 40 (to 45 for our example).

And then there is the decision line - if a random d100 roll is less than n (45 if the target was the head/neck, 5 otherwise), then we continue.  Continue means we go back and look at the next item of clothing in the list - nothing happens, and we go to the next piece of clothing worn by the character.

And if we get past the d100 check, the clothing falls.  In the Bandana example, this means that Bandanas stay on 5% of the time, and when the target is the head or neck they stay on 45% of the time.

As I've reported before, this is a bug.  If the Bandana Mask should have the low 5% chance of falling, not staying on.  So this line should read Rand.Next((int)100>= n.


How Much Protection Is This?

In my last post, I calculated that a single zombie attacking from the front targets the neck 6.4% of the time, and the head or neck 12.7% of the time.  There's a little rounding there, so I'm just going to use the slightly more accurate figure of 6.36% for head hits (and neck hits) in this example.

Bandanas (as a mask or on your head) have a ChanceToFall of 5, meaning there is a 55% chance to fall from a head/neck attack, and a 95% chance to fall for other body parts.  If a head is targeted 6.36% of the time and the neck is targeted 6.36% of the time, then all other body parts together are targeted 87.28% of the time.

  • Head/Neck:  55% chance to fall * 12.72 chance of being targeted
  • Other: 95% chance to fall * 87.28% chance of being targeted
Total Protection:  89.912%

Glasses have a ChanceToFall of 50.

Total Protection = 10% * .1272 + 50% * .8728 = 44.912%

Reflective Ski Glasses (and Safety Goggles and some other eyewear) have a ChanceToFall of 20.

Total Protection = 40% * .1272 + 80% * .8728 = 71.312%

Baseball Caps (and Tinfoil Hats) have a ChanceToFall of 80, so the protection isn't great.  If you are hit in the head or neck while wearing them, they never fall off.  Plus they don't have any bite or scratch protection.  Still they, are better than nothing because when other parts of your body are hit, they might fall off.  Plus, you can start with them.

Total Protection = 20% * .8728 = 17.456%

Calculating the protection from items like the Riot Helmet was a little more complicated, because even when they don't fall off of your head they offer 100% protection to the head, so that modifies the protection a little.  I ran some items through my spreadsheet (with slightly more accurate numbers):

  • Riot Helmet - 96.52% Total Protection
  • Football Helmet - 94.65% Total Protection
  • Military Helmet - 88.01% Total Protection
  • Hard Hat or Firefighter Helmet - 78.73% Total Protection

Also remember that these values combine if you are wearing more than one.

If you are wearing a head bandana + a bandana mask + regular glasses, the total protection is actually

1 - (1-0.89912) * (1-0.89912) * (1-0.44912) = 99.44%!

If you start with a baseball cap + regular glasses, you get to start with a protection value of

1 - (1-0.17456) * (1-0.44912) = 54.53%!

 

Comments On the Bug

So one of the reasons this is such a big deal is because of the bug I mentioned.  Things that should stay on almost all the time are falling off, and items that should fall easily are staying on.  

I suspect that Indie Stone will rework this mechanic a little more than just fixing the specific bug I mentioned.  If they ONLY fix my bug, baseball caps will become pretty OP, falling off 100% of the time the head or neck is attacked, and 80% of the time other body parts are attacked.  But at least baseball caps are supposed to be fairly easy to fall off in real life.  Bugged Football Helmets fall off to 57% of head/neck attacks, and 97% to other attacks.

Whatever work they do to this part of the code, I'll cover it when it changes.  I suspect glasses will still be worth wearing, even if they aren't as powerful as they are now. 


Vanilla Items That Can Fall

Here's a list of vanilla items that can fall, and the base % chance that they will fall.  Remember, while this bug still exists, it is actually the base % chance that they won't fall.  For the best benefit right now, pick items with a low percentage.  Once the bug is fixed, I'll update this blog post and tell everyone to pick items with a high percentage chance of falling.

Military Helmet                       10
Bandana (Head)                         5
Baseball Cap                          80
KY Baseball Cap                       80
Baseball Cap                          80
Army Baseball Cap                     80
Kentucky Baseball Helmet              10
Riverside Rangers Baseball Helmet     10
Z Hurricanes Baseball Helmet          10
Beanie Hat                            40
Beret                                 80
Army Beret                            80
Bicycle Helmet                        20
Chef Hat                              80
Cowboy Hat                            80
Crash Helmet                          10
Motorcycle Helmet                      3
Police Motorcycle Helmet              10
USA Crash Helmet                      10
Ear Muffs                             80
Ear Protectors                        80
Fast Food Server Hat                  80
Ice Cream Server Hat                  80
Spiffo's Server Hat                   80
Fedora                                80
Black Band Fedora                     80
Firefighter Helmet                    20
Football Helmet                        3
Tartan Golf Cap                       80
Golf Cap                              80
Hard Hat                              20
Mining Helmet                         20
Hockey Helmet                         10
Jockey Helmet - 1                     10
Jockey Helmet - 2                     10
Jockey Helmet - 3                     10
Jockey Helmet - 4                     10
Jockey Helmet - 5                     10
Jockey Helmet - 6                     10
Police Trooper Hat                    70
Police Deputy Hat                     70
Raccoon Hat                           30
Ranger Hat                            70
Riding Helmet                         10
Riot Helmet                            1
Santa Hat                             80
Green Santa Hat                       80
Shower Cap                            80
Airforce Helmet                       10
Spiffo Suit Head                      10
Summer Hat                            80
Medical Cap                           80
Sweatband                             50
Wedding Veil                          80
Welder Mask                           30
Winter Hat                            40
Woolly Hat                            40
Bucket Hat                            80
Bonnie Hat                            80
Visor                                 80
Peaked Army Cap                       80
Bandana (Tied)                         5
Bandana (Face)                         5
Newspaper Hat                         80
Party Hat with Stars                  80
Coloured Party Hat                    80
Tin Foil Hat                          80
Hockey Goalie Mask                    30

Reflective Ski Sunglasses             20
Aviator Glasses                       50
Glasses                               50
Reading Glasses                       50
Safety Goggles                        20
Shooting Glasses                      20
Ski Goggles                           20
Sunglasses                            50
Swimming Goggles                      30

Although Riot Helmets, Football Helmets, and Motorcycle Helmets fall more easily than the bandana, they already offered 100% protection to the head, so I don't consider them as OP as bandanas.


Thanks for reading!  If you see any mistakes, or if you have a question you'd like me to answer, let me know in the comments!

Wednesday, August 25, 2021

Why 100% Protection Doesn't Protect 100% of the Time

Update 2021-09-16: In Build 41.54 this has been fixed.  Holes are put in clothing AFTER protection is determined.

Every once in a while I'll hear (read) about someone complaining that they had 100% protection on a body part and still got hit by a zombie.

Should 100% protection protect you 100% of the time?  Maybe.  Maybe not.  But it doesn't.  Why?

Sometimes you get a hole in your clothing from an attack shortly before Project Zomboid calculates whether your clothing protects you from that attack.

The Code

The BodyDamage class is where damage (to your body) is determined.

Before we get to this part of the code, Project Zomboid has already randomly determined which body part is being attacked.

Right before the roll to see if you are hit, it checks to see if you will get a hole because of the attack

        bl = false;
        bl = this.getParentChar().addHoleFromZombieAttacks(BloodBodyPartType.FromIndex(n3));
        if (Rand.Next(100) > n4) {

addHoleFromZombieAttacks is in the IsoGameCharacter class and randomly determines if this zombie attack put a hole in a piece of clothing on the body part being attacked.  This is really important, because if there is a new hole, the protection on that part of your body goes down.

After it checks to see if you got a hole, it checks to see if you've been hit.  So it is possible to get a hole in your clothes and not get hit.

After Project Zomboid determines you've been hit, it figures out if it is a scratch or laceration or bite.  Here is code from what happens if you are scratched.  The code for lacerations and bites is almost identical to this part:

                Float f2 = Float.valueOf(this.getParentChar().getBodyPartClothingDefense(n3, false, false));
                if (this.getHealth() > 0.0f) {
                    this.getParentChar().getEmitter().playSound("ZombieScratch");
                }
                if ((float)Rand.Next(100) < f2.floatValue()) {
                    return false;
                }

getBodyPartClothingDefense adds up the clothing protection from the body part being attacked, with a maximum of 100.  Remember, though, that if you acquired a hole right before this, your protection is lower than it was right before the attack.

If a random 0-99 roll is less than defense value of your clothes, the protection holds and nothing else happens.

If the protection doesn't hold, then the code continues and you take the damage (and possibly get the zombie infection).

Some Thoughts

If you are wearing magical longjohns with 100% protection to your upper torso, a single attack can put a hole in it and reduce that protection to 0%.  

If you are wearing layers, a single hole will not reduce your protection by as much, and if your layers gives you more than 100% protection, you may still have 100% protection!

Some items, like the Hard Hat, can't have holes.  Unless a Hard Hat gets knocked off, you will never take damage to the head while wearing it.


Thanks for reading!  If you see a mistake or want me to answer a question about the code, please let me know!


Update:  Project Zomboid developer lemmy101 posted that this is unintentional and will be fixed.  I'll be sure to follow up on this topic when that happens!

Monday, August 23, 2021

How Head Protection Works

Edit 2021-09-18: I misinterpreted this code a little.  Here I say that the code checks to see if a hat/helmet falls off if the attack is on the head or neck.  The correct interpretation is that the code checks to see if any clothing with the ChanceToFall attribute falls whenever there is an attack (with the chance to fall being modified if the attack is on the head or neck).  For more about this, take a look at this newer blog entry.

Head and neck protection is just like protection for other body parts, with one little addition - your hat might fall off.

So how does it work?

When a zombie attacks you, here are the steps:

  • Find hit location (the body part being attacked)
  • Calculate (potential) damage
  • Check if attack puts a hole in the clothing for that body part
  • Check to see if the attack hits
  • Check if a hat falls off
  • Determine severity of the attack (scratch, laceration, bite)
  • Check if the clothing protection repels the attack
  • Actually damage the character (change the health stat, etc)

Every time you see your hat fall off your head, it is because you were HIT on the head or neck, but instead of you taking damage your hat just fell off.  If your hat stays on, it can still protect you from the attack.

Let's look at the code!

Hats Sometimes Fall

Once the code has determined your survivor is hit

        if (Rand.Next(100) > n4) {
            n = 1;
            boolean bl4 = false;
            if (this.getParentChar().helmetFall(n3 == BodyPartType.ToIndex(BodyPartType.Neck) || n3 == BodyPartType.ToIndex(BodyPartType.Head))) {
                return false;
            }

Before anything else actually happens, it checks to see if your "helmet" falls!  I say "helmets" but this applies to all hats!  The baseball cap has a much greater chance of falling off here.  If it does it returns and skips all the code that calculates the damage!  No damage!

Your hat just took the hit for you.

helmetFall actually leads to a bunch of code in IsoGameCharacter, where it determines whether or not your hat fell based on the ChanceToFall attribute in the scripts.  In vanilla PZ the hats are in clothing_hats.txt, and here's an example of the hardhat:

	item Hat_HardHat
	{
		Type = Clothing,
		DisplayName = Hard Hat,
		ClothingItem = Hat_HardHat,
		BodyLocation = Hat,
		IconsForTexture = HardHatYellow;HardHatBlue;HardHatRed;HardHatWhite,
		CanHaveHoles = false,
		BloodLocation = Head,
		BiteDefense = 100,
		ScratchDefense = 100,
		ChanceToFall = 20,
		Insulation = 0.15,
		WindResistance = 0.25,
		WaterResistance = 0.5,
	}

Hard Hats have a base 20% chance to fall.  If you look at the other hats, you can see Baseball Caps have an 80% chance to fall and Football Helmets have a 3% chance to fall.

One thing I find interesting is that even if the hat doesn't normally protect your neck, a neck attack can still trigger your hat to fall and avoid causing damage to you.

Hats Also Protect Like Other Clothing

Once you are hit, the severity of the hit is calculated and then it checks to see if your clothing protected you, also in BodyDamage:

                Float f2 = Float.valueOf(this.getParentChar().getBodyPartClothingDefense(n3, false, false));
                if (this.getHealth() > 0.0f) {
                    this.getParentChar().getEmitter().playSound("ZombieScratch");
                }
                if ((float)Rand.Next(100) < f2.floatValue()) {
                    return false;
                }

getBodyPartClothingDefense goes through your clothes and determines which clothing covers the body part that was hit and then adds up the defense values (ScratchDefense for scratches and lacerations, BiteDefense for bites).

Once the defense number (up to 100) is calculated, it plays the awful zombie scratch sound.  

Then, it randomly rolls to check if your defense protected you from the attack.  If it did protect you, you skip all the damage!

So this is why sometimes you heard the zombie crunch sound but when you check your health status you have taken no damage.  Your armor protected you.

There Might Be a Bug

On 2021-08-13 I filed a bug stating that the ChanceToFall was being used incorrectly, opposite to how it should be used.  This would mean that a Hard Hat with a 20% chance to fall is actually falling 80% of the time.

Whether or not I am right about the bug or if the bug has been fixed by the time you read this, falling hats protect you from attacks.

Conclusion

Always wear a hat!  Even if it doesn't protect you much, a fallen hat means you were completely protected from an attack that otherwise would have caused head or neck damage.  And when the hat doesn't fall off, it still protects you like other pieces of clothing protect other parts of your body.


Thanks for reading!  If you see a mistake here or if you have any questions you'd like me to answer, let me know!


Do Gloves Protect You From Broken Glass?

Yes, gloves protect you from handling broken glass - any pair of gloves.  But gloves are not needed when removing broken glass from a smashe...