Implement first pass at config loading, /join, /leave commands, tab completion (broken)

This commit is contained in:
Torrie Fischer 2022-05-10 21:35:25 +02:00
parent 75debe1905
commit a4b110773e
17 changed files with 410 additions and 110 deletions

39
TODO.md
View File

@ -8,12 +8,18 @@
# Malloc Beta # Malloc Beta
[ ] Join games [X] One arena config
[ ] Leave games [ ] Config reload
[ ] One arena config [X] Leave games
[X] Join games
[ ] Lobby with instructions
[ ] Drop back to lobby on game over
[ ] Grist drops
[ ] Item shoppes
# Scaled waves # Scaled waves
[ ] Limit ranged mobs and Ravagers to non-bomb-carrier state
[ ] Weaker mobs, more of them [ ] Weaker mobs, more of them
[ ] Mob categories [ ] Mob categories
[ ] Spawnpoint categories [ ] Spawnpoint categories
@ -24,9 +30,16 @@
[X] Mob count boss bar [X] Mob count boss bar
[X] Stage titles [X] Stage titles
[X] EXPLOSIONS [X] EXPLOSIONS
[ ] Target catches on fire more it is lit [X] Bomb model
[ ] Pretty bomb model
[ ] "Player $X is ready" message in chat
[ ] Colored titles [ ] Colored titles
[ ] Clickable join links in /list
[ ] Clickable /leave action
[ ] Clickable /ready in chat [ ] Clickable /ready in chat
[ ] Countdown while in warmup
[ ] Countdown shrinks w/ every /ready player
[ ] Target catches on fire more it is lit
[ ] Sidebar [ ] Sidebar
[ ] List of mobs in next wave [ ] List of mobs in next wave
@ -37,13 +50,16 @@
[ ] Plan stats [ ] Plan stats
[ ] /invite friends to games [ ] /invite friends to games
[ ] /voterestart [ ] /voterestart
[ ] Medals/awards/scoreboards
# Mechanics # Mechanics
[ ] Coin drops
[X] Mob tracking should prioritize bomb [X] Mob tracking should prioritize bomb
[X] Mobs recover dropped bombs [X] Mobs recover dropped bombs
[X] Bomb carriers are slower [X] Bomb carriers are slower
[ ] Coin drops
[ ] Mob categories
[ ] Mobs split between bomb and player priorities
[ ] Bonus coins for complete coin pickup [ ] Bonus coins for complete coin pickup
[ ] Infinite weapons + armor [ ] Infinite weapons + armor
[ ] Ammo/health spawns [ ] Ammo/health spawns
@ -56,26 +72,29 @@
[X] Batch overlap [X] Batch overlap
[ ] Scripted batch overlap/timings [ ] Scripted batch overlap/timings
[ ] Scripted spawn locations [ ] Scripted spawn locations
[ ] Scripted waypoint paths
[ ] Bosses [ ] Bosses
# Mapping # Mapping
[ ] Load arenas from config file [X] Load arenas from config file
[ ] Live map editing [ ] Live map editing
# Game lifecycle # Game lifecycle
[ ] /list arenas and games [X] /list arenas and games
[ ] /start a game on an arena [X] /join games
[ ] /join games
[X] /ready [X] /ready
[ ] /leave games [X] /leave games
[ ] /restart games [ ] /restart games
[X] Spectator mode on death [X] Spectator mode on death
[X] Player readiness starts countdown [X] Player readiness starts countdown
[ ] Game is automatically closed some time after game over [ ] Game is automatically closed some time after game over
[ ] Return to lobby on leave/close [ ] Return to lobby on leave/close
[ ] Instancing [ ] Instancing
[ ] Restore health+hunger on respawn/game start
[ ] Respawn during games
[ ] Player revival items
# Powerups # Powerups

View File

@ -1,41 +1,27 @@
package gg.malloc.defense; package gg.malloc.defense;
import gg.malloc.defense.model.Arena; import gg.malloc.defense.model.Arena;
import gg.malloc.defense.model.Spawnpoint; import gg.malloc.defense.model.Waypoint;
import org.bukkit.World; import org.bukkit.World;
public class MemoryArena implements Arena { public class MemoryArena implements Arena {
Spawnpoint[] m_spawnpoints; Waypoint[] m_spawnpoints;
Spawnpoint m_bombTarget; Waypoint m_bombTarget;
World m_world;
String m_name;
public MemoryArena(String name, World world, Spawnpoint[] spawnpoints, Spawnpoint bombTarget) { public MemoryArena(Waypoint[] spawnpoints, Waypoint bombTarget) {
m_world = world;
m_spawnpoints = spawnpoints; m_spawnpoints = spawnpoints;
m_name = name;
m_bombTarget = bombTarget; m_bombTarget = bombTarget;
} }
@Override @Override
public String name() { public Waypoint[] spawnpoints() {
return m_name;
}
@Override
public Spawnpoint[] spawnpoints() {
return m_spawnpoints; return m_spawnpoints;
} }
@Override @Override
public World getWorld() { public Waypoint bombTarget() {
return m_world;
}
@Override
public Spawnpoint bombTarget() {
return m_bombTarget; return m_bombTarget;
} }
} }

View File

@ -5,17 +5,27 @@ import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.Location; 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.entity.Player;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.WorldCreator; import org.bukkit.WorldCreator;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.EventHandler;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import gg.malloc.defense.model.Arena; import gg.malloc.defense.model.Arena;
import gg.malloc.defense.model.Spawnpoint; import gg.malloc.defense.model.Waypoint;
import gg.malloc.defense.model.Game; import gg.malloc.defense.model.Game;
import gg.malloc.defense.games.LinearGame; import gg.malloc.defense.games.LinearGame;
@ -26,32 +36,40 @@ 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; import gg.malloc.defense.commands.PlayerReadyCommand;
import gg.malloc.defense.commands.ListGamesCommand;
import gg.malloc.defense.commands.JoinGameCommand;
import gg.malloc.defense.commands.LeaveGameCommand;
public class Plugin extends JavaPlugin { public class Plugin extends JavaPlugin {
ArrayList<Arena> m_arenas = new ArrayList<>(); HashMap<String, Arena> m_arenas = new HashMap<>();
ArrayList<Game> m_games = new ArrayList<>(); ArrayList<Game> m_games = new ArrayList<>();
HashMap<World, GameRunner> m_runningGames = new HashMap<>(); HashMap<String, GameRunner> m_runningGames = new HashMap<>();
HashMap<Player, GameRunner> m_playerGames = new HashMap<>();
private class TestSpawn implements Spawnpoint { public Collection<String> arenaNames() {
Location m_location; return m_arenas.keySet();
public TestSpawn(Location location) {
m_location = location;
} }
@Override private class TestSpawn implements Waypoint {
public Location getLocation() { double m_x;
return m_location; double m_y;
double m_z;
String m_name;
public TestSpawn(double x, double y, double z) {
m_x = x;
m_y = y;
m_z = z;
m_name = "(" + x + "," + y + "," + z + ")";
} }
public double getX() { return m_x; }
public double getY() { return m_y; }
public double getZ() { return m_z; }
@Override @Override
public String getName() { public String getName() {
return "Mob Spawner"; return m_name;
}
@Override
public String getID() {
return "mob-spawner";
} }
} }
@ -63,23 +81,107 @@ public class Plugin extends JavaPlugin {
public void onEnable() { public void onEnable() {
getLogger().info("Malloc Defense registered"); getLogger().info("Malloc Defense registered");
getLogger().setLevel(Level.FINEST); getLogger().setLevel(Level.FINEST);
setupDemoGame(); //setupDemoGame();
getCommand("setstage").setExecutor(new SetStageCommand(this)); m_games.add(new ScaledWaves());
getCommand("addplayer").setExecutor(new AddPlayerCommand(this)); loadArenas();
getCommand("debuginfo").setExecutor(new DebuginfoCommand(this)); getCommand("join").setExecutor(new JoinGameCommand(this));
getCommand("leave").setExecutor(new LeaveGameCommand(this));
getCommand("ready").setExecutor(new PlayerReadyCommand(this)); getCommand("ready").setExecutor(new PlayerReadyCommand(this));
getCommand("list").setExecutor(new ListGamesCommand(this));
getCommand("addplayer").setExecutor(new AddPlayerCommand(this));
getCommand("setstage").setExecutor(new SetStageCommand(this));
getCommand("debuginfo").setExecutor(new DebuginfoCommand(this));
getServer().getPluginManager().registerEvents(new PlayerQuitHandler(), this);
} }
public GameRunner getRunnerForWorld(World world) { void loadArenas() {
getLogger().info("Loading arenas...");
saveDefaultConfig();
ConfigurationSection pluginConfig = getConfig();
ConfigurationSection mapList = pluginConfig.getConfigurationSection("maps");
for(String mapName : mapList.getKeys(false)) {
getLogger().info("Loading arena: " + mapName);
ConfigurationSection mapConfig = mapList.getConfigurationSection(mapName);
List<Map<?, ?>> spawnpointList = mapConfig.getMapList("spawnpoints");
ArrayList<Waypoint> spawnpoints = new ArrayList<>();
for(Map<?, ?> spawnerObj : spawnpointList) {
Map<String, Double> thisSpawner = (Map<String, Double>)spawnerObj;
double x = thisSpawner.get("x");
double y = thisSpawner.get("y");
double z = thisSpawner.get("z");
spawnpoints.add(new TestSpawn(x, y, z));
}
ConfigurationSection targetConfig = mapConfig.getConfigurationSection("target");
double x = targetConfig.getDouble("x");
double y = targetConfig.getDouble("y");
double z = targetConfig.getDouble("z");
Waypoint bombTarget = new TestSpawn(x, y, z);
Waypoint[] spawnArray = new Waypoint[spawnpoints.size()];
spawnpoints.toArray(spawnArray);
Arena arena = new MemoryArena(spawnArray, bombTarget);
m_arenas.put(mapName, arena);
}
}
/*GameRunner getRunnerForWorld(World world) {
GameRunner ret; GameRunner ret;
if (m_runningGames.containsKey(world)) { if (m_runningGames.containsKey(world)) {
ret = m_runningGames.get(world); ret = m_runningGames.get(world);
} else { } else if (m_arenas.containsKey(world)) {
ret = new GameRunner(this, m_games.get(0), m_arenas.get(0)); ret = new GameRunner(this, m_games.get(0), m_arenas.get(world.getName()), world);
m_runningGames.put(world, ret); m_runningGames.put(world, ret);
getServer().getPluginManager().registerEvents(new GameEventHandler(ret), this); getServer().getPluginManager().registerEvents(new GameEventHandler(ret), this);
} }
return ret; return ret;
}*/
public boolean hasRunnerForArenaName(String arenaName) {
return m_runningGames.containsKey(arenaName);
}
public void addPlayerToArena(String arenaName, Player player) {
GameRunner runner = getRunnerForArenaName(arenaName);
runner.addPlayer(player);
m_playerGames.put(player, runner);
}
class PlayerQuitHandler implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent evt) {
m_playerGames.remove(evt.getPlayer());
}
}
public GameRunner getRunnerForArenaName(String arenaName) {
GameRunner ret = null;
if (m_runningGames.containsKey(arenaName)) {
ret = m_runningGames.get(arenaName);
} else if (m_arenas.containsKey(arenaName)) {
getLogger().info("Loading game world " + arenaName);
World gameWorld = getServer().getWorld(arenaName);
if (gameWorld == null) {
getLogger().info("Creating game world " + arenaName);
gameWorld = new WorldCreator(arenaName).generateStructures(false).createWorld();
}
ret = new GameRunner(this, m_games.get(0), m_arenas.get(arenaName), gameWorld);
getServer().getPluginManager().registerEvents(new GameEventHandler(ret), this);
m_runningGames.put(arenaName, ret);
getLogger().info("Game ready for " + arenaName);
}
return ret;
}
public GameRunner getRunnerForPlayer(Player p) {
GameRunner ret = null;
if (m_playerGames.containsKey(p)) {
ret = m_playerGames.get(p);
}
return ret;
} }
void setupDemoGame() { void setupDemoGame() {
@ -88,12 +190,11 @@ public class Plugin extends JavaPlugin {
if (testWorld == null) { if (testWorld == null) {
testWorld = new WorldCreator("quarry").generateStructures(false).createWorld(); testWorld = new WorldCreator("quarry").generateStructures(false).createWorld();
} }
Spawnpoint[] spawnpoints = new Spawnpoint[3]; Waypoint[] spawnpoints = new Waypoint[3];
spawnpoints[0] = new TestSpawn(new Location(testWorld, -15, 80, -46)); spawnpoints[0] = new TestSpawn(-15, 80, -46);
spawnpoints[1] = new TestSpawn(new Location(testWorld, -1, 80, -45)); spawnpoints[1] = new TestSpawn(-1, 80, -45);
spawnpoints[2] = new TestSpawn(new Location(testWorld, 12, 81, -42)); spawnpoints[2] = new TestSpawn(12, 81, -42);
Spawnpoint bombTarget = new TestSpawn(new Location(testWorld, -20, 80, 31)); Waypoint bombTarget = new TestSpawn(-20, 80, 31);
m_arenas.add(new MemoryArena("Test Arena", testWorld, spawnpoints, bombTarget)); m_arenas.put("quarry", new MemoryArena(spawnpoints, bombTarget));
m_games.add(new ScaledWaves());
} }
} }

View File

@ -18,8 +18,12 @@ public class AddPlayerCommand implements CommandExecutor {
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) { public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
String worldName = args[0]; String arenaName = args[0];
GameRunner runner = m_plugin.getRunnerForWorld(m_plugin.getServer().getWorld(worldName)); GameRunner runner = m_plugin.getRunnerForArenaName(arenaName);
if (runner == null) {
sender.sendMessage("No such arena '" + arenaName + "'");
return true;
}
Player player = m_plugin.getServer().getPlayer(args[1]); Player player = m_plugin.getServer().getPlayer(args[1]);
runner.addPlayer(player); runner.addPlayer(player);
return true; return true;

View File

@ -0,0 +1,56 @@
package gg.malloc.defense.commands;
import org.bukkit.command.Command;
import org.bukkit.command.TabExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import gg.malloc.defense.engine.GameRunner;
import gg.malloc.defense.Plugin;
import java.util.ArrayList;
import java.util.List;
public class JoinGameCommand implements TabExecutor {
Plugin m_plugin;
public JoinGameCommand(Plugin plugin) {
m_plugin = plugin;
}
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
ArrayList<String> ret = new ArrayList<>();
if (args.length == 1) {
String proposal = args[0].toLowerCase();
for(String arena : m_plugin.arenaNames()) {
if (proposal.startsWith(arena.toLowerCase())) {
ret.add(arena);
}
}
}
return ret;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
if (args.length != 1) {
sender.sendMessage("Usage: join <game>");
return true;
}
if (sender instanceof Player) {
Player player = (Player)sender;
String arenaName = args[0];
sender.sendMessage("Joining arena '" + arenaName + "'...");
GameRunner runner = m_plugin.getRunnerForArenaName(arenaName);
if (runner == null) {
sender.sendMessage("No such arena '" + arenaName + "'");
return true;
}
m_plugin.addPlayerToArena(arenaName, player);
} else {
sender.sendMessage("Only players may use htis command.");
}
return true;
}
}

View File

@ -0,0 +1,35 @@
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 LeaveGameCommand implements CommandExecutor {
Plugin m_plugin;
public LeaveGameCommand(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.getRunnerForPlayer(player);
if (runner == null) {
sender.sendMessage("You ae not currently in a game.");
return true;
}
runner.removePlayer(player);
} else {
sender.sendMessage("Only players may use htis command.");
}
return true;
}
}

View File

@ -0,0 +1,35 @@
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 ListGamesCommand implements CommandExecutor {
Plugin m_plugin;
public ListGamesCommand(Plugin plugin) {
m_plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
sender.sendMessage("Available games:");
for(String arenaName : m_plugin.arenaNames()) {
String arenaDescription = arenaName + ": ";
if (m_plugin.hasRunnerForArenaName(arenaName)) {
GameRunner runner = m_plugin.getRunnerForArenaName(arenaName);
arenaDescription += runner.getStage().toString();
} else {
arenaDescription += "Loadable";
}
sender.sendMessage(arenaDescription);
}
return true;
}
}

View File

@ -20,9 +20,12 @@ public class PlayerReadyCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) { public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
if (sender instanceof Player) { if (sender instanceof Player) {
Player player = (Player)sender; Player player = (Player)sender;
GameRunner runner = m_plugin.getRunnerForWorld(player.getLocation().getWorld()); GameRunner runner = m_plugin.getRunnerForPlayer(player);
runner.addPlayer(player); if (runner != null) {
runner.togglePlayerReady(player); runner.togglePlayerReady(player);
} else {
sender.sendMessage("You are not part of any game.");
}
return true; return true;
} else { } else {
sender.sendMessage("You must be a player to use this command."); sender.sendMessage("You must be a player to use this command.");

View File

@ -1,25 +1,56 @@
package gg.malloc.defense.commands; package gg.malloc.defense.commands;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.World; import org.bukkit.World;
import gg.malloc.defense.engine.GameRunner; import gg.malloc.defense.engine.GameRunner;
import gg.malloc.defense.Plugin; import gg.malloc.defense.Plugin;
public class SetStageCommand implements CommandExecutor { import java.util.ArrayList;
import java.util.List;
public class SetStageCommand implements TabExecutor {
Plugin m_plugin; Plugin m_plugin;
public SetStageCommand(Plugin plugin) { public SetStageCommand(Plugin plugin) {
m_plugin = plugin; m_plugin = plugin;
} }
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
ArrayList<String> ret = new ArrayList<>();
if (args.length == 1) {
String proposal = args[0];
for(String arena : m_plugin.arenaNames()) {
if (proposal.startsWith(arena)) {
ret.add(arena);
}
}
} else if (args.length == 2) {
String proposal = args[1].toLowerCase();
GameRunner.Stage stages[] = GameRunner.Stage.Idle.getDeclaringClass().getEnumConstants();
for(GameRunner.Stage stage : stages) {
if (proposal.startsWith(stage.toString().toLowerCase())) {
ret.add(stage.toString());
}
}
}
return ret;
}
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String s, String[] args) { public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
World world = m_plugin.getServer().getWorld(args[1]); if (args.length != 2) {
GameRunner runner = m_plugin.getRunnerForWorld(world); sender.sendMessage("Usage: setstage <stage> <arena>");
return true;
}
GameRunner runner = m_plugin.getRunnerForArenaName(args[1]);
if (runner == null) {
sender.sendMessage("Unknown arena " + args[1]);
return true;
}
String stateName = args[0].toLowerCase(); String stateName = args[0].toLowerCase();
boolean ret = false; boolean ret = false;
GameRunner.Stage decodedStage = null; GameRunner.Stage decodedStage = null;

View File

@ -3,6 +3,7 @@ package gg.malloc.defense.engine;
import gg.malloc.defense.model.Arena; import gg.malloc.defense.model.Arena;
import gg.malloc.defense.model.Game; import gg.malloc.defense.model.Game;
import gg.malloc.defense.model.Spawner; import gg.malloc.defense.model.Spawner;
import gg.malloc.defense.model.Waypoint;
import gg.malloc.defense.ui.BombCarrier; import gg.malloc.defense.ui.BombCarrier;
import gg.malloc.defense.ui.BossBars; import gg.malloc.defense.ui.BossBars;
@ -60,9 +61,16 @@ public class GameRunner {
BossBars m_bars; BossBars m_bars;
public GameRunner(Plugin plugin, Game game, Arena arena) { World m_world;
Location getLocation(Waypoint waypoint) {
return new Location(m_world, waypoint.getX(), waypoint.getY(), waypoint.getZ());
}
public GameRunner(Plugin plugin, Game game, Arena arena, World world) {
m_plugin = plugin; m_plugin = plugin;
m_game = game; m_game = game;
m_world = world;
m_arena = arena; m_arena = arena;
m_stage = Stage.Idle; m_stage = Stage.Idle;
m_mobs = new MobManager(); m_mobs = new MobManager();
@ -92,13 +100,13 @@ public class GameRunner {
}, 20); }, 20);
m_bombSmokeTask = new TickTask(m_plugin, () -> { m_bombSmokeTask = new TickTask(m_plugin, () -> {
Location targetLoc = m_arena.bombTarget().getLocation(); Location targetLoc = getLocation(m_arena.bombTarget());
m_arena.getWorld().spawnParticle(Particle.SMOKE_LARGE, targetLoc, 35, 4, 2, 4); m_world.spawnParticle(Particle.SMOKE_LARGE, targetLoc, 35, 4, 2, 4);
m_arena.getWorld().spawnParticle(Particle.SMALL_FLAME, targetLoc, 30, 3, 2, 3); m_world.spawnParticle(Particle.SMALL_FLAME, targetLoc, 30, 3, 2, 3);
}, 5); }, 5);
m_bombCrackleTask = new TickTask(m_plugin, () -> { m_bombCrackleTask = new TickTask(m_plugin, () -> {
Location targetLoc = m_arena.bombTarget().getLocation(); Location targetLoc = getLocation(m_arena.bombTarget());
m_arena.getWorld().playSound(targetLoc, Sound.BLOCK_CAMPFIRE_CRACKLE, SoundCategory.NEUTRAL, 1.0f, 1.0f); m_world.playSound(targetLoc, Sound.BLOCK_CAMPFIRE_CRACKLE, SoundCategory.NEUTRAL, 1.0f, 1.0f);
}, 35); }, 35);
} }
@ -115,12 +123,12 @@ public class GameRunner {
m_log.info("Target attacked!"); m_log.info("Target attacked!");
entityEvt.getDamager().setGlowing(true); entityEvt.getDamager().setGlowing(true);
m_bombFuse.tickLit(); m_bombFuse.tickLit();
m_arena.getWorld().playSound(m_arena.bombTarget().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.5f, 0.9f); m_world.playSound(getLocation(m_arena.bombTarget()), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.5f, 0.9f);
m_bars.update(); m_bars.update();
if (m_bombFuse.isExploded()) { if (m_bombFuse.isExploded()) {
m_arena.getWorld().strikeLightningEffect(m_arena.bombTarget().getLocation()); m_world.strikeLightningEffect(getLocation(m_arena.bombTarget()));
m_arena.getWorld().playSound(m_arena.bombTarget().getLocation(), Sound.ENTITY_GENERIC_EXPLODE, SoundCategory.NEUTRAL, 1.3f, 1.0f); m_world.playSound(getLocation(m_arena.bombTarget()), Sound.ENTITY_GENERIC_EXPLODE, SoundCategory.NEUTRAL, 1.3f, 1.0f);
m_arena.getWorld().spawnParticle(Particle.EXPLOSION_HUGE, m_arena.bombTarget().getLocation(), 8, 5, 2, 5); m_world.spawnParticle(Particle.EXPLOSION_HUGE, getLocation(m_arena.bombTarget()), 8, 5, 2, 5);
requestTransition(Stage.GameOver); requestTransition(Stage.GameOver);
m_bombSmokeTask.start(); m_bombSmokeTask.start();
m_bombCrackleTask.start(); m_bombCrackleTask.start();
@ -150,7 +158,7 @@ public class GameRunner {
m_waves.next(); m_waves.next();
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_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
} }
m_players.setReady(p, false); m_players.setReady(p, false);
} }
@ -179,11 +187,11 @@ public class GameRunner {
private boolean enterPlaying() { private boolean enterPlaying() {
m_log.info("Starting wave " + m_waves.currentWaveNum()); m_log.info("Starting wave " + m_waves.currentWaveNum());
m_mobs.spawnTarget(m_arena.bombTarget().getLocation()); m_mobs.spawnTarget(getLocation(m_arena.bombTarget()));
// TODO: Set helmet with custom model data // TODO: Set helmet with custom model data
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_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
} }
} }
m_countdownTask.stop(); m_countdownTask.stop();
@ -211,14 +219,14 @@ public class GameRunner {
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, m_players); Spawner spawner = new GameSpawner(m_world, m_arena, m_mobs, m_players);
m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum()); m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum());
m_bars.update(); m_bars.update();
} }
private void handlePlayerDeath(Player player) { private void handlePlayerDeath(Player player) {
if (m_players.requestTransition(player, PlayerManager.State.Dead)) { if (m_players.requestTransition(player, PlayerManager.State.Dead)) {
m_arena.getWorld().strikeLightningEffect(player.getLocation()); m_world.strikeLightningEffect(player.getLocation());
if (!m_players.isAnyoneAlive()) { if (!m_players.isAnyoneAlive()) {
broadcastMessage("Everyone is dead :("); broadcastMessage("Everyone is dead :(");
requestTransition(Stage.GameOver); requestTransition(Stage.GameOver);
@ -273,7 +281,7 @@ public class GameRunner {
private boolean validateTransition(Stage from, Stage to) { private boolean validateTransition(Stage from, Stage to) {
switch(from) { switch(from) {
case Idle: case Idle:
return to == Stage.Warmup; return !m_players.isEmpty() && to == Stage.Warmup;
case Warmup: case Warmup:
return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown ; return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown ;
case Countdown: case Countdown:
@ -309,7 +317,7 @@ public class GameRunner {
m_bars.addPlayer(p); m_bars.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_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
} }
broadcastMessage(p.getName() + " has joined the game"); broadcastMessage(p.getName() + " has joined the game");
if (m_stage == Stage.Idle) { if (m_stage == Stage.Idle) {
@ -337,8 +345,7 @@ public class GameRunner {
} }
void broadcastMessage(String string) { void broadcastMessage(String string) {
World world = m_arena.getWorld(); for(Player p : m_world.getPlayers()) {
for(Player p : world.getPlayers()) {
p.sendMessage(string); p.sendMessage(string);
} }
} }

View File

@ -1,13 +1,15 @@
package gg.malloc.defense.engine; package gg.malloc.defense.engine;
import gg.malloc.defense.model.Spawner; import gg.malloc.defense.model.Spawner;
import gg.malloc.defense.model.Spawnpoint; import gg.malloc.defense.model.Waypoint;
import gg.malloc.defense.model.Arena; 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; import org.bukkit.entity.Mob;
import org.bukkit.World;
import org.bukkit.Location;
import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
@ -20,8 +22,10 @@ public class GameSpawner implements Spawner {
MobManager m_manager; MobManager m_manager;
PlayerManager m_players; PlayerManager m_players;
int m_spawnIdx = 0; int m_spawnIdx = 0;
World m_world;
public GameSpawner(Arena arena, MobManager manager, PlayerManager players) { public GameSpawner(World world, Arena arena, MobManager manager, PlayerManager players) {
m_world = world;
m_arena = arena; m_arena = arena;
m_manager = manager; m_manager = manager;
m_players = players; m_players = players;
@ -38,10 +42,12 @@ public class GameSpawner implements Spawner {
@Override @Override
public LivingEntity spawnMob(EntityType type) { public LivingEntity spawnMob(EntityType type) {
Spawnpoint[] spawnpoints = m_arena.spawnpoints(); Waypoint[] spawnpoints = m_arena.spawnpoints();
m_spawnIdx %= spawnpoints.length; m_spawnIdx %= spawnpoints.length;
//m_log.fine("Spawning " + type + " at " + spawnpoints[m_spawnIdx]); //m_log.fine("Spawning " + type + " at " + spawnpoints[m_spawnIdx]);
Entity newMob = m_arena.getWorld().spawnEntity(spawnpoints[m_spawnIdx].getLocation(), type); Waypoint thisSpawner = spawnpoints[m_spawnIdx];
Location loc = new Location(m_world, thisSpawner.getX(), thisSpawner.getY(), thisSpawner.getZ());
Entity newMob = m_world.spawnEntity(loc, type);
LivingEntity livingMob = (LivingEntity)newMob; LivingEntity livingMob = (LivingEntity)newMob;
livingMob.setRemoveWhenFarAway(false); livingMob.setRemoveWhenFarAway(false);
m_manager.addEntity(livingMob); m_manager.addEntity(livingMob);

View File

@ -3,8 +3,6 @@ package gg.malloc.defense.model;
import org.bukkit.World; import org.bukkit.World;
public interface Arena { public interface Arena {
World getWorld(); Waypoint[] spawnpoints();
String name(); Waypoint bombTarget();
Spawnpoint[] spawnpoints();
Spawnpoint bombTarget();
} }

View File

@ -1,8 +1,6 @@
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) {}
} }

View File

@ -1,9 +0,0 @@
package gg.malloc.defense.model;
import org.bukkit.Location;
public interface Spawnpoint {
Location getLocation();
String getName();
String getID();
}

View File

@ -0,0 +1,8 @@
package gg.malloc.defense.model;
public interface Waypoint {
double getX();
double getY();
double getZ();
String getName();
}

View File

@ -0,0 +1,16 @@
maps:
quarry:
target:
x: -20.0
y: 80.0
z: 31.0
spawnpoints:
- x: -15.0
y: 80.0
z: -46.0
- x: -1.0
y: 80.0
z: -45.0
- x: 12.0
y: 81.0
z: -42.0

View File

@ -4,11 +4,17 @@ api-version: 1.18
main: gg.malloc.defense.Plugin main: gg.malloc.defense.Plugin
depend: [] depend: []
commands: commands:
ready:
description: Mark yourself as ready for the next wave
list:
description: List games
join:
description: Join a game
leave:
description: Leave a game
setstage: setstage:
description: Sets a game stage description: Sets a game stage
addplayer: addplayer:
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