package gg.malloc.defense; import gg.malloc.defense.model.Game; import gg.malloc.defense.model.Arena; import gg.malloc.defense.model.Spawnpoint; import gg.malloc.defense.model.Spawner; import gg.malloc.defense.model.Wave; import java.util.ArrayList; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.scheduler.BukkitTask; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.boss.BarColor; import org.bukkit.boss.BarStyle; import org.bukkit.boss.BossBar; import org.bukkit.GameMode; import org.bukkit.attribute.Attribute; public class GameRunner { ArrayList m_spawnedMobs = new ArrayList(); int m_killedMobs = 0; Arena m_arena; Game m_game; State m_state; ArrayList m_players = new ArrayList<>(); ArrayList m_livingPlayers = new ArrayList<>(); Plugin m_plugin; BossBar m_gameBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID); BukkitTask m_countdownTask; int m_currentWaveNum = 0; int m_currentBatch = 0; Wave m_currentWave = null; enum State { Idle, Warmup, Playing, GameOver } public GameRunner(Plugin plugin, Game game, Arena arena) { m_plugin = plugin; m_game = game; m_arena = arena; m_state = State.Idle; m_gameBar.setVisible(true); } int m_warmupCountdown = 0; private void countdownTick() { if (m_warmupCountdown == 0) { requestTransition(State.Playing); } else { m_gameBar.setProgress((double)m_warmupCountdown / (double)30); broadcastMessage("Starting game in " + m_warmupCountdown); m_warmupCountdown--; m_countdownTask = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> { countdownTick(); }, 20); } } private void clearMobs() { for(Entity e : m_spawnedMobs) { e.remove(); } m_spawnedMobs.clear(); } private boolean enterIdle() { broadcastMessage("Game state: Idle"); m_currentWaveNum = 0; m_gameBar.setColor(BarColor.PURPLE); m_gameBar.setProgress(1.0); m_gameBar.setTitle("Idle"); if (m_countdownTask != null) { m_countdownTask.cancel(); m_countdownTask = null; } clearMobs(); return true; } private boolean enterWarmup() { broadcastMessage("Game state: Warmup"); m_currentWaveNum += 1; m_currentWave = m_game.getWave(m_currentWaveNum); m_gameBar.setColor(BarColor.YELLOW); m_gameBar.setProgress(1.0); m_gameBar.setTitle("Warmup"); m_warmupCountdown = 30; for(Player p : m_players) { m_livingPlayers.add(p); } clearMobs(); countdownTick(); return true; } private boolean enterPlaying() { broadcastMessage("Game state: Playing"); broadcastMessage("Starting wave " + m_currentWaveNum); m_currentBatch = 1; m_gameBar.setColor(BarColor.GREEN); m_gameBar.setProgress(0.0); m_gameBar.setTitle("Playing"); spawnNextBatch(); return true; } private boolean enterGameOver() { broadcastMessage("Game state: Game Over!"); m_gameBar.setColor(BarColor.RED); m_gameBar.setProgress(1.0); m_gameBar.setTitle("Game Over!"); if (m_countdownTask != null) { m_countdownTask.cancel(); m_countdownTask = null; } for(Player p : m_players) { p.setGameMode(GameMode.ADVENTURE); } clearMobs(); return true; } private void updateMobBar() { m_gameBar.setTitle("Mobs remaining: " + (m_currentWave.totalMobCount() - m_killedMobs)); m_gameBar.setProgress(m_killedMobs / m_currentWave.totalMobCount()); } private void spawnNextBatch() { broadcastMessage("Spawning batch " + m_currentBatch); Spawner spawner = new GameSpawner(m_arena.spawnpoints()[0]); m_currentWave.spawnBatch(spawner, m_currentBatch); updateMobBar(); } public void handlePlayerDeath(Player player) { if (m_livingPlayers.contains(player)) { m_livingPlayers.remove(player); player.setGameMode(GameMode.SPECTATOR); if (m_livingPlayers.size() == 0) { broadcastMessage("Everyone is dead :("); requestTransition(State.GameOver); } } } public void handleEntityDeath(Entity entity) { if (m_spawnedMobs.contains(entity)) { broadcastMessage("Killed game entity " + entity); m_spawnedMobs.remove(entity); m_killedMobs += 1; updateMobBar(); if (m_spawnedMobs.size() == 0) { broadcastMessage("Batch complete!"); if (m_currentBatch >= m_currentWave.batchCount()) { if (m_currentWaveNum >= m_game.getWaveCount()) { requestTransition(State.GameOver); } else { requestTransition(State.Warmup); } } else { m_currentBatch += 1; spawnNextBatch(); } } else { broadcastMessage("Entities remaining: " + m_spawnedMobs.size()); } } } private boolean syncPlayer(Player player) { World playerWorld = player.getLocation().getWorld(); World gameWorld = m_arena.getWorld(); m_gameBar.addPlayer(player); if (m_livingPlayers.contains(player)) { player.setGameMode(GameMode.ADVENTURE); player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()); } else { player.setGameMode(GameMode.SPECTATOR); } if (playerWorld != gameWorld) { return player.teleport(gameWorld.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); } else { return false; } } private boolean attemptTransition(State state) { if (m_state == state) { return false; } switch(state) { case Idle: return enterIdle(); case Warmup: return enterWarmup(); case Playing: return enterPlaying(); case GameOver: return enterGameOver(); } return false; } private boolean validateTransition(State from, State to) { switch(from) { case Idle: return to == State.Warmup; case Warmup: return to == State.Playing || to == State.Idle || to == State.GameOver; case Playing: return to == State.Warmup || to == State.GameOver || to == State.Idle; case GameOver: return to == State.Idle; } return false; } public boolean requestTransition(State state) { if (!validateTransition(m_state, state)) { return false; } if (attemptTransition(state)) { m_state = state; for(Player p : m_players) { syncPlayer(p); } return true; } return false; } public boolean addPlayer(Player p) { if (m_state == State.Idle || m_state == State.Warmup) { m_players.add(p); broadcastMessage("Added player " + p + " to game"); syncPlayer(p); if (m_state == State.Idle) { requestTransition(State.Warmup); } return true; } else { return false; } } public void removePlayer(Player p) { m_gameBar.removePlayer(p); m_players.remove(p); m_livingPlayers.remove(p); if (m_players.size() == 0) { requestTransition(State.Idle); } } void broadcastMessage(String string) { World world = m_arena.getWorld(); for(Player p : world.getPlayers()) { p.sendMessage(string); } } void registerSpawnedMob(Entity entity) { m_spawnedMobs.add(entity); } private class GameSpawner implements Spawner { Spawnpoint m_spawnpoint; public GameSpawner(Spawnpoint spawnpoint) { m_spawnpoint = spawnpoint; } @Override public Entity spawnMob(EntityType type) { Entity newMob = m_arena.getWorld().spawnEntity(m_spawnpoint.getLocation(), type); LivingEntity livingMob = (LivingEntity)newMob; livingMob.setRemoveWhenFarAway(false); registerSpawnedMob(newMob); return newMob; } } }