Make things less debug-y, introduce the start of scripted waves, UX improvements
This commit is contained in:
parent
d2c12b4792
commit
9e7ab2c0d4
30
TODO.md
Normal file
30
TODO.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
[ ] Coin drops
|
||||||
|
[ ] Basic setup commands
|
||||||
|
[ ] Config file
|
||||||
|
[ ] Spectator mode
|
||||||
|
[ ] Commands/tools to start warmup countdown/mark 'ready'
|
||||||
|
[ ] Upgrades
|
||||||
|
[ ] Plan stats
|
||||||
|
[ ] Live map editing
|
||||||
|
[ ] Instancing
|
||||||
|
[ ] DiscordSRV
|
||||||
|
[ ] Voice Chat
|
||||||
|
[ ] Player readiness starts countdown
|
||||||
|
|
||||||
|
# Scripted waves
|
||||||
|
|
||||||
|
[ ] Bosses
|
||||||
|
[ ] Batch overlap/timing
|
||||||
|
|
||||||
|
# Game lifecycle
|
||||||
|
|
||||||
|
[ ] Return to lobby
|
||||||
|
[ ] Join game via sign/commands/something
|
||||||
|
[ ] Bonus coins for complete coin pickup
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
|
||||||
|
[ ] Generic countdown mechanic
|
||||||
|
[ ]
|
@ -4,27 +4,25 @@ import org.bukkit.event.EventHandler;
|
|||||||
import org.bukkit.event.Listener;
|
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.PlayerQuitEvent;
|
|
||||||
import org.bukkit.event.entity.EntityCombustEvent;
|
import org.bukkit.event.entity.EntityCombustEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class GameEventHandler implements Listener {
|
public class GameEventHandler implements Listener {
|
||||||
Plugin m_plugin;
|
GameRunner m_runner;
|
||||||
|
|
||||||
public GameEventHandler(Plugin plugin) {
|
public GameEventHandler(GameRunner runner) {
|
||||||
m_plugin = plugin;
|
m_runner = runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityDeath(EntityDeathEvent evt) {
|
public void onEntityDeath(EntityDeathEvent evt) {
|
||||||
GameRunner runner = m_plugin.getRunnerForWorld(evt.getEntity().getLocation().getWorld());
|
m_runner.handleEntityDeath(evt.getEntity());
|
||||||
runner.handleEntityDeath(evt.getEntity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerQuit(PlayerQuitEvent evt) {
|
public void onPlayerQuit(PlayerQuitEvent evt) {
|
||||||
GameRunner runner = m_plugin.getRunnerForWorld(evt.getPlayer().getLocation().getWorld());
|
m_runner.removePlayer(evt.getPlayer());
|
||||||
runner.removePlayer(evt.getEntity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -37,10 +35,11 @@ public class GameEventHandler implements Listener {
|
|||||||
if (evt.getEntity() instanceof Player) {
|
if (evt.getEntity() instanceof Player) {
|
||||||
Player player = (Player)evt.getEntity();
|
Player player = (Player)evt.getEntity();
|
||||||
if (player.getHealth() - evt.getFinalDamage() <= 0) {
|
if (player.getHealth() - evt.getFinalDamage() <= 0) {
|
||||||
GameRunner runner = m_plugin.getRunnerForWorld(evt.getEntity().getLocation().getWorld());
|
|
||||||
evt.setCancelled(true);
|
evt.setCancelled(true);
|
||||||
runner.handlePlayerDeath(player);
|
m_runner.handlePlayerDeath(player);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
m_runner.handleEntityDamage(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,39 +5,47 @@ import gg.malloc.defense.model.Arena;
|
|||||||
import gg.malloc.defense.model.Spawnpoint;
|
import gg.malloc.defense.model.Spawnpoint;
|
||||||
import gg.malloc.defense.model.Spawner;
|
import gg.malloc.defense.model.Spawner;
|
||||||
import gg.malloc.defense.model.Wave;
|
import gg.malloc.defense.model.Wave;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
import org.bukkit.boss.BarColor;
|
||||||
|
import org.bukkit.boss.BarStyle;
|
||||||
|
import org.bukkit.boss.BossBar;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
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.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.boss.BarColor;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import org.bukkit.boss.BarStyle;
|
|
||||||
import org.bukkit.boss.BossBar;
|
|
||||||
import org.bukkit.GameMode;
|
|
||||||
import org.bukkit.attribute.Attribute;
|
|
||||||
|
|
||||||
public class GameRunner {
|
public class GameRunner {
|
||||||
ArrayList<Entity> m_spawnedMobs = new ArrayList<Entity>();
|
HashSet<Entity> m_livingMobs = new HashSet<>();
|
||||||
|
int m_createdMobs = 0;
|
||||||
int m_killedMobs = 0;
|
int m_killedMobs = 0;
|
||||||
Arena m_arena;
|
Arena m_arena;
|
||||||
Game m_game;
|
Game m_game;
|
||||||
State m_state;
|
State m_state;
|
||||||
ArrayList<Player> m_players = new ArrayList<>();
|
HashSet<Player> m_players = new HashSet<>();
|
||||||
ArrayList<Player> m_livingPlayers = new ArrayList<>();
|
HashSet<Player> m_livingPlayers = new HashSet<>();
|
||||||
Plugin m_plugin;
|
Plugin m_plugin;
|
||||||
|
|
||||||
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);
|
||||||
BukkitTask m_countdownTask;
|
BukkitTask m_countdownTask;
|
||||||
|
|
||||||
int m_currentWaveNum = 0;
|
int m_currentWaveNum = 0;
|
||||||
int m_currentBatch = 0;
|
int m_currentBatch = 0;
|
||||||
Wave m_currentWave = null;
|
Wave m_currentWave = null;
|
||||||
|
|
||||||
|
Logger m_log;
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
Idle,
|
Idle,
|
||||||
Warmup,
|
Warmup,
|
||||||
@ -51,15 +59,26 @@ public class GameRunner {
|
|||||||
m_arena = arena;
|
m_arena = arena;
|
||||||
m_state = State.Idle;
|
m_state = State.Idle;
|
||||||
m_gameBar.setVisible(true);
|
m_gameBar.setVisible(true);
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
m_log = m_plugin.getLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
int m_warmupCountdown = 0;
|
int m_warmupCountdown = 0;
|
||||||
|
|
||||||
|
public void handleEntityDamage(EntityDamageEvent evt) {
|
||||||
|
Entity entity = evt.getEntity();
|
||||||
|
m_log.info("Damage " + entity);
|
||||||
|
m_log.info("Living Mobs " + m_livingMobs);
|
||||||
|
if (m_livingMobs.contains(entity)) {
|
||||||
|
m_game.onMobDamaged(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void countdownTick() {
|
private void countdownTick() {
|
||||||
if (m_warmupCountdown == 0) {
|
if (m_warmupCountdown == 0) {
|
||||||
requestTransition(State.Playing);
|
requestTransition(State.Playing);
|
||||||
} else {
|
} else {
|
||||||
m_gameBar.setProgress((double)m_warmupCountdown / (double)30);
|
updateMobBars();
|
||||||
broadcastMessage("Starting game in " + m_warmupCountdown);
|
broadcastMessage("Starting game in " + m_warmupCountdown);
|
||||||
m_warmupCountdown--;
|
m_warmupCountdown--;
|
||||||
m_countdownTask = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
m_countdownTask = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
||||||
@ -69,18 +88,21 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void clearMobs() {
|
private void clearMobs() {
|
||||||
for(Entity e : m_spawnedMobs) {
|
for(Entity e : m_livingMobs) {
|
||||||
e.remove();
|
e.remove();
|
||||||
}
|
}
|
||||||
m_spawnedMobs.clear();
|
m_livingMobs.clear();
|
||||||
|
m_createdMobs = 0;
|
||||||
|
m_killedMobs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterIdle() {
|
private boolean enterIdle() {
|
||||||
broadcastMessage("Game state: Idle");
|
broadcastMessage("Game state: Idle");
|
||||||
m_currentWaveNum = 0;
|
m_currentWaveNum = 0;
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
m_livingPlayers.clear();
|
||||||
m_gameBar.setProgress(1.0);
|
for(Player p : m_players) {
|
||||||
m_gameBar.setTitle("Idle");
|
m_livingPlayers.add(p);
|
||||||
|
}
|
||||||
if (m_countdownTask != null) {
|
if (m_countdownTask != null) {
|
||||||
m_countdownTask.cancel();
|
m_countdownTask.cancel();
|
||||||
m_countdownTask = null;
|
m_countdownTask = null;
|
||||||
@ -90,83 +112,115 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterWarmup() {
|
private boolean enterWarmup() {
|
||||||
broadcastMessage("Game state: Warmup");
|
m_log.info("Game state: Warmup");
|
||||||
m_currentWaveNum += 1;
|
m_currentWaveNum += 1;
|
||||||
m_currentWave = m_game.getWave(m_currentWaveNum);
|
m_currentWave = m_game.getWave(m_currentWaveNum);
|
||||||
m_gameBar.setColor(BarColor.YELLOW);
|
m_warmupCountdown = 10;
|
||||||
m_gameBar.setProgress(1.0);
|
|
||||||
m_gameBar.setTitle("Warmup");
|
|
||||||
m_warmupCountdown = 30;
|
|
||||||
for(Player p : m_players) {
|
for(Player p : m_players) {
|
||||||
m_livingPlayers.add(p);
|
m_livingPlayers.add(p);
|
||||||
}
|
}
|
||||||
|
broadcastTitle("Warmup", "Prepare yourself for wave " + m_currentWaveNum);
|
||||||
clearMobs();
|
clearMobs();
|
||||||
countdownTick();
|
countdownTick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterPlaying() {
|
private boolean enterPlaying() {
|
||||||
broadcastMessage("Game state: Playing");
|
m_log.info("Game state: Playing");
|
||||||
broadcastMessage("Starting wave " + m_currentWaveNum);
|
m_log.info("Starting wave " + m_currentWaveNum);
|
||||||
m_currentBatch = 1;
|
m_currentBatch = 1;
|
||||||
m_gameBar.setColor(BarColor.GREEN);
|
|
||||||
m_gameBar.setProgress(0.0);
|
|
||||||
m_gameBar.setTitle("Playing");
|
|
||||||
spawnNextBatch();
|
spawnNextBatch();
|
||||||
|
broadcastTitle("Wave " + m_currentWaveNum);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterGameOver() {
|
private boolean enterGameOver() {
|
||||||
broadcastMessage("Game state: Game Over!");
|
broadcastTitle("Game Over!");
|
||||||
m_gameBar.setColor(BarColor.RED);
|
|
||||||
m_gameBar.setProgress(1.0);
|
|
||||||
m_gameBar.setTitle("Game Over!");
|
|
||||||
if (m_countdownTask != null) {
|
if (m_countdownTask != null) {
|
||||||
m_countdownTask.cancel();
|
m_countdownTask.cancel();
|
||||||
m_countdownTask = null;
|
m_countdownTask = null;
|
||||||
}
|
}
|
||||||
for(Player p : m_players) {
|
|
||||||
p.setGameMode(GameMode.ADVENTURE);
|
|
||||||
}
|
|
||||||
clearMobs();
|
clearMobs();
|
||||||
|
for(Player p : m_players) {
|
||||||
|
removePlayer(p);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMobBar() {
|
private void updateMobBars() {
|
||||||
m_gameBar.setTitle("Mobs remaining: " + (m_currentWave.totalMobCount() - m_killedMobs));
|
m_gameBar.setVisible(true);
|
||||||
m_gameBar.setProgress(m_killedMobs / m_currentWave.totalMobCount());
|
switch(m_state) {
|
||||||
|
case Idle:
|
||||||
|
m_gameBar.setProgress(1.0);
|
||||||
|
m_gameBar.setTitle("Waiting for playres...");
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
case Warmup:
|
||||||
|
m_gameBar.setProgress((double)m_currentWaveNum / (double)m_game.getWaveCount());
|
||||||
|
m_gameBar.setTitle("Wave " + m_currentWaveNum + " / " + m_game.getWaveCount());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.YELLOW);
|
||||||
|
m_waveBar.setTitle("Warmup");
|
||||||
|
m_waveBar.setProgress((double)m_warmupCountdown / (double)10);
|
||||||
|
break;
|
||||||
|
case Playing:
|
||||||
|
m_gameBar.setProgress((double)m_currentWaveNum / (double)m_game.getWaveCount());
|
||||||
|
m_gameBar.setTitle("Wave " + m_currentWaveNum + " / " + m_game.getWaveCount());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
if (m_createdMobs > 0) {
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.GREEN);
|
||||||
|
m_waveBar.setTitle("Mobs remaining: " + (m_createdMobs - m_killedMobs));
|
||||||
|
m_waveBar.setProgress((double)m_killedMobs / (double)m_createdMobs);
|
||||||
|
} else {
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GameOver:
|
||||||
|
m_gameBar.setColor(BarColor.RED);
|
||||||
|
m_gameBar.setProgress(1.0);
|
||||||
|
m_gameBar.setTitle("Game Over!");
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnNextBatch() {
|
private void spawnNextBatch() {
|
||||||
broadcastMessage("Spawning batch " + m_currentBatch);
|
broadcastMessage("Spawning batch " + m_currentBatch);
|
||||||
Spawner spawner = new GameSpawner(m_arena.spawnpoints()[0]);
|
Spawner spawner = new GameSpawner(m_arena.spawnpoints());
|
||||||
m_currentWave.spawnBatch(spawner, m_currentBatch);
|
m_currentWave.spawnBatch(spawner, m_currentBatch);
|
||||||
updateMobBar();
|
updateMobBars();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handlePlayerDeath(Player player) {
|
public void handlePlayerDeath(Player player) {
|
||||||
if (m_livingPlayers.contains(player)) {
|
if (m_livingPlayers.contains(player)) {
|
||||||
|
m_log.info("Player has died in game" + player);
|
||||||
m_livingPlayers.remove(player);
|
m_livingPlayers.remove(player);
|
||||||
|
m_arena.getWorld().strikeLightningEffect(player.getLocation());
|
||||||
player.setGameMode(GameMode.SPECTATOR);
|
player.setGameMode(GameMode.SPECTATOR);
|
||||||
if (m_livingPlayers.size() == 0) {
|
if (m_livingPlayers.size() == 0) {
|
||||||
broadcastMessage("Everyone is dead :(");
|
broadcastMessage("Everyone is dead :(");
|
||||||
requestTransition(State.GameOver);
|
requestTransition(State.GameOver);
|
||||||
|
} else {
|
||||||
|
m_log.info("Remaining players " + m_livingPlayers.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleEntityDeath(Entity entity) {
|
public void handleEntityDeath(Entity entity) {
|
||||||
if (m_spawnedMobs.contains(entity)) {
|
if (m_livingMobs.contains(entity)) {
|
||||||
broadcastMessage("Killed game entity " + entity);
|
broadcastMessage("Killed game entity " + entity);
|
||||||
m_spawnedMobs.remove(entity);
|
m_livingMobs.remove(entity);
|
||||||
m_killedMobs += 1;
|
m_killedMobs += 1;
|
||||||
updateMobBar();
|
updateMobBars();
|
||||||
if (m_spawnedMobs.size() == 0) {
|
if (m_livingMobs.size() <= 3) {
|
||||||
broadcastMessage("Batch complete!");
|
m_log.info("Starting next batch!");
|
||||||
if (m_currentBatch >= m_currentWave.batchCount()) {
|
if (m_currentBatch >= m_currentWave.batchCount()) {
|
||||||
if (m_currentWaveNum >= m_game.getWaveCount()) {
|
if (m_currentWaveNum >= m_game.getWaveCount()) {
|
||||||
requestTransition(State.GameOver);
|
requestTransition(State.GameOver);
|
||||||
} else {
|
} else if (m_livingMobs.size() == 0) {
|
||||||
requestTransition(State.Warmup);
|
requestTransition(State.Warmup);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -174,22 +228,31 @@ public class GameRunner {
|
|||||||
spawnNextBatch();
|
spawnNextBatch();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
broadcastMessage("Entities remaining: " + m_spawnedMobs.size());
|
m_log.fine("Living mobs remaining: " + m_livingMobs.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean syncPlayer(Player player) {
|
private boolean syncPlayer(Player player) {
|
||||||
|
m_log.fine("Synchronizing player " + player);
|
||||||
World playerWorld = player.getLocation().getWorld();
|
World playerWorld = player.getLocation().getWorld();
|
||||||
World gameWorld = m_arena.getWorld();
|
World gameWorld = m_arena.getWorld();
|
||||||
m_gameBar.addPlayer(player);
|
m_gameBar.addPlayer(player);
|
||||||
|
m_waveBar.addPlayer(player);
|
||||||
if (m_livingPlayers.contains(player)) {
|
if (m_livingPlayers.contains(player)) {
|
||||||
player.setGameMode(GameMode.ADVENTURE);
|
m_log.fine("Player is alive, turning into adventure mode " + player);
|
||||||
|
if (player.getGameMode() != Bukkit.getDefaultGameMode()) {
|
||||||
|
player.setGameMode(Bukkit.getDefaultGameMode());
|
||||||
|
player.teleport(gameWorld.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
|
}
|
||||||
player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
|
player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
|
||||||
|
player.setFoodLevel(10);
|
||||||
} else {
|
} else {
|
||||||
|
m_log.fine("Player is dead, turning into spectator " + player);
|
||||||
player.setGameMode(GameMode.SPECTATOR);
|
player.setGameMode(GameMode.SPECTATOR);
|
||||||
}
|
}
|
||||||
if (playerWorld != gameWorld) {
|
if (playerWorld != gameWorld) {
|
||||||
|
m_log.info("Teleporting player " + player);
|
||||||
return player.teleport(gameWorld.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
return player.teleport(gameWorld.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -229,22 +292,27 @@ public class GameRunner {
|
|||||||
|
|
||||||
public boolean requestTransition(State state) {
|
public boolean requestTransition(State state) {
|
||||||
if (!validateTransition(m_state, state)) {
|
if (!validateTransition(m_state, state)) {
|
||||||
|
m_log.warning("Attemped illegal transition: " + m_state + " -> " + state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (attemptTransition(state)) {
|
if (attemptTransition(state)) {
|
||||||
|
updateMobBars();
|
||||||
|
m_log.info("Game transition: " + m_state + " -> " + state);
|
||||||
m_state = state;
|
m_state = state;
|
||||||
for(Player p : m_players) {
|
for(Player p : m_players) {
|
||||||
syncPlayer(p);
|
syncPlayer(p);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
m_log.severe("Failed to complete transition: " + m_state + " -> " + state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addPlayer(Player p) {
|
public boolean addPlayer(Player p) {
|
||||||
if (m_state == State.Idle || m_state == State.Warmup) {
|
if (m_state == State.Idle || m_state == State.Warmup) {
|
||||||
|
m_log.info("Adding player " + p);
|
||||||
m_players.add(p);
|
m_players.add(p);
|
||||||
broadcastMessage("Added player " + p + " to game");
|
broadcastMessage(p.getName() + " has joined the game");
|
||||||
syncPlayer(p);
|
syncPlayer(p);
|
||||||
if (m_state == State.Idle) {
|
if (m_state == State.Idle) {
|
||||||
requestTransition(State.Warmup);
|
requestTransition(State.Warmup);
|
||||||
@ -256,14 +324,27 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removePlayer(Player p) {
|
public void removePlayer(Player p) {
|
||||||
|
m_log.info("Removing player " + p);
|
||||||
m_gameBar.removePlayer(p);
|
m_gameBar.removePlayer(p);
|
||||||
|
m_waveBar.removePlayer(p);
|
||||||
m_players.remove(p);
|
m_players.remove(p);
|
||||||
m_livingPlayers.remove(p);
|
m_livingPlayers.remove(p);
|
||||||
|
p.setGameMode(Bukkit.getDefaultGameMode());
|
||||||
if (m_players.size() == 0) {
|
if (m_players.size() == 0) {
|
||||||
requestTransition(State.Idle);
|
requestTransition(State.Idle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void broadcastTitle(String title) {
|
||||||
|
broadcastTitle(title, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void broadcastTitle(String title, String subtitle) {
|
||||||
|
for(Player p : m_players) {
|
||||||
|
p.sendTitle(title, subtitle, 10, 70, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void broadcastMessage(String string) {
|
void broadcastMessage(String string) {
|
||||||
World world = m_arena.getWorld();
|
World world = m_arena.getWorld();
|
||||||
for(Player p : world.getPlayers()) {
|
for(Player p : world.getPlayers()) {
|
||||||
@ -272,22 +353,28 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void registerSpawnedMob(Entity entity) {
|
void registerSpawnedMob(Entity entity) {
|
||||||
m_spawnedMobs.add(entity);
|
m_log.fine("Registered new mob " + entity);
|
||||||
|
m_livingMobs.add(entity);
|
||||||
|
m_createdMobs += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GameSpawner implements Spawner {
|
private class GameSpawner implements Spawner {
|
||||||
Spawnpoint m_spawnpoint;
|
Spawnpoint[] m_spawnpoints;
|
||||||
|
int m_spawnIdx = 0;
|
||||||
public GameSpawner(Spawnpoint spawnpoint) {
|
|
||||||
m_spawnpoint = spawnpoint;
|
public GameSpawner(Spawnpoint[] spawnpoints) {
|
||||||
|
m_spawnpoints = spawnpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entity spawnMob(EntityType type) {
|
public Entity spawnMob(EntityType type) {
|
||||||
Entity newMob = m_arena.getWorld().spawnEntity(m_spawnpoint.getLocation(), type);
|
m_log.fine("Spawning " + type + " at " + m_spawnpoints[m_spawnIdx]);
|
||||||
|
Entity newMob = m_arena.getWorld().spawnEntity(m_spawnpoints[m_spawnIdx].getLocation(), type);
|
||||||
LivingEntity livingMob = (LivingEntity)newMob;
|
LivingEntity livingMob = (LivingEntity)newMob;
|
||||||
livingMob.setRemoveWhenFarAway(false);
|
livingMob.setRemoveWhenFarAway(false);
|
||||||
registerSpawnedMob(newMob);
|
registerSpawnedMob(newMob);
|
||||||
|
m_spawnIdx += 1;
|
||||||
|
m_spawnIdx %= m_spawnpoints.length;
|
||||||
return newMob;
|
return newMob;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ 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.plugin.PluginLogger;
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -16,6 +18,7 @@ import gg.malloc.defense.model.Spawnpoint;
|
|||||||
import gg.malloc.defense.model.Game;
|
import gg.malloc.defense.model.Game;
|
||||||
|
|
||||||
import gg.malloc.defense.games.LinearGame;
|
import gg.malloc.defense.games.LinearGame;
|
||||||
|
import gg.malloc.defense.games.ScaledWaves;
|
||||||
|
|
||||||
public class Plugin extends JavaPlugin {
|
public class Plugin extends JavaPlugin {
|
||||||
ArrayList<Arena> m_arenas = new ArrayList<>();
|
ArrayList<Arena> m_arenas = new ArrayList<>();
|
||||||
@ -45,20 +48,18 @@ public class Plugin extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginLogger m_log = new PluginLogger(this);
|
|
||||||
|
|
||||||
public void debuginfo() {
|
public void debuginfo() {
|
||||||
m_log.info("Debug Info:");
|
getLogger().info("Debug Info:");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
getLogger().info("Malloc Defense registered");
|
getLogger().info("Malloc Defense registered");
|
||||||
setupTestGame();
|
getLogger().setLevel(Level.FINEST);
|
||||||
|
setupDemoGame();
|
||||||
getCommand("setstage").setExecutor(new SetStageCommand(this));
|
getCommand("setstage").setExecutor(new SetStageCommand(this));
|
||||||
getCommand("initgame").setExecutor(new InitGameCommand(this));
|
getCommand("initgame").setExecutor(new InitGameCommand(this));
|
||||||
getCommand("debuginfo").setExecutor(new DebuginfoCommand(this));
|
getCommand("debuginfo").setExecutor(new DebuginfoCommand(this));
|
||||||
getServer().getPluginManager().registerEvents(new GameEventHandler(this), this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameRunner getRunnerForWorld(World world) {
|
public GameRunner getRunnerForWorld(World world) {
|
||||||
@ -68,15 +69,21 @@ public class Plugin extends JavaPlugin {
|
|||||||
} else {
|
} else {
|
||||||
ret = new GameRunner(this, m_games.get(0), m_arenas.get(0));
|
ret = new GameRunner(this, m_games.get(0), m_arenas.get(0));
|
||||||
m_runningGames.put(world, ret);
|
m_runningGames.put(world, ret);
|
||||||
|
getServer().getPluginManager().registerEvents(new GameEventHandler(ret), this);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupTestGame() {
|
void setupDemoGame() {
|
||||||
|
getLogger().info("Setting up demo data'");
|
||||||
World testWorld = getServer().getWorld("world");
|
World testWorld = getServer().getWorld("world");
|
||||||
Spawnpoint[] spawnpoints = new Spawnpoint[1];
|
Spawnpoint[] spawnpoints = new Spawnpoint[4];
|
||||||
spawnpoints[0] = new TestSpawn(testWorld.getSpawnLocation());
|
Location spawnCenter = testWorld.getSpawnLocation();
|
||||||
|
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));
|
m_arenas.add(new MemoryArena("Test Arena", testWorld, spawnpoints));
|
||||||
m_games.add(new LinearGame());
|
m_games.add(new ScaledWaves());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,6 @@ public class LinearGame implements Game {
|
|||||||
m_zombiesPerBatch = zombiesPerBatch;
|
m_zombiesPerBatch = zombiesPerBatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int totalMobCount() {
|
|
||||||
int ret = 0;
|
|
||||||
for(int i = 1; i <= m_batches; i++) {
|
|
||||||
ret += i * m_zombiesPerBatch;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int batchCount() {
|
public int batchCount() {
|
||||||
return m_batches;
|
return m_batches;
|
||||||
|
79
src/main/java/gg/malloc/defense/games/ScaledWaves.java
Normal file
79
src/main/java/gg/malloc/defense/games/ScaledWaves.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package gg.malloc.defense.games;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Wave;
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
|
import gg.malloc.defense.model.Spawner;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class ScaledWaves implements Game {
|
||||||
|
@Override
|
||||||
|
public int getWaveCount() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wave getWave(int waveNumber) {
|
||||||
|
HashMap<EntityType, Double> weights = new HashMap<>();
|
||||||
|
if (waveNumber <= 3) {
|
||||||
|
weights.put(EntityType.ZOMBIE, 1.0);
|
||||||
|
} else if (waveNumber <= 5) {
|
||||||
|
weights.put(EntityType.ZOMBIE, 0.6);
|
||||||
|
weights.put(EntityType.SPIDER, 0.1);
|
||||||
|
weights.put(EntityType.SKELETON, 0.3);
|
||||||
|
} else if (waveNumber <= 7) {
|
||||||
|
weights.put(EntityType.ZOMBIE, 0.7);
|
||||||
|
weights.put(EntityType.SKELETON, 0.3);
|
||||||
|
} else {
|
||||||
|
weights.put(EntityType.PILLAGER, 0.2);
|
||||||
|
weights.put(EntityType.ZOMBIE, 0.8);
|
||||||
|
}
|
||||||
|
boolean hasRavager = waveNumber >= 9;
|
||||||
|
return new MobWave(weights, waveNumber * 3, 3, hasRavager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MobWave implements Wave {
|
||||||
|
HashMap<EntityType, Double> m_spawnWeights;
|
||||||
|
int m_batches;
|
||||||
|
int m_mobsPerBatch;
|
||||||
|
boolean m_hasRavager;
|
||||||
|
|
||||||
|
MobWave(HashMap<EntityType, Double> weights, int totalCount, int batches, boolean hasRavager) {
|
||||||
|
m_batches = batches;
|
||||||
|
m_mobsPerBatch = Math.max(1, totalCount / batches);
|
||||||
|
m_spawnWeights = weights;
|
||||||
|
m_hasRavager = hasRavager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int batchCount() {
|
||||||
|
return m_batches;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void spawnBatch(Spawner spawner, int batch) {
|
||||||
|
assert(m_mobsPerBatch > 0);
|
||||||
|
for(int i = 0; i < m_mobsPerBatch; i++) {
|
||||||
|
EntityType selectedType = null;
|
||||||
|
double random = Math.random() * 1.0;
|
||||||
|
for(EntityType type : m_spawnWeights.keySet()) {
|
||||||
|
random -= m_spawnWeights.get(type);
|
||||||
|
if (random <= 0.0d) {
|
||||||
|
selectedType = type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(selectedType != null);
|
||||||
|
Entity newMob = spawner.spawnMob(selectedType);
|
||||||
|
newMob.setCustomName("Mob " + i + "/" + m_mobsPerBatch);
|
||||||
|
}
|
||||||
|
if (m_hasRavager) {
|
||||||
|
Entity newMob = spawner.spawnMob(EntityType.RAVAGER);
|
||||||
|
newMob.setCustomName("RAVAGER");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package gg.malloc.defense.model;
|
package gg.malloc.defense.model;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
|
||||||
public interface Game {
|
public interface Game {
|
||||||
int getWaveCount();
|
int getWaveCount();
|
||||||
Wave getWave(int waveNumber);
|
Wave getWave(int waveNumber);
|
||||||
|
default void onMobDamaged(Entity entity) {}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,5 @@ package gg.malloc.defense.model;
|
|||||||
|
|
||||||
public interface Wave {
|
public interface Wave {
|
||||||
int batchCount();
|
int batchCount();
|
||||||
int totalMobCount();
|
|
||||||
void spawnBatch(Spawner spawner, int batch);
|
void spawnBatch(Spawner spawner, int batch);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user