mojira.dev

etch

Assigned

No issues.

Reported

No issues.

Comments

This bug effects also affects other explosions in the game, such as TNT explosions. It doesn't affect bed and respawn anchor explosions (because their damage source is specified as a BadRespawnPointDamageSource with scaleWithDifficulty = true) and creeper explosions (because it dies and the causingEntity is null).

Code Analysis (yarn mappings):

Scaling damage with difficulty is done in the damage method of the PlayerEntity class:

public boolean damage(DamageSource source, float amount) {
    ...
    if (source.isScaledWithDifficulty()) {
        if (this.world.getDifficulty() == Difficulty.PEACEFUL) {
            amount = 0.0f;
        }
        if (this.world.getDifficulty() == Difficulty.EASY) {
            amount = Math.min(amount / 2.0f + 1.0f, amount);
        }
        if (this.world.getDifficulty() == Difficulty.HARD) {
            amount = amount * 3.0f / 2.0f;
        }
    }
    if (amount == 0.0f) {
        return false;
    }
    return super.damage(source, amount);
}

Whether the damage is scaled is determined on if the scaleWithDifficulty field of source is true. In the damage method of the EndCrystalEntity class, these two lines handle the explosion:

DamageSource damageSource = source.getAttacker() != null ? DamageSource.explosion(this, source.getAttacker()) : null;
this.world.createExplosion(this, damageSource, null, this.getX(), this.getY(), this.getZ(), 6.0f, false, World.ExplosionSourceType.BLOCK);

In the case of TNT lit by a player, it explodes without specifying the damage source, instead having a getCausingEntity method that is called in Explosion.getCausingEntity which is called in DamageSource.explosion which is called in the Explosion constructor called in World.createExplosion.

In both these cases, DamageSource.explosion is called:

public static DamageSource explosion(@Nullable Explosion explosion) {
    return explosion != null ? DamageSource.explosion(explosion.getEntity(), explosion.getCausingEntity()) : DamageSource.explosion(null, null);
}

public static DamageSource explosion(@Nullable Entity explosion, @Nullable Entity attacker) {
    if (attacker != null && explosion != null) {
        return new ProjectileDamageSource("explosion.player", explosion, attacker).setScaledWithDifficulty().setExplosive();
    }
    if (explosion != null) {
        return new EntityDamageSource("explosion", explosion).setScaledWithDifficulty().setExplosive();
    }
    return new DamageSource("explosion").setScaledWithDifficulty().setExplosive();
}

Because both attacker and explosion are not null, a ProjectileDamageSource is created which extends EntityDamageSource which overrides the isScaledWithDifficulty method in DamageSource.
Instead of

public boolean isScaledWithDifficulty() {
    return this.scaleWithDifficulty;
}

it's

public boolean isScaledWithDifficulty() {
    return this.source instanceof LivingEntity && !(this.source instanceof PlayerEntity);
}

This is done to have difficulty scaling apply only to mobs and not players. Since a player lit the TNT and a player hit the end crystal, this will be false, so the damage of the explosion will not be scaled.

Proposed Fix:

Change EntityDamageSource.isScaledWithDifficulty to

public boolean isScaledWithDifficulty() {
    return this.scaleWithDifficulty || (this.source instanceof LivingEntity && !(this.source instanceof PlayerEntity));
}

This shouldn't break anything to my knowledge.

Edit:

Because of MC-258561 and MC-157206 it's probably better to create a separate damage source for explosions instead of using the one for projectiles.