Skip to content

Commit

Permalink
Better handling of new dragon spawns.
Browse files Browse the repository at this point in the history
 * Try to preserve the dragon associated with the DragonBattle
   when removing surplus dragons.
 * Wait one more tick after a dragon spawns before removing
   surplus dragons (fingers crossed).
 * More logging of surplus dragon removal.
 * Jump to stage 11 when stage 0 is inferred but a dragon spawns (and
   we're actually in stage 11).
 * Prevent NPE in `/df info` when the DragonBattle has spontaneously
   forgotten about the dragon.
  • Loading branch information
totemo committed May 7, 2020
1 parent 54e0a5e commit 184caf8
Showing 1 changed file with 53 additions and 24 deletions.
77 changes: 53 additions & 24 deletions src/main/java/nu/nerd/df/FightState.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,14 @@ public void cmdInfo(CommandSender sender) {

DragonBattle battle = DragonUtil.getFightWorld().getEnderDragonBattle();
EnderDragon dragon = battle.getEnderDragon();
sender.sendMessage(ChatColor.DARK_PURPLE + "The current dragon health is " +
ChatColor.LIGHT_PURPLE + dragon.getHealth() +
ChatColor.DARK_PURPLE + " out of " +
ChatColor.LIGHT_PURPLE + dragon.getMaxHealth() + ".");
if (dragon == null) {
sender.sendMessage(ChatColor.DARK_PURPLE + "The current dragon health is " +
ChatColor.LIGHT_PURPLE + dragon.getHealth() +
ChatColor.DARK_PURPLE + " out of " +
ChatColor.LIGHT_PURPLE + dragon.getMaxHealth() + ".");
} else {
sender.sendMessage(ChatColor.DARK_PURPLE + "There is no dragon associated with the battle. :/");
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -451,7 +455,7 @@ protected void discoverFightState() {
// The dragon can randomly despawn. Make a best guess.
// Eventually will rewrite to load from config.
if (battle.getEnderDragon() == null && _bosses.isEmpty() && _crystals.isEmpty()) {
log("No dragon, bosses or pillar cyrstals. Guess stage 0.");
log("No dragon, bosses or pillar crystals. Guess stage 0.");
_stageNumber = 0;
} else {
// We hope that vanilla code respawns the dragon at some point.
Expand Down Expand Up @@ -1057,23 +1061,22 @@ protected void onDragonSpawn(EnderDragon dragon) {
log("Dragon " + dragon.getUniqueId() + " spawned.");

// Remove surplus dragons after this one is added to the world.
Bukkit.getScheduler().runTaskLater(DragonFight.PLUGIN, () -> removeSurplusDragons(), 1);
Bukkit.getScheduler().runTaskLater(DragonFight.PLUGIN, () -> removeSurplusDragons(), 2);

// debug("Dragon spawned. Spawning crystals: " +
// getDragonSpawnCrystals());
// debug("Respawn phase: " +
if (_crystals.size() == 0) {
// Observed once in testing. Don't set invulnerable.
log("Dragon spawned but there were no ender crystals?!");
return;
} else {
// The dragon is invulnerable for stages 1 through 10.
// NOTE: creative mode trumps invulnerability.
dragon.setInvulnerable(true);
}

// Setting the crystals invulnerable before the dragon spawns does not
// work. But Minecraft prevents them from being damaged.
// work. But Minecraft prevents them from being da maged.
// Cannot set them invulnerable this tick either.
for (EnderCrystal crystal : _crystals) {
Bukkit.getScheduler().runTaskLater(DragonFight.PLUGIN,
Expand All @@ -1082,9 +1085,21 @@ protected void onDragonSpawn(EnderDragon dragon) {
reconfigureDragonBossBar();

// Since extra dragons can spawn randomly mid-fight, we should only
// advance to the next stage at the start of the fight.
if (_stageNumber == 0) {
nextStage();
// advance to the next stage if there are no bosses.
if (_stageNumber == 0 && _bosses.isEmpty()) {
log("Stage 0, no bosses, new dragon.");
// We may have previously inferred the stage to be 0 based on an
// absence of the dragon, crystals and bosses. if there are 10
// crystals and no bosses, we're going from stage 0 to stage 1.
if (_crystals.size() == 10) {
log("All 10 crystals, no bosses, new dragon. Go go to stage 1.");
nextStage();
} else if (_crystals.size() == 0) {
log("No crystals, no bosses, new dragon. Must be in stage 11.");
startStage11();
}
} else {
log("Stay in this stage. Bosses: " + _bosses.size() + ", Crystals: " + _crystals.size());
}
}

Expand Down Expand Up @@ -1378,7 +1393,10 @@ protected void startStage11() {

// The dragon was set invulnerable in stage 1.
DragonBattle battle = DragonUtil.getFightWorld().getEnderDragonBattle();
battle.getEnderDragon().setInvulnerable(false);
EnderDragon dragon = battle.getEnderDragon();
if (dragon != null) {
dragon.setInvulnerable(false);
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -1515,20 +1533,31 @@ public double getTotalBossHealth() {
* battle.
*/
protected void removeSurplusDragons() {
log("Removing surplus dragons.");
World fightWorld = DragonUtil.getFightWorld();
Collection<EnderDragon> dragons = fightWorld.getEntitiesByClass(EnderDragon.class);

// Assume vanilla intends the newest dragon instance to replace
// whatever others exist. Sort into ascending order by time existed.
List<EnderDragon> dragonsByAge = dragons.stream()
.sorted((d1, d2) -> d1.getTicksLived() - d2.getTicksLived())
.collect(Collectors.toCollection(ArrayList::new));

// Remove every dragon after the youngest.
for (int i = 1; i < dragonsByAge.size(); ++i) {
EnderDragon dragon = dragonsByAge.get(i);
log("Remove surplus dragon: " + dragon.getUniqueId());
DragonUtil.removeDragon(dragon);
log("There are currently: " + dragons.size() + " dragons.");
DragonBattle battle = fightWorld.getEnderDragonBattle();
if (battle.getEnderDragon() == null) {
// Assume vanilla intends the newest dragon instance to replace
// whatever others exist. Sort into ascending order by time existed.
List<EnderDragon> dragonsByAge = dragons.stream()
.sorted((d1, d2) -> d1.getTicksLived() - d2.getTicksLived())
.collect(Collectors.toCollection(ArrayList::new));

// Remove every dragon after the youngest.
for (int i = 1; i < dragonsByAge.size(); ++i) {
EnderDragon dragon = dragonsByAge.get(i);
DragonUtil.removeDragon(dragon);
}
} else {
// Let the dragon battle keep the ender dragon it manages, remove
// the others.
for (EnderDragon dragon : dragons) {
if (dragon != battle.getEnderDragon()) {
DragonUtil.removeDragon(dragon);
}
}
}
}

Expand Down

0 comments on commit 184caf8

Please sign in to comment.