Wednesday, September 1, 2021

Calculating Zombies vs. Walls/Doors/Barricades Damage

Update 2022-01-10: I checked build 41.65.  The HP of barricades is still 1000/5000/5000 as I indicated in this post.

I recently posted about wall strength.  Really, the only reason to care about whether one wall is stronger than another is because walls protect us from our opponents.  That's mainly zombies, since multiplayer hasn't been released for Build 41.

This is actually a big topic, so I'm only covering some of the code right now.

Wall/Door/Barricade Health

Wall health ranges from 400 - 1400, depending on factors including wall type, carpentry skill level, and upgrade path.  Either trust me on that, or go read my other post.  But how strong are doors and barricades?

Door Health

Regular doors that characters construct (player-constructed doors) have 300 health plus bonuses for carpentry skill and the handy trait.  This is set in ISWoodenDoor.lua:

-- return the health of the new wall, it's 300 + 100 per carpentry lvl
function ISWoodenDoor:getHealth()
	return 300 + buildUtil.getWoodHealth(self);
end

The comment is wrong (wrong both because this is not a wall, and because the carpentry modifier is 50) but we can see the correct value is set in ISBuildUtil.lua, just like it is for log walls:

-- give the health of the item, depending on you're carpentry level + if you're a construction worker
buildUtil.getWoodHealth = function(ISItem)
	if not ISItem or not ISItem.player then
		return 100;
	end
	local playerObj = getSpecificPlayer(ISItem.player)
	local health = (playerObj:getPerkLevel(Perks.Woodwork) * 50);
	if playerObj:HasTrait("Handy") then
		health = health + 100;
	end
	return health;
end

So with level 10 carpentry and handy, the door can have a maximum of 900 health.  At every level of carpentry, doors are 100 weaker than log walls.

It's important to note that player-constructed doors are not the same doors that you normally find in the game world.  Most of the pre-existing doors are IsoDoors, while player-constructed doors are still IsoThumpables:

function ISWoodenDoor:create(x, y, z, north, sprite)
	if self.openNailsBox then
		buildUtil.openNailsBox(self);
	end
	local cell = getWorld():getCell();
	self.sq = cell:getGridSquare(x, y, z);
	local openSprite = self.openSprite;
	if north then
		openSprite = self.openNorthSprite;
	end
	self.javaObject = IsoThumpable.new(cell, self.sq, sprite, openSprite, north, self);

Later, in ISBuildUtil, these player-constructed doors are set with an isDoor property to differentiate them from other IsoThumpables:

function buildUtil.setInfo(javaObject, ISItem)
	javaObject:setCanPassThrough(ISItem.canPassThrough);
	javaObject:setCanBarricade(ISItem.canBarricade);
	javaObject:setThumpDmg(ISItem.thumpDmg);
	javaObject:setIsContainer(ISItem.isContainer);
	javaObject:setIsDoor(ISItem.isDoor);

Barricade Health

Barricades are handled in the IsoBarricade class. 

There are three types of barricades:

  • Planks
  • Metal Bars
  • Metal Sheets

You can put up to 4 planks on either side of a door/window to barricade it.  Each plank has a base health of 1000.  Yes, 1000.

    public void addPlank(IsoGameCharacter isoGameCharacter, InventoryItem inventoryItem) {
        int n;
        if (!this.canAddPlank()) {
            return;
        }
        int n2 = 1000;
        if (inventoryItem != null) {
            n2 = (int)((float)inventoryItem.getCondition() / (float)inventoryItem.getConditionMax() * 1000.0f);
        }
        if (isoGameCharacter != null) {
            n2 = (int)((float)n2 * isoGameCharacter.getBarricadeStrengthMod());
        }
        for (n = 0; n < 4; ++n) {
            if (this.plankHealth[n] > 0) continue;
            this.plankHealth[n] = n2;
            break;
        }

Plank barricade health is modified by the condition of the plank, and is also modified by getBarricadeStrengthMod, which returns a value between 1.0 and 1.5, depending on the character's carpentry level (I'll show getMetalBarricadeStrengthMod() a little later, which is almost the same).

Does this mean that if you put four planks on both sides of a window, you have 12,000 points of protection, compared with just 1400 for the best wall?  Yes, but there's a little more to that that I'll get into when I cover zombie damage.

Metal bars and sheets have very similar code, so I'll put them together here:

    public void addMetalBar(IsoGameCharacter isoGameCharacter, InventoryItem inventoryItem) {
        if (this.getNumPlanks() > 0) {
            return;
        }
        if (this.metalHealth > 0) {
            return;
        }
        if (this.metalBarHealth > 0) {
            return;
        }
        this.metalBarHealth = 3000;
        if (inventoryItem != null) {
            this.metalBarHealth = (int)((float)inventoryItem.getCondition() / (float)inventoryItem.getConditionMax() * 5000.0f);
        }
        if (isoGameCharacter != null) {
            this.metalBarHealth = (int)((float)this.metalBarHealth * isoGameCharacter.getMetalBarricadeStrengthMod());
        }
    public void addMetal(IsoGameCharacter isoGameCharacter, InventoryItem inventoryItem) {
        if (this.getNumPlanks() > 0) {
            return;
        }
        if (this.metalHealth > 0) {
            return;
        }
        this.metalHealth = 5000;
        if (inventoryItem != null) {
            this.metalHealth = (int)((float)inventoryItem.getCondition() / (float)inventoryItem.getConditionMax() * 5000.0f);
        }
        if (isoGameCharacter != null) {
            this.metalHealth = (int)((float)this.metalHealth * isoGameCharacter.getMetalBarricadeStrengthMod());
        }

Actually, this code looks a little too similar.  It looks like both of these lines give a base 5000 health (modified by condition) to the barricade.  Looks like a bug, where the metal bar was intended to only have 3000 health.  It might actually give 3000 health when there is no inventoryItem, but if it works that should be unintended.  I reported the bug.

The health of the barricade then gets modified by getMetalBarricadeStrengthMod in IsoGameCharacter, which I'm listing here because it is a little complicated:

    public float getMetalBarricadeStrengthMod() {
        switch (this.getPerkLevel(PerkFactory.Perks.MetalWelding)) {
            case 2: {
                return 1.1f;
            }
            case 3: {
                return 1.14f;
            }
            case 4: {
                return 1.18f;
            }
            case 5: {
                return 1.22f;
            }
            case 6: {
                return 1.16f;
            }
            case 7: {
                return 1.3f;
            }
            case 8: {
                return 1.34f;
            }
            case 9: {
                return 1.4f;
            }
            case 10: {
                return 1.5f;
            }
        }
        int n = this.getPerkLevel(PerkFactory.Perks.Woodwork);
        if (n == 2) {
            return 1.1f;
        }
        if (n == 3) {
            return 1.14f;
        }
        if (n == 4) {
            return 1.18f;
        }
        if (n == 5) {
            return 1.22f;
        }
        if (n == 6) {
            return 1.26f;
        }
        if (n == 7) {
            return 1.3f;
        }
        if (n == 8) {
            return 1.34f;
        }
        if (n == 9) {
            return 1.4f;
        }
        if (n == 10) {
            return 1.5f;
        }
        return 1.0f;
    }

Basically, it is saying that if you have metalworking of levels 2-10, then you will use that to modify the barricade's health.  If you don't have that, then if you have carpentry level 2-10 that will determine the modifier.  If you don't have either, the modifier is 1.  Personally, I think this should be adjusted, because if you have level 10 carpentry and level 0 metalworking, you get a modifier of 1.5.  But if you have level 10 carpentry and level 2 metalworking, you only get a modifier of 1.1.  Plus deciding on whether to use switch statements or multiple ifs would be cleaner (and there are even better ways generate these modifiers this than that).

Also, there's a bug.  The value for metalwork level 6 is probably a typo and should be 1.26.  I reported the bug, and used the bug as an excuse to also suggest they rework the method.  I didn't say so in my bug report, but that is just bad code.  I'm reluctant to criticize some code because sometimes the compiler + decompiler process can make code look worse than it originally was, but no decompiler is going to produce that from good code.

So the maximum health of a barricaded window is 15,000 (barricading both sides with metal sheets if you have level 10 metalworking), not including what I think are bugs.

Zombie Damage

But that doesn't exactly mean that a 15,000 health barricaded window is 10+ times better than the best wall, because zombies damage these two different structures differently.

First, let's look at IsoThumpable, which handles walls and player-constructed doors:

    public void Thump(IsoMovingObject isoMovingObject) {
        // Some code removed by Ed for brevity
        if (isoMovingObject instanceof IsoZombie) {
            int n;
            // More code removed by Ed for brevity
            int n2 = isoMovingObject.getCurrentSquare().getMovingObjects().size();
            if (isoMovingObject.getCurrentSquare().getW() != null) {
                n2 += isoMovingObject.getCurrentSquare().getW().getMovingObjects().size();
            }
            if (isoMovingObject.getCurrentSquare().getE() != null) {
                n2 += isoMovingObject.getCurrentSquare().getE().getMovingObjects().size();
            }
            if (isoMovingObject.getCurrentSquare().getS() != null) {
                n2 += isoMovingObject.getCurrentSquare().getS().getMovingObjects().size();
            }
            if (isoMovingObject.getCurrentSquare().getN() != null) {
                n2 += isoMovingObject.getCurrentSquare().getN().getMovingObjects().size();
            }
            if (n2 >= (n = this.thumpDmg)) {
                int n3 = 1 * ThumpState.getFastForwardDamageMultiplier();
                this.Health -= n3;
            } else {
                this.partialThumpDmg += (float)n2 / (float)n * (float)ThumpState.getFastForwardDamageMultiplier();
                if ((int)this.partialThumpDmg > 0) {
                    int n4 = (int)this.partialThumpDmg;
                    this.Health -= n4;
                    this.partialThumpDmg -= (float)n4;
                }
            }

A few points:

If many zombies (8+) are thumping on the wall together (the default value for thumpDmg is set originally at 8, but it might be set to something else elsewhere), the zombies together cause one point of damage (multiplied depending on the fast forward state).  If there are fewer than that, they'll cause a fraction of one point of damage (multiplied by the fast forward state).  It looks like partialThumpDmg is keeping track of fractions of damage less than one for the next time thump damage is calculated.

Damage to pre-built doors (most of the ones that exist in the world already) is handled similarly, in the IsoDoor class:

            int n = isoMovingObject.getCurrentSquare().getMovingObjects().size();
            if (isoMovingObject.getCurrentSquare().getW() != null) {
                n += isoMovingObject.getCurrentSquare().getW().getMovingObjects().size();
            }
            if (isoMovingObject.getCurrentSquare().getE() != null) {
                n += isoMovingObject.getCurrentSquare().getE().getMovingObjects().size();
            }
            if (isoMovingObject.getCurrentSquare().getS() != null) {
                n += isoMovingObject.getCurrentSquare().getS().getMovingObjects().size();
            }
            if (isoMovingObject.getCurrentSquare().getN() != null) {
                n += isoMovingObject.getCurrentSquare().getN().getMovingObjects().size();
            }
            int n2 = ThumpState.getFastForwardDamageMultiplier();
            int n3 = ((IsoZombie)isoMovingObject).strength;
            if (n >= 2) {
                this.DirtySlice();
                this.Damage(((IsoZombie)isoMovingObject).strength * n2);
                if (SandboxOptions.instance.Lore.Strength.getValue() == 1) {
                    this.Damage(n * 2 * n2);
                }
            }

Here, you can see that if there aren't at least 2 zombies banging on the IsoDoor, there will be no damage.  I've seen that happen, but wasn't sure where that was happening until now.

I don't know what DirtySlice() does.  I haven't seen a non-empty definition of it that would affect IsoDoors.  Maybe it is a placeholder for something they are adding in later.

Damage to IsoDoors is actually determined by the strength of the zombie!  And if the zombie is superhuman it causes even more damage!  As a reminder, here is the code from IsoZombie setting zombie strength:

        if (this.strength == -1 && SandboxOptions.instance.Lore.Strength.getValue() == 1) {
            this.strength = 5;
        }
        if (this.strength == -1 && SandboxOptions.instance.Lore.Strength.getValue() == 2) {
            this.strength = 3;
        }
        if (this.strength == -1 && SandboxOptions.instance.Lore.Strength.getValue() == 3) {
            this.strength = 1;
        }

So zombie strength can be either 1, 3, or 5.

Damage to barricades is handled in the IsoBarricade class:

    public void Thump(IsoMovingObject isoMovingObject) {
        if (this.isDestroyed()) {
            return;
        }
        if (isoMovingObject instanceof IsoZombie) {
            int n = this.getNumPlanks();
            boolean bl = this.metalHealth > 2500;
            int n2 = ThumpState.getFastForwardDamageMultiplier();
            this.Damage(((IsoZombie)isoMovingObject).strength * n2);

This doesn't have any mention of multiple zombies.  It does use zombie strength, but no modifier for having superhuman strength.

I don't know if there is other code that handles multiple zombies, so it might not matter that barricades aren't counting how many zombies are around before calculating damage.  So besides possible differences because of multiple zombies, here is what I'm seeing about zombie damage to these structures:

  • Zombie strength is ignored when attacking walls (and other standard thumpables)
  • Zombie strength is used for calculating damage to both doors and barricades
  • Superhuman zombies cause even more damage to IsoDoors (most of the pre-built doors)
  • Solo zombies can't damage IsoDoors (most of the pre-built doors) alone

Summary

Barricades may look strong, but they aren't overly powerful against zombie attacks.  The best barricade has 15,000 health, compared to 1,400 health on the best wall.  But zombies don't cause much damage to walls - it looks like they cause 0.125 each (maximum of 1 if eight or more zombies gang up).  Standard strength zombies each cause 3 damage to barricades, so that's 24 times as much damage.

So don't substitute barricaded windows for walls when constructing your base, because walls are still better against zombies.  Against human survivors with weapons, it is a different story.  But I'm not covering that today.


Thanks for reading!  If have any questions, let me know!  And if you notice a mistake I made here, let me know that too - especially if it is about multiple zombies thumping on a single target.


I'm trying to work on mods, so it may be a while before I get another post up!


Correction:  In the first version of this post, I incorrectly confused player-built doors with the pre-built doors.  They are different structures and handle damage from zombies differently.  I have updated this post to reflect that.

3 comments:

  1. Awesome post, but now I'm more curious about the HP values of the various metal doors and the double doors too.

    ReplyDelete
  2. The essential reason to have an expert to clean your carpet is that they are certified and professional too. This is immensely important because a certified carpet cleaner knows exactly how to clean every kind of carpet. Upholstery Cleaning Melbourne

    ReplyDelete
  3. Hi, thank you for your efforts. I want to create a mod for a multiplayer server and that mod should prevent climbing through player-made walls. How can I check if the wall is made by a player?
    Thanks in advance!

    ReplyDelete

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...