Sunday, October 3, 2021

Do the Fast/Slow Healer Traits Affect Fractures?


The wiki says that the Fast Healer and Slow Healer traits do not apply to fractures.

Does not apply to fractures and exercise fatigues. Recently inflicted injuries starts with +20% less severity.

But the wiki is wrong.

Fast Healer and Slow Healer do apply to fractures, both from falling from high distances and from vehicle crashes.

They're done a little differently, so let's look at the code.

Fractures From Falling

This comes from the IsoGameCharacter class, in the DoLand method:

        if (this.fallTime > 70.0f) {
            int n = 100 - (int)((double)this.fallTime * 0.6);
            if (this.getInventory().getMaxWeight() - this.getInventory().getCapacityWeight() < 2.0f) {
                n = (int)((float)n - this.getInventory().getCapacityWeight() / this.getInventory().getMaxWeight() * 100.0f / 5.0f);
            }
            if (this.Traits.Obese.isSet() || this.Traits.Emaciated.isSet()) {
                n -= 20;
            }
            if (this.Traits.Overweight.isSet() || this.Traits.VeryUnderweight.isSet()) {
                n -= 10;
            }
            if (this.getPerkLevel(PerkFactory.Perks.Fitness) > 4) {
                n += (this.getPerkLevel(PerkFactory.Perks.Fitness) - 4) * 3;
            }
            if (Rand.Next(100) >= n) {
                if (!SandboxOptions.instance.BoneFracture.getValue()) {
                    return;
                }
                float f2 = Rand.Next(50, 80);
                if (this.Traits.FastHealer.isSet()) {
                    f2 = Rand.Next(30, 50);
                } else if (this.Traits.SlowHealer.isSet()) {
                    f2 = Rand.Next(80, 150);
                }
                switch (SandboxOptions.instance.InjurySeverity.getValue()) {
                    case 1: {
                        f2 *= 0.5f;
                        break;
                    }
                    case 3: {
                        f2 *= 1.5f;
                    }
                }
                this.getBodyDamage().getBodyPart(BodyPartType.FromIndex(Rand.Next(BodyPartType.ToIndex(BodyPartType.UpperLeg_L), BodyPartType.ToIndex(BodyPartType.Foot_R) + 1))).setFractureTime(f2);
            }

It's an interesting read, but I'm not going to walk through the whole thing.

The value n gets modified by a few different factors, and if the random 1-100 roll is equal to or larger than n, you have a fracture.

The value f2 is assigned a random number that can be generated based on having FastHealer or SlowHealer.  You can see that FastHealers get a smaller f2 and SlowHealers get a larger f2.  Eventually, f2 is sent to setFractureTime for a random body part.  Clearly, the wiki is wrong here - whether you have FastHealer or SlowHealer affects how long your body part will be fractured.

Fractures From Vehicle Crashes

In the BaseVehicle class, we see in addRandomDamageFromCrash that setFractureTime is handled differently:

            float f2 = Math.max(Rand.Next(f - 15.0f, f), 5.0f);
            if (isoGameCharacter.Traits.FastHealer.isSet()) {
                f2 = (float)((double)f2 * 0.8);
            } else if (isoGameCharacter.Traits.SlowHealer.isSet()) {
                f2 = (float)((double)f2 * 1.2);
            }
            switch (SandboxOptions.instance.InjurySeverity.getValue()) {
                case 1: {
                    f2 *= 0.5f;
                    break;
                }
                case 3: {
                    f2 *= 1.5f;
                }
            }
            f2 *= this.getScript().getPlayerDamageProtection();
            f2 = (float)((double)f2 * 0.9);
            bodyPart.AddDamage(f2);
            if (f2 > 40.0f && Rand.Next(12) == 0) {
                bodyPart.generateDeepWound();
            } else if (f2 > 50.0f && Rand.Next(10) == 0 && SandboxOptions.instance.BoneFracture.getValue()) {
                if (bodyPart.getType() == BodyPartType.Neck || bodyPart.getType() == BodyPartType.Groin) {
                    bodyPart.generateDeepWound();
                } else {
                    bodyPart.setFractureTime(Rand.Next(Rand.Next(10.0f, f2 + 10.0f), Rand.Next(f2 + 20.0f, f2 + 30.0f)));
                }
            }

In the DoLand code, we saw that slow healers chose a random number between 30 and 50 to determine fracture time, while fast healers chose a random number between 80 and 150 to determine fracture time.  

Here, we see that we start with a random number between f-15 and f, and then we take the the maximum of that and 5.  (I'll talk about what f is later - it's complicated)

So that random number, f2, is multiplied by 0.8 if you are a fast healer and by 1.2 if you are a slow healer.

It gets modified a bit, and IF you took a lot of damage and it randomly determines there is a bad wound and the wound location isn't the neck or groin, it is used to determine fracture time.

So you can see that Fast/Slow Healer traits do affect fractures.

Extra:  Calculating Vehicle Fracture Time (f)

Ok, this is complicated.  I'm not taking the time to dive deep into this calculation.

  • f is one of the values passed in to addRandomDamageFromCrash
  • That value is one of the values passed in to damagePlayers
  • That value is set by one of the values passed in to crash
  • crash gets called by Damage, HitByVehicle, and update

I'm guessing that if you're driving and crash your car, we are looking at update, so here's how it works there:

            if (this.jniIsCollide) {
                this.jniIsCollide = false;
                Vector3f vector3f = (Vector3f)TL_vector3f_pool.get().alloc();
                if (GameServer.bServer) {
                    vector3f.set((Vector3fc)this.netLinearVelocity);
                } else {
                    vector3f.set((Vector3fc)this.jniLinearVelocity);
                }
                vector3f.negate();
                vector3f.add((Vector3fc)this.lastLinearVelocity);
                vector3f.y = 0.0f;
                float f6 = Math.abs(vector3f.length());
                if (f6 > 2.0f) {
                    if (this.lastLinearVelocity.length() < 6.0f) {
                        f6 /= 3.0f;
                    }
                    this.jniTransform.getRotation(this.tempQuat4f);
                    this.tempQuat4f.invert(this.tempQuat4f);
                    if (this.lastLinearVelocity.rotate((Quaternionfc)this.tempQuat4f).z < 0.0f) {
                        f6 *= -1.0f;
                    }
                    if (Core.bDebug) {
                        DebugLog.log("CRASH lastSpeed=" + this.lastLinearVelocity.length() + " speed=" + vector3f + " delta=" + f6 + " netLinearVelocity=" + this.netLinearVelocity.length());
                    }
                    Vector3f vector3f2 = this.getForwardVector((Vector3f)TL_vector3f_pool.get().alloc());
                    float f7 = vector3f.normalize().dot((Vector3fc)vector3f2);
                    TL_vector3f_pool.get().release(vector3f2);
                    this.crash(Math.abs(f6 * 3.0f), f7 > 0.0f);

I'm doing some guesswork here!  f6 * 3.0 is what gets passed to crash.  f6 is a "delta", so the absolute value of the speed vector (so, just the magnitude part of the vector, not the direction).

So it looks like the fracture time is directly related to the speed when you crash.  Modified by things like Fast/Slow healer.

And fractures from being hit by a car (like in multiplayer) are handled differently, but also use Fast/Slow healer to determine fracture time.


Thanks for reading!  If you see a mistake I made or have a question I can answer by looking at the code, let me know!

No comments:

Post a Comment

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