Implement /ready and basic objective mechanics
This commit is contained in:
		
							
								
								
									
										86
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								TODO.md
									
									
									
									
									
								
							| @@ -1,30 +1,78 @@ | |||||||
| # TODO | # Objective | ||||||
|  |  | ||||||
|  | [X] Mobs spawn in waves | ||||||
|  | [X] Mobs move towards goal | ||||||
|  | [ ] Mobs carry bomb to goal | ||||||
|  | [X] Mobs arm bomb | ||||||
|  | [X] Bomb explodes | ||||||
|  |  | ||||||
|  | # UX | ||||||
|  |  | ||||||
|  | [X] Wave boss bar | ||||||
|  | [X] Mob count boss bar | ||||||
|  | [X] Stage titles | ||||||
|  | [ ] EXPLOSIONS | ||||||
|  | [ ] Colored titles | ||||||
|  | [ ] Clickable /ready in chat | ||||||
|  | [ ] Sidebar | ||||||
|  |  | ||||||
|  | # Social | ||||||
|  |  | ||||||
|  | [ ] Automatic VC groups for games | ||||||
|  | [ ] DiscordSRV | ||||||
|  | [ ] Plan stats | ||||||
|  | [ ] /invite friends to games | ||||||
|  | [ ] /voterestart | ||||||
|  |  | ||||||
|  | # Mechanics | ||||||
|  |  | ||||||
| [ ] Coin drops | [ ] Coin drops | ||||||
| [ ] Basic setup commands | [ ] Mob tracking should prioritize bomb | ||||||
| [ ] Config file | [ ] Mobs recover dropped bombs | ||||||
| [ ] Spectator mode | [ ] Bomb carriers are slower | ||||||
| [ ] Commands/tools to start warmup countdown/mark 'ready' | [ ] Bonus coins for complete coin pickup | ||||||
| [ ] Upgrades | [ ] Infinite weapons + armor | ||||||
| [ ] Plan stats | [ ] Ammo/health spawns | ||||||
| [ ] Live map editing |  | ||||||
| [ ] Instancing |  | ||||||
| [ ] DiscordSRV |  | ||||||
| [ ] Voice Chat |  | ||||||
| [ ] Player readiness starts countdown |  | ||||||
|  |  | ||||||
| # Scripted waves | # Scripted waves | ||||||
|  |  | ||||||
|  | [X] Spawn in batches | ||||||
|  | [X] Randomized spawn locations | ||||||
|  | [X] Weighted distributions | ||||||
|  | [X] Batch overlap | ||||||
|  | [ ] Scripted batch overlap/timings | ||||||
|  | [ ] Scripted spawn locations | ||||||
| [ ] Bosses | [ ] Bosses | ||||||
| [ ] Batch overlap/timing |  | ||||||
|  | # Mapping | ||||||
|  |  | ||||||
|  | [ ] Load arenas from config file | ||||||
|  | [ ] Live map editing | ||||||
|  |  | ||||||
| # Game lifecycle | # Game lifecycle | ||||||
|  |  | ||||||
| [ ] Return to lobby | [ ] /list arenas and games | ||||||
| [ ] Join game via sign/commands/something | [ ] /start a game on an arena | ||||||
| [ ] Bonus coins for complete coin pickup | [ ] /join games | ||||||
|  | [X] /ready | ||||||
|  | [ ] /leave games | ||||||
|  | [ ] /restart games | ||||||
|  | [X] Spectator mode on death | ||||||
|  | [X] Player readiness starts countdown | ||||||
|  | [ ] Game is automatically closed some time after game over | ||||||
|  | [ ] Return to lobby on leave/close | ||||||
|  | [ ] Instancing | ||||||
|  |  | ||||||
| # Cleanup | # Powerups | ||||||
|  |  | ||||||
| [ ] Generic countdown mechanic | [ ] Coin pickup range | ||||||
| [ ]  | [ ] Coin boost | ||||||
|  | [ ] Knockback on weapons | ||||||
|  | [ ] Damage boost | ||||||
|  | [ ] Speed boost | ||||||
|  | [ ] Health boost | ||||||
|  | [ ] Repair barriers | ||||||
|  |  | ||||||
|  | # Fantasy | ||||||
|  |  | ||||||
|  | [ ] Totems/turrets/stationary weapons | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import org.bukkit.event.Listener; | |||||||
| import org.bukkit.event.entity.EntityDeathEvent; | import org.bukkit.event.entity.EntityDeathEvent; | ||||||
| import org.bukkit.event.entity.EntityDamageEvent; | import org.bukkit.event.entity.EntityDamageEvent; | ||||||
| import org.bukkit.event.entity.EntityCombustEvent; | import org.bukkit.event.entity.EntityCombustEvent; | ||||||
|  | import org.bukkit.event.entity.EntityTargetEvent; | ||||||
| import org.bukkit.event.player.PlayerQuitEvent; | import org.bukkit.event.player.PlayerQuitEvent; | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
|  |  | ||||||
| @@ -32,6 +33,11 @@ public class GameEventHandler implements Listener { | |||||||
|     evt.setCancelled(true); |     evt.setCancelled(true); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   @EventHandler | ||||||
|  |   public void onEntityTarget(EntityTargetEvent evt) { | ||||||
|  |     m_runner.handleEntityRetargeting(evt); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @EventHandler |   @EventHandler | ||||||
|   public void onEntityDamage(EntityDamageEvent evt) { |   public void onEntityDamage(EntityDamageEvent evt) { | ||||||
|     if (evt.getEntity() instanceof Player) { |     if (evt.getEntity() instanceof Player) { | ||||||
|   | |||||||
| @@ -8,13 +8,15 @@ import org.bukkit.World; | |||||||
| public class MemoryArena implements Arena { | public class MemoryArena implements Arena { | ||||||
|  |  | ||||||
|   Spawnpoint[] m_spawnpoints; |   Spawnpoint[] m_spawnpoints; | ||||||
|  |   Spawnpoint m_bombTarget; | ||||||
|   World m_world; |   World m_world; | ||||||
|   String m_name; |   String m_name; | ||||||
|  |  | ||||||
|   public MemoryArena(String name, World world, Spawnpoint[] spawnpoints) { |   public MemoryArena(String name, World world, Spawnpoint[] spawnpoints, Spawnpoint bombTarget) { | ||||||
|     m_world = world; |     m_world = world; | ||||||
|     m_spawnpoints = spawnpoints; |     m_spawnpoints = spawnpoints; | ||||||
|     m_name = name; |     m_name = name; | ||||||
|  |     m_bombTarget = bombTarget; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
| @@ -31,4 +33,9 @@ public class MemoryArena implements Arena { | |||||||
|   public World getWorld() { |   public World getWorld() { | ||||||
|     return m_world; |     return m_world; | ||||||
|   } |   } | ||||||
|  |    | ||||||
|  |   @Override | ||||||
|  |   public Spawnpoint bombTarget() { | ||||||
|  |     return m_bombTarget; | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import org.bukkit.Location; | |||||||
| import org.bukkit.entity.Entity; | import org.bukkit.entity.Entity; | ||||||
| import org.bukkit.entity.EntityType; | import org.bukkit.entity.EntityType; | ||||||
| import org.bukkit.World; | import org.bukkit.World; | ||||||
|  | import org.bukkit.WorldCreator; | ||||||
|  |  | ||||||
| import java.util.logging.Logger; | import java.util.logging.Logger; | ||||||
| import java.util.logging.Level; | import java.util.logging.Level; | ||||||
| @@ -24,6 +25,7 @@ import gg.malloc.defense.engine.GameRunner; | |||||||
|  |  | ||||||
| import gg.malloc.defense.commands.AddPlayerCommand; | import gg.malloc.defense.commands.AddPlayerCommand; | ||||||
| import gg.malloc.defense.commands.SetStageCommand; | import gg.malloc.defense.commands.SetStageCommand; | ||||||
|  | import gg.malloc.defense.commands.PlayerReadyCommand; | ||||||
|  |  | ||||||
| public class Plugin extends JavaPlugin { | public class Plugin extends JavaPlugin { | ||||||
|   ArrayList<Arena> m_arenas = new ArrayList<>(); |   ArrayList<Arena> m_arenas = new ArrayList<>(); | ||||||
| @@ -65,6 +67,7 @@ public class Plugin extends JavaPlugin { | |||||||
|     getCommand("setstage").setExecutor(new SetStageCommand(this)); |     getCommand("setstage").setExecutor(new SetStageCommand(this)); | ||||||
|     getCommand("addplayer").setExecutor(new AddPlayerCommand(this)); |     getCommand("addplayer").setExecutor(new AddPlayerCommand(this)); | ||||||
|     getCommand("debuginfo").setExecutor(new DebuginfoCommand(this)); |     getCommand("debuginfo").setExecutor(new DebuginfoCommand(this)); | ||||||
|  |     getCommand("ready").setExecutor(new PlayerReadyCommand(this)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public GameRunner getRunnerForWorld(World world) { |   public GameRunner getRunnerForWorld(World world) { | ||||||
| @@ -81,14 +84,16 @@ public class Plugin extends JavaPlugin { | |||||||
|  |  | ||||||
|   void setupDemoGame() { |   void setupDemoGame() { | ||||||
|     getLogger().info("Setting up demo data'"); |     getLogger().info("Setting up demo data'"); | ||||||
|     World testWorld = getServer().getWorld("world"); |     World testWorld = getServer().getWorld("quarry"); | ||||||
|     Spawnpoint[] spawnpoints = new Spawnpoint[4]; |     if (testWorld == null) { | ||||||
|     Location spawnCenter = testWorld.getSpawnLocation(); |       testWorld = new WorldCreator("quarry").generateStructures(false).createWorld(); | ||||||
|     for(int i = 0; i < 4; i++) { |  | ||||||
|       Location spawnLocation = spawnCenter.add(Math.random() * 3, 0, Math.random() * 3); |  | ||||||
|       spawnpoints[i] = new TestSpawn(spawnLocation); |  | ||||||
|     } |     } | ||||||
|     m_arenas.add(new MemoryArena("Test Arena", testWorld, spawnpoints)); |     Spawnpoint[] spawnpoints = new Spawnpoint[3]; | ||||||
|  |     spawnpoints[0] = new TestSpawn(new Location(testWorld, -15, 80, -46)); | ||||||
|  |     spawnpoints[1] = new TestSpawn(new Location(testWorld, -1, 80, -45)); | ||||||
|  |     spawnpoints[2] = new TestSpawn(new Location(testWorld, 12, 81, -42)); | ||||||
|  |     Spawnpoint bombTarget = new TestSpawn(new Location(testWorld, -20, 80, 31)); | ||||||
|  |     m_arenas.add(new MemoryArena("Test Arena", testWorld, spawnpoints, bombTarget)); | ||||||
|     m_games.add(new ScaledWaves()); |     m_games.add(new ScaledWaves()); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | package gg.malloc.defense.commands; | ||||||
|  |  | ||||||
|  | import org.bukkit.command.Command; | ||||||
|  | import org.bukkit.command.CommandExecutor; | ||||||
|  | import org.bukkit.command.CommandSender; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  |  | ||||||
|  | import gg.malloc.defense.engine.GameRunner; | ||||||
|  |  | ||||||
|  | import gg.malloc.defense.Plugin; | ||||||
|  |  | ||||||
|  | public class PlayerReadyCommand implements CommandExecutor { | ||||||
|  |   Plugin m_plugin; | ||||||
|  |  | ||||||
|  |   public PlayerReadyCommand(Plugin plugin) { | ||||||
|  |     m_plugin = plugin; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public boolean onCommand(CommandSender sender, Command command, String s, String[] args) { | ||||||
|  |     if (sender instanceof Player) { | ||||||
|  |       Player player = (Player)sender; | ||||||
|  |       GameRunner runner = m_plugin.getRunnerForWorld(player.getLocation().getWorld()); | ||||||
|  |       runner.addPlayer(player); | ||||||
|  |       runner.togglePlayerReady(player); | ||||||
|  |       return true; | ||||||
|  |     } else { | ||||||
|  |       sender.sendMessage("You must be a player to use this command."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -10,14 +10,18 @@ import gg.malloc.defense.Plugin; | |||||||
| import java.util.logging.Logger; | import java.util.logging.Logger; | ||||||
|  |  | ||||||
| import org.bukkit.Bukkit; | import org.bukkit.Bukkit; | ||||||
|  | import org.bukkit.Sound; | ||||||
| import org.bukkit.World; | import org.bukkit.World; | ||||||
| import org.bukkit.boss.BarColor; | import org.bukkit.boss.BarColor; | ||||||
| import org.bukkit.boss.BarStyle; | import org.bukkit.boss.BarStyle; | ||||||
| import org.bukkit.boss.BossBar; | import org.bukkit.boss.BossBar; | ||||||
| import org.bukkit.event.player.PlayerTeleportEvent; | import org.bukkit.event.player.PlayerTeleportEvent; | ||||||
|  | import org.bukkit.event.entity.EntityTargetEvent; | ||||||
| import org.bukkit.event.entity.EntityDamageEvent; | import org.bukkit.event.entity.EntityDamageEvent; | ||||||
|  | import org.bukkit.event.entity.EntityDamageByEntityEvent; | ||||||
| import org.bukkit.entity.Entity; | import org.bukkit.entity.Entity; | ||||||
| import org.bukkit.entity.LivingEntity; | import org.bukkit.entity.LivingEntity; | ||||||
|  | import org.bukkit.entity.ArmorStand; | ||||||
| import org.bukkit.entity.EntityType; | import org.bukkit.entity.EntityType; | ||||||
| import org.bukkit.entity.Player; | import org.bukkit.entity.Player; | ||||||
| import org.bukkit.scheduler.BukkitTask; | import org.bukkit.scheduler.BukkitTask; | ||||||
| @@ -30,17 +34,23 @@ public class GameRunner { | |||||||
|  |  | ||||||
|   BossBar m_gameBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID); |   BossBar m_gameBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID); | ||||||
|   BossBar m_waveBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID); |   BossBar m_waveBar = Bukkit.createBossBar("Malloc Defense", BarColor.PURPLE, BarStyle.SOLID); | ||||||
|  |   BossBar m_bombBar = Bukkit.createBossBar("Bomb HP", BarColor.RED, BarStyle.SOLID); | ||||||
|  |  | ||||||
|   BukkitTask m_countdownTask; |   BukkitTask m_countdownTask; | ||||||
|  |  | ||||||
|   MobManager m_mobs; |   MobManager m_mobs; | ||||||
|   WaveManager m_waves; |   WaveManager m_waves; | ||||||
|   PlayerManager m_players; |   PlayerManager m_players; | ||||||
|  |  | ||||||
|  |   LivingEntity m_bombTarget = null; | ||||||
|  |   int m_bombHP = 100; | ||||||
|  |  | ||||||
|   Logger m_log; |   Logger m_log; | ||||||
|  |  | ||||||
|   public enum Stage { |   public enum Stage { | ||||||
|     Idle, |     Idle, | ||||||
|     Warmup, |     Warmup, | ||||||
|  |     Countdown, | ||||||
|     Playing, |     Playing, | ||||||
|     GameOver |     GameOver | ||||||
|   } |   } | ||||||
| @@ -104,6 +114,7 @@ public class GameRunner { | |||||||
|     m_stage = Stage.Idle; |     m_stage = Stage.Idle; | ||||||
|     m_gameBar.setVisible(true); |     m_gameBar.setVisible(true); | ||||||
|     m_waveBar.setVisible(false); |     m_waveBar.setVisible(false); | ||||||
|  |     m_bombBar.setVisible(false); | ||||||
|     m_mobs = new MobManager(m_game); |     m_mobs = new MobManager(m_game); | ||||||
|     m_waves = new WaveManager(m_game); |     m_waves = new WaveManager(m_game); | ||||||
|     m_players = new PlayerManager(); |     m_players = new PlayerManager(); | ||||||
| @@ -112,8 +123,30 @@ public class GameRunner { | |||||||
|  |  | ||||||
|   int m_warmupCountdown = 0; |   int m_warmupCountdown = 0; | ||||||
|  |  | ||||||
|  |   public void handleEntityRetargeting(EntityTargetEvent evt) { | ||||||
|  |     if (m_mobs.contains(evt.getEntity()) && evt.getReason() == EntityTargetEvent.TargetReason.FORGOT_TARGET) { | ||||||
|  |       evt.setTarget(m_bombTarget); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public void handleEntityDamage(EntityDamageEvent evt) { |   public void handleEntityDamage(EntityDamageEvent evt) { | ||||||
|     m_mobs.handleEntityDamage(evt); |     if (evt.getEntity() == m_bombTarget && evt instanceof EntityDamageByEntityEvent) { | ||||||
|  |       EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt; | ||||||
|  |       if (m_mobs.contains(entityEvt.getDamager())) { | ||||||
|  |         entityEvt.getDamager().setGlowing(true); | ||||||
|  |         m_bombHP -= 1; | ||||||
|  |         broadcastMessage("The bomb has been struck!"); | ||||||
|  |         m_arena.getWorld().playSound(m_bombTarget.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.5f, 0.9f); | ||||||
|  |         updateMobBars(); | ||||||
|  |         if (m_bombHP <= 0) { | ||||||
|  |           m_arena.getWorld().strikeLightningEffect(m_bombTarget.getLocation()); | ||||||
|  |           requestTransition(Stage.GameOver); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       evt.setCancelled(true); | ||||||
|  |     } else { | ||||||
|  |       m_mobs.handleEntityDamage(evt); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void countdownTick() { |   private void countdownTick() { | ||||||
| @@ -130,9 +163,14 @@ public class GameRunner { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private boolean enterIdle() { |   private boolean enterIdle() { | ||||||
|  |     m_bombHP = 100; | ||||||
|     m_waves.reset(); |     m_waves.reset(); | ||||||
|     m_mobs.clear(); |     m_mobs.clear(); | ||||||
|     m_players.requestTransitionForAll(PlayerManager.State.Idle); |     m_players.requestTransitionForAll(PlayerManager.State.Idle); | ||||||
|  |     if (m_bombTarget != null) { | ||||||
|  |       m_bombTarget.remove(); | ||||||
|  |       m_bombTarget = null; | ||||||
|  |     } | ||||||
|     if (m_countdownTask != null) { |     if (m_countdownTask != null) { | ||||||
|       m_countdownTask.cancel(); |       m_countdownTask.cancel(); | ||||||
|       m_countdownTask = null; |       m_countdownTask = null; | ||||||
| @@ -142,25 +180,54 @@ public class GameRunner { | |||||||
|  |  | ||||||
|   private boolean enterWarmup() { |   private boolean enterWarmup() { | ||||||
|     m_waves.next(); |     m_waves.next(); | ||||||
|     m_warmupCountdown = 10; |  | ||||||
|     for(Player p : m_players.getPlayers()) { |     for(Player p : m_players.getPlayers()) { | ||||||
|         if (m_players.requestTransition(p, PlayerManager.State.Playing)) { |         if (m_players.requestTransition(p, PlayerManager.State.Playing)) { | ||||||
|           p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); |           p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); | ||||||
|         } |         } | ||||||
|  |         m_players.setReady(p, false); | ||||||
|     } |     } | ||||||
|     broadcastTitle("Warmup", "Prepare yourself for wave " + m_waves.currentWaveNum()); |     broadcastTitle("Warmup", "Prepare yourself for wave " + m_waves.currentWaveNum()); | ||||||
|     m_mobs.clear(); |     m_mobs.clear(); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void togglePlayerReady(Player p) { | ||||||
|  |     setPlayerReady(p, !m_players.isReady(p)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setPlayerReady(Player p, boolean isReady) { | ||||||
|  |     m_players.setReady(p, isReady); | ||||||
|  |     if (m_players.isEveryoneReady()) { | ||||||
|  |       requestTransition(Stage.Countdown); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private boolean enterCountdown() { | ||||||
|  |     m_warmupCountdown = 10; | ||||||
|     countdownTick(); |     countdownTick(); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private boolean enterPlaying() { |   private boolean enterPlaying() { | ||||||
|     m_log.info("Starting wave " + m_waves.currentWaveNum()); |     m_log.info("Starting wave " + m_waves.currentWaveNum()); | ||||||
|  |     if (m_bombTarget == null) { | ||||||
|  |       m_bombTarget = (LivingEntity)m_arena.getWorld().spawnEntity(m_arena.bombTarget().getLocation(), EntityType.ARMOR_STAND); | ||||||
|  |     } | ||||||
|  |     ArmorStand bombStand = (ArmorStand)m_bombTarget; | ||||||
|  |     bombStand.setCustomName("Bomb Target"); | ||||||
|  |     bombStand.setVisible(true); | ||||||
|  |     bombStand.setCustomNameVisible(true); | ||||||
|  |     bombStand.setGlowing(true); | ||||||
|  |     for(Player p : m_players.getPlayers()) { | ||||||
|  |         if (m_players.requestTransition(p, PlayerManager.State.Playing)) { | ||||||
|  |           p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     spawnNextBatch(); |     spawnNextBatch(); | ||||||
|     broadcastTitle("Wave " + m_waves.currentWaveNum()); |     broadcastTitle("Wave " + m_waves.currentWaveNum()); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |    | ||||||
|   private boolean enterGameOver() { |   private boolean enterGameOver() { | ||||||
|     broadcastTitle("Game Over!"); |     broadcastTitle("Game Over!"); | ||||||
|     if (m_countdownTask != null) { |     if (m_countdownTask != null) { | ||||||
| @@ -179,18 +246,30 @@ public class GameRunner { | |||||||
|     switch(m_stage) { |     switch(m_stage) { | ||||||
|       case Idle: |       case Idle: | ||||||
|         m_gameBar.setProgress(1.0); |         m_gameBar.setProgress(1.0); | ||||||
|         m_gameBar.setTitle("Waiting for playres..."); |         m_gameBar.setTitle("Waiting for players..."); | ||||||
|         m_gameBar.setColor(BarColor.PURPLE); |         m_gameBar.setColor(BarColor.PURPLE); | ||||||
|         m_waveBar.setVisible(false); |         m_waveBar.setVisible(false); | ||||||
|  |         m_bombBar.setVisible(false); | ||||||
|         break; |         break; | ||||||
|       case Warmup: |       case Warmup: | ||||||
|  |         m_gameBar.setProgress(m_waves.progress()); | ||||||
|  |         m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount()); | ||||||
|  |         m_gameBar.setColor(BarColor.PURPLE); | ||||||
|  |         m_waveBar.setVisible(true); | ||||||
|  |         m_waveBar.setColor(BarColor.BLUE); | ||||||
|  |         m_waveBar.setTitle("Warmup - Waiting for players to get ready..."); | ||||||
|  |         m_waveBar.setProgress(m_players.readyProgress()); | ||||||
|  |         m_bombBar.setVisible(false); | ||||||
|  |         break; | ||||||
|  |       case Countdown: | ||||||
|         m_gameBar.setProgress(m_waves.progress()); |         m_gameBar.setProgress(m_waves.progress()); | ||||||
|         m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount()); |         m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount()); | ||||||
|         m_gameBar.setColor(BarColor.PURPLE); |         m_gameBar.setColor(BarColor.PURPLE); | ||||||
|         m_waveBar.setVisible(true); |         m_waveBar.setVisible(true); | ||||||
|         m_waveBar.setColor(BarColor.YELLOW); |         m_waveBar.setColor(BarColor.YELLOW); | ||||||
|         m_waveBar.setTitle("Warmup"); |         m_waveBar.setTitle("Wave starting!"); | ||||||
|         m_waveBar.setProgress((double)m_warmupCountdown / (double)10); |         m_waveBar.setProgress((double)m_warmupCountdown / (double)10); | ||||||
|  |         m_bombBar.setVisible(false); | ||||||
|         break; |         break; | ||||||
|       case Playing: |       case Playing: | ||||||
|         m_gameBar.setProgress(m_waves.progress()); |         m_gameBar.setProgress(m_waves.progress()); | ||||||
| @@ -204,19 +283,22 @@ public class GameRunner { | |||||||
|         } else { |         } else { | ||||||
|           m_waveBar.setVisible(false); |           m_waveBar.setVisible(false); | ||||||
|         } |         } | ||||||
|  |         m_bombBar.setVisible(true); | ||||||
|  |         m_bombBar.setProgress((double)m_bombHP / (double)100); | ||||||
|         break; |         break; | ||||||
|       case GameOver: |       case GameOver: | ||||||
|         m_gameBar.setColor(BarColor.RED); |         m_gameBar.setColor(BarColor.RED); | ||||||
|         m_gameBar.setProgress(1.0); |         m_gameBar.setProgress(1.0); | ||||||
|         m_gameBar.setTitle("Game Over!"); |         m_gameBar.setTitle("Game Over!"); | ||||||
|         m_waveBar.setVisible(false); |         m_waveBar.setVisible(false); | ||||||
|  |         m_bombBar.setVisible(false); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void spawnNextBatch() { |   private void spawnNextBatch() { | ||||||
|     broadcastMessage("Spawning batch " + m_waves.currentBatchNum()); |     broadcastMessage("Spawning batch " + m_waves.currentBatchNum()); | ||||||
|     Spawner spawner = new GameSpawner(m_arena, m_mobs); |     Spawner spawner = new GameSpawner(m_arena, m_mobs, m_players, m_bombTarget); | ||||||
|     m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum()); |     m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum()); | ||||||
|     updateMobBars(); |     updateMobBars(); | ||||||
|   } |   } | ||||||
| @@ -265,6 +347,8 @@ public class GameRunner { | |||||||
|         return enterIdle(); |         return enterIdle(); | ||||||
|       case Warmup: |       case Warmup: | ||||||
|         return enterWarmup(); |         return enterWarmup(); | ||||||
|  |       case Countdown: | ||||||
|  |         return enterCountdown(); | ||||||
|       case Playing: |       case Playing: | ||||||
|         return enterPlaying(); |         return enterPlaying(); | ||||||
|       case GameOver: |       case GameOver: | ||||||
| @@ -278,7 +362,9 @@ public class GameRunner { | |||||||
|       case Idle: |       case Idle: | ||||||
|         return to == Stage.Warmup; |         return to == Stage.Warmup; | ||||||
|       case Warmup: |       case Warmup: | ||||||
|         return to == Stage.Playing || to == Stage.Idle || to == Stage.GameOver; |         return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown ; | ||||||
|  |       case Countdown: | ||||||
|  |         return to == Stage.Playing || to == Stage.Idle || to == Stage.Warmup; | ||||||
|       case Playing: |       case Playing: | ||||||
|         return to == Stage.Warmup || to == Stage.GameOver || to == Stage.Idle; |         return to == Stage.Warmup || to == Stage.GameOver || to == Stage.Idle; | ||||||
|       case GameOver: |       case GameOver: | ||||||
| @@ -306,6 +392,7 @@ public class GameRunner { | |||||||
|     m_players.addPlayer(p); |     m_players.addPlayer(p); | ||||||
|     m_gameBar.addPlayer(p); |     m_gameBar.addPlayer(p); | ||||||
|     m_waveBar.addPlayer(p); |     m_waveBar.addPlayer(p); | ||||||
|  |     m_bombBar.addPlayer(p); | ||||||
|     if (m_stage == Stage.Idle || m_stage == Stage.Warmup) { |     if (m_stage == Stage.Idle || m_stage == Stage.Warmup) { | ||||||
|       if (m_players.requestTransition(p, PlayerManager.State.Playing)) { |       if (m_players.requestTransition(p, PlayerManager.State.Playing)) { | ||||||
|         p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); |         p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); | ||||||
| @@ -346,5 +433,4 @@ public class GameRunner { | |||||||
|   void registerSpawnedMob(Entity entity) { |   void registerSpawnedMob(Entity entity) { | ||||||
|     m_mobs.addEntity(entity); |     m_mobs.addEntity(entity); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,15 +7,20 @@ import gg.malloc.defense.model.Arena; | |||||||
| import org.bukkit.entity.Entity; | import org.bukkit.entity.Entity; | ||||||
| import org.bukkit.entity.LivingEntity; | import org.bukkit.entity.LivingEntity; | ||||||
| import org.bukkit.entity.EntityType; | import org.bukkit.entity.EntityType; | ||||||
|  | import org.bukkit.entity.Mob; | ||||||
|  |  | ||||||
| public class GameSpawner implements Spawner { | public class GameSpawner implements Spawner { | ||||||
|   Arena m_arena; |   Arena m_arena; | ||||||
|   MobManager m_manager; |   MobManager m_manager; | ||||||
|  |   PlayerManager m_players; | ||||||
|  |   LivingEntity m_bombTarget; | ||||||
|   int m_spawnIdx = 0; |   int m_spawnIdx = 0; | ||||||
|  |  | ||||||
|   public GameSpawner(Arena arena, MobManager manager) { |   public GameSpawner(Arena arena, MobManager manager, PlayerManager players, LivingEntity bombTarget) { | ||||||
|     m_arena = arena; |     m_arena = arena; | ||||||
|     m_manager = manager; |     m_manager = manager; | ||||||
|  |     m_players = players; | ||||||
|  |     m_bombTarget = bombTarget; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
| @@ -28,6 +33,7 @@ public class GameSpawner implements Spawner { | |||||||
|     livingMob.setRemoveWhenFarAway(false); |     livingMob.setRemoveWhenFarAway(false); | ||||||
|     m_manager.addEntity(newMob); |     m_manager.addEntity(newMob); | ||||||
|     m_spawnIdx += 1; |     m_spawnIdx += 1; | ||||||
|  |     ((Mob)newMob).setTarget(m_bombTarget); | ||||||
|     return newMob; |     return newMob; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,6 +27,10 @@ public class MobManager { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public boolean contains(Entity entity) { | ||||||
|  |     return m_livingMobs.contains(entity); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public boolean killMob(Entity entity) { |   public boolean killMob(Entity entity) { | ||||||
|     if (m_livingMobs.contains(entity)) { |     if (m_livingMobs.contains(entity)) { | ||||||
|       m_killedMobs += 1; |       m_killedMobs += 1; | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import java.util.Collection; | |||||||
|  |  | ||||||
| public class PlayerManager { | public class PlayerManager { | ||||||
|   HashMap<Player, State> m_playerStates = new HashMap<>(); |   HashMap<Player, State> m_playerStates = new HashMap<>(); | ||||||
|  |   HashMap<Player, Boolean> m_playerReadyStates = new HashMap<>(); | ||||||
|  |  | ||||||
|   public enum State { |   public enum State { | ||||||
|     Idle, |     Idle, | ||||||
| @@ -70,6 +71,34 @@ public class PlayerManager { | |||||||
|   public void addPlayer(Player player) { |   public void addPlayer(Player player) { | ||||||
|     //m_log.info("Adding player " + player);  |     //m_log.info("Adding player " + player);  | ||||||
|     m_playerStates.put(player, State.Idle); |     m_playerStates.put(player, State.Idle); | ||||||
|  |     m_playerReadyStates.put(player, false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public boolean isReady(Player player) { | ||||||
|  |     return m_playerReadyStates.get(player); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setReady(Player player, boolean ready) { | ||||||
|  |     m_playerReadyStates.put(player, ready); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public double readyProgress() { | ||||||
|  |     int readyNum = 0; | ||||||
|  |     for(boolean b : m_playerReadyStates.values()) { | ||||||
|  |       if (b) { | ||||||
|  |         readyNum += 1; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return (double)readyNum / (double)m_playerReadyStates.size(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public boolean isEveryoneReady() { | ||||||
|  |     for(boolean b : m_playerReadyStates.values()) { | ||||||
|  |       if (!b) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public boolean removePlayer(Player player) { |   public boolean removePlayer(Player player) { | ||||||
|   | |||||||
| @@ -6,4 +6,5 @@ public interface Arena { | |||||||
|   World getWorld(); |   World getWorld(); | ||||||
|   String name(); |   String name(); | ||||||
|   Spawnpoint[] spawnpoints(); |   Spawnpoint[] spawnpoints(); | ||||||
|  |   Spawnpoint bombTarget(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,3 +10,5 @@ commands: | |||||||
|     description: Adds a player to a game |     description: Adds a player to a game | ||||||
|   debuginfo: |   debuginfo: | ||||||
|     description: Unknowable powers |     description: Unknowable powers | ||||||
|  |   ready: | ||||||
|  |     description: Mark yourself as ready for the next wave | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user