Skip to content

Commit

Permalink
Deal with randomly absent dragons and random surplus dragons.
Browse files Browse the repository at this point in the history
 * Testing reveals that sometimes the dragon isn't loaded and
   is then generated when a player logs in to the end.
 * Only advance stages if we detect a dragon spawn in stage 0.
 * Extra logging about state inferences.
  • Loading branch information
totemo committed May 5, 2020
1 parent 8f8777e commit 4035887
Showing 1 changed file with 55 additions and 3 deletions.
58 changes: 55 additions & 3 deletions src/main/java/nu/nerd/df/FightState.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nu.nerd.df;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
Expand Down Expand Up @@ -410,6 +411,7 @@ protected void cleanUp(CommandSender sender) {
*/
protected void discoverFightState() {
World fightWorld = DragonUtil.getFightWorld();
DragonBattle battle = fightWorld.getEnderDragonBattle();

// Preload chunks to ensure we find the crystals.
int chunkRange = (int) Math.ceil(TRACKED_RADIUS / 16);
Expand Down Expand Up @@ -439,9 +441,23 @@ protected void discoverFightState() {
}
log("Discovered bosses: " + _bosses.size());

if (battle.getEnderDragon() != null) {
log("Dragon " + battle.getEnderDragon().getUniqueId() + " exists.");
} else {
log("No dragon exists.");
}

// Work out what stage we're in.
DragonBattle battle = fightWorld.getEnderDragonBattle();
_stageNumber = (battle.getEnderDragon() == null) ? 0 : 10 - _crystals.size();
// 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.");
_stageNumber = 0;
} else {
// We hope that vanilla code respawns the dragon at some point.
_stageNumber = 10 - _crystals.size();
log("Intial guess stage " + _stageNumber);
}

// A restart during the stage start spawn sequence can leave us
// without bosses.
Expand Down Expand Up @@ -1038,6 +1054,11 @@ protected void onPillarCrystalSpawn(EnderCrystal crystal) {
* crystals still exist and the RespawnPhase is NONE.
*/
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);

// debug("Dragon spawned. Spawning crystals: " +
// getDragonSpawnCrystals());
// debug("Respawn phase: " +
Expand All @@ -1059,7 +1080,12 @@ protected void onDragonSpawn(EnderDragon dragon) {
() -> crystal.setInvulnerable(true), 1);
}
reconfigureDragonBossBar();
nextStage();

// 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();
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -1480,6 +1506,32 @@ public double getTotalBossHealth() {
return _bosses.stream().reduce(0.0, (sum, b) -> sum + b.getHealth(), (h1, h2) -> h1 + h2);
}

// ------------------------------------------------------------------------
/**
* Vanilla code can randomly spawn extra dragons.
*
* Try to adapt. Not clear on when the DragonBattle has its EnderDragon
* reference set, so let's keep at least one, even if not referenced by the
* battle.
*/
protected void removeSurplusDragons() {
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);
}
}

// ------------------------------------------------------------------------
/**
* A repeating task that tracks boss fight participants to:
Expand Down

0 comments on commit 4035887

Please sign in to comment.