Fix solo-play win condition, floor-spawn clipping, and make round-end block removal instantaneous
Build / build (push) Successful in 1m17s

This commit is contained in:
Michael Burgess
2026-08-07 08:09:45 -04:00
parent c78d35d104
commit 07d210d8db
3 changed files with 57 additions and 31 deletions
@@ -49,6 +49,10 @@ public class Arena {
private int countdownRemaining;
private int round = 0;
/** True when the match started with exactly one player (solo/testing play): in that case
* the match doesn't end just because "one player remains" — it keeps running rounds until
* that player is actually eliminated or leaves. */
private boolean soloMode;
private Material currentTarget;
private Material previousTarget;
private int currentRoundTime;
@@ -248,19 +252,21 @@ public class Arena {
return;
}
round = 0;
soloMode = players.size() == 1;
previousTarget = null;
if (!floorManager.hasLayout()) {
floorManager.generate(random);
}
floorManager.restoreFull(plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> {
Location safeSpawn = safeSpawnLocation();
for (UUID uuid : players) {
Player p = plugin.getServer().getPlayer(uuid);
if (p == null) {
continue;
}
p.setGameMode(GameMode.ADVENTURE);
if (config.getSpawn() != null) {
p.teleport(config.getSpawn());
if (safeSpawn != null) {
p.teleport(safeSpawn);
}
}
startNextRound();
@@ -272,10 +278,12 @@ public class Arena {
endMatch(null);
return;
}
// Only treat "down to one player" as a win once at least one round has actually been
// played (round > 0). At round 0 (the very first round after the countdown), a single
// player is expected whenever min-players is configured as 1 for solo play/testing.
if (round > 0 && EliminationLogic.isMatchOver(players.size())) {
// In solo mode the match only ends when the lone player is actually eliminated (see
// evaluateEliminations) or leaves (players.isEmpty() above) — "one player remaining"
// is the expected steady state, not a win condition, so round count keeps climbing.
// In normal multiplayer, only treat "down to one player" as a win once at least one
// round has actually been played (round > 0); at round 0 that's just the initial join.
if (!soloMode && round > 0 && EliminationLogic.isMatchOver(players.size())) {
endMatch(players.get(0));
return;
}
@@ -379,8 +387,12 @@ public class Arena {
}
}
if (EliminationLogic.isMatchOver(players.size())) {
UUID winner = players.isEmpty() ? null : players.get(0);
// In solo mode, "1 player left" is the normal steady state — only end when that
// player is actually eliminated (players empty). In multiplayer, end as soon as at
// most one player remains.
boolean over = soloMode ? players.isEmpty() : EliminationLogic.isMatchOver(players.size());
if (over) {
UUID winner = (soloMode || players.isEmpty()) ? null : players.get(0);
if (bossBar != null) {
for (UUID uuid : spectators) {
Player p = plugin.getServer().getPlayer(uuid);
@@ -536,6 +548,23 @@ public class Arena {
spectators.clear();
}
/** The configured spawn, lifted above the floor's top block layer if it would otherwise
* place the player inside/underneath the freshly (re)generated floor. */
private Location safeSpawnLocation() {
Location spawn = config.getSpawn();
if (spawn == null) {
return null;
}
if (config.hasFloorRegion()) {
int floorTopY = Math.max(config.getPos1()[1], config.getPos2()[1]);
if (spawn.getBlockY() <= floorTopY) {
spawn = spawn.clone();
spawn.setY(floorTopY + 1);
}
}
return spawn;
}
public BillboardManager getBillboardManager() {
return billboardManager;
}