Refactor obsidian-bomb explosions.

This commit changes the "source" of the explosions from `obsidian-bomb`
to _something_ that isn't the boss itself. Originally, these explosions
were block explosions, but without a "source" from the damage caused to
other mobs, the `monster-infight` flag was largely ignored. By switching
to "entity explosions" with the boss as the "source", the flag was now
respected, and mobs would no longer take damage from these explosions if
the `monster-infight` flag was set to `false`.

However, the handler for "entity explosions" now started reacting to the
explosions caused by the `obsidian-bomb` ability as if they were normal
entity explosions, e.g. those caused by creepers, and because entities
that explode usually "die" (although without an EntityDeathEvent), the
handler removes the entity from the monster manager, which effectively
"unregisters" the boss, leaving the entity itself just hanging around in
the arena as a normal mob (with a huge health pool).

The "obvious" solution is to change back to "block explosions", and that
could perhaps have worked if the API had consistent interplay between an
"explode event" and the resulting "damage event". It turns out that the
EntityExplodeEvent results in an EntityDamageByEntityEvent, and the same
can be said for BlockExplodeEvent and EntityDamageByBlockEvent, except
the latter will have a `null` block in it, so even if we were to attach
some metadata to the block, we wouldn't be able to retrieve it in the
damage handler. So we're sticking with "entity explosions"...

The hacky solution: We just spawn a new entity and pin the explosion on
it instead of the boss. That way the new entity is removed by the entity
explosion handler (which is a no-op, because the entity isn't an arena
monster) instead of the boss. It technically doesn't matter which entity
we spawn, but by spawning a primed TNT block, we can naturally stick a
source on it, and the damage handler will treat it as if the boss placed
a TNT block and detonated it, which is an apt analogy after all. Just to
make sure we don't forget the dual responsibility, we've added a little
comment in the damage handler.

An alternative solution would have been to spawn any other entity and
stick the boss entity ID on it, then look up the entity by that ID in
the damage handler if the damager had metadata on it. This is just a bit
more streamlined and likely a lot more performant.

Fixes #789
This commit is contained in:
Andreas Troelsen 2024-07-23 01:00:43 +02:00
parent a23be39663
commit c5802faefc
3 changed files with 11 additions and 2 deletions

View File

@ -18,6 +18,7 @@ These changes will (most likely) be included in the next version.
### Fixed
- MobArena no longer throws errors when handling block explosions on Minecraft 1.21.
- The `shuffle-positions` ability now correctly shuffles the position of the boss as well if `monster-teleport` is set to `false`.
- The `obsidian-bomb` ability no longer breaks boss waves.
## [0.108] - 2024-01-01
### Added

View File

@ -687,6 +687,7 @@ public class ArenaListener
}
}
// This also covers the Obsidian Bomb ability!
if (damager instanceof TNTPrimed) {
damager = ((TNTPrimed) damager).getSource();
}

View File

@ -12,6 +12,7 @@ import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.TNTPrimed;
@AbilityInfo(
name = "Obsidian Bomb",
@ -54,8 +55,14 @@ public class ObsidianBomb implements Ability
if (!arena.isRunning())
return;
world.getBlockAt(loc).setType(Material.AIR);
world.createExplosion(loc, 3F, false, true, boss.getEntity());
TNTPrimed scapegoat = world.spawn(loc, TNTPrimed.class);
scapegoat.setSource(boss.getEntity());
try {
world.getBlockAt(loc).setType(Material.AIR);
world.createExplosion(loc, 3F, false, true, scapegoat);
} finally {
scapegoat.remove();
}
}
}, FUSE);
}