Reimplement wave AI, split out some UI code into a ui module, update TODO
This commit is contained in:
parent
2f63695ee9
commit
75debe1905
24
TODO.md
24
TODO.md
@ -2,19 +2,33 @@
|
|||||||
|
|
||||||
[X] Mobs spawn in waves
|
[X] Mobs spawn in waves
|
||||||
[X] Mobs move towards goal
|
[X] Mobs move towards goal
|
||||||
[ ] Mobs carry bomb to goal
|
[X] Mobs carry bomb to goal
|
||||||
[X] Mobs arm bomb
|
[X] Mobs arm bomb
|
||||||
[X] Bomb explodes
|
[X] Bomb explodes
|
||||||
|
|
||||||
|
# Malloc Beta
|
||||||
|
|
||||||
|
[ ] Join games
|
||||||
|
[ ] Leave games
|
||||||
|
[ ] One arena config
|
||||||
|
|
||||||
|
# Scaled waves
|
||||||
|
|
||||||
|
[ ] Weaker mobs, more of them
|
||||||
|
[ ] Mob categories
|
||||||
|
[ ] Spawnpoint categories
|
||||||
|
|
||||||
# UX
|
# UX
|
||||||
|
|
||||||
[X] Wave boss bar
|
[X] Wave boss bar
|
||||||
[X] Mob count boss bar
|
[X] Mob count boss bar
|
||||||
[X] Stage titles
|
[X] Stage titles
|
||||||
[ ] EXPLOSIONS
|
[X] EXPLOSIONS
|
||||||
|
[ ] Target catches on fire more it is lit
|
||||||
[ ] Colored titles
|
[ ] Colored titles
|
||||||
[ ] Clickable /ready in chat
|
[ ] Clickable /ready in chat
|
||||||
[ ] Sidebar
|
[ ] Sidebar
|
||||||
|
[ ] List of mobs in next wave
|
||||||
|
|
||||||
# Social
|
# Social
|
||||||
|
|
||||||
@ -27,9 +41,9 @@
|
|||||||
# Mechanics
|
# Mechanics
|
||||||
|
|
||||||
[ ] Coin drops
|
[ ] Coin drops
|
||||||
[ ] Mob tracking should prioritize bomb
|
[X] Mob tracking should prioritize bomb
|
||||||
[ ] Mobs recover dropped bombs
|
[X] Mobs recover dropped bombs
|
||||||
[ ] Bomb carriers are slower
|
[X] Bomb carriers are slower
|
||||||
[ ] Bonus coins for complete coin pickup
|
[ ] Bonus coins for complete coin pickup
|
||||||
[ ] Infinite weapons + armor
|
[ ] Infinite weapons + armor
|
||||||
[ ] Ammo/health spawns
|
[ ] Ammo/health spawns
|
||||||
|
@ -40,14 +40,6 @@ public class GameEventHandler implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityDamage(EntityDamageEvent evt) {
|
public void onEntityDamage(EntityDamageEvent evt) {
|
||||||
if (evt.getEntity() instanceof Player) {
|
|
||||||
Player player = (Player)evt.getEntity();
|
|
||||||
if (player.getHealth() - evt.getFinalDamage() <= 0) {
|
|
||||||
evt.setCancelled(true);
|
|
||||||
m_runner.handlePlayerDeath(player);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_runner.handleEntityDamage(evt);
|
m_runner.handleEntityDamage(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -22,21 +22,24 @@ public class SetStageCommand implements CommandExecutor {
|
|||||||
GameRunner runner = m_plugin.getRunnerForWorld(world);
|
GameRunner runner = m_plugin.getRunnerForWorld(world);
|
||||||
String stateName = args[0].toLowerCase();
|
String stateName = args[0].toLowerCase();
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
if (stateName.equals("idle")) {
|
GameRunner.Stage decodedStage = null;
|
||||||
ret = runner.requestTransition(GameRunner.Stage.Idle);
|
GameRunner.Stage stages[] = GameRunner.Stage.Idle.getDeclaringClass().getEnumConstants();
|
||||||
} else if (stateName.equals("warmup")) {
|
for(GameRunner.Stage stage : stages) {
|
||||||
ret = runner.requestTransition(GameRunner.Stage.Warmup);
|
if (stage.toString().toLowerCase().equals(stateName)) {
|
||||||
} else if (stateName.equals("playing")) {
|
decodedStage = stage;
|
||||||
ret = runner.requestTransition(GameRunner.Stage.Playing);
|
break;
|
||||||
} else if (stateName.equals("gameover")) {
|
}
|
||||||
ret = runner.requestTransition(GameRunner.Stage.GameOver);
|
}
|
||||||
|
if (decodedStage != null) {
|
||||||
|
sender.sendMessage("Requesting transition to " + decodedStage);
|
||||||
|
if (!runner.requestTransition(decodedStage)) {
|
||||||
|
sender.sendMessage("Could not transition to " + decodedStage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage("Unknown state " + stateName);
|
sender.sendMessage("Unknown state " + stateName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ret) {
|
|
||||||
sender.sendMessage("Could not set state to " + stateName);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/main/java/gg/malloc/defense/engine/BombFuse.java
Normal file
30
src/main/java/gg/malloc/defense/engine/BombFuse.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
public class BombFuse {
|
||||||
|
int m_bombFuseCount = 0;
|
||||||
|
int m_bombFuseTarget = 10;
|
||||||
|
|
||||||
|
public double getProgress() {
|
||||||
|
return Math.max(0.0, Math.min(1.0, (double)m_bombFuseCount / (double)m_bombFuseTarget));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLit() {
|
||||||
|
return m_bombFuseCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExploded() {
|
||||||
|
return m_bombFuseCount > m_bombFuseTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
m_bombFuseCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickLit() {
|
||||||
|
m_bombFuseCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickDecay() {
|
||||||
|
m_bombFuseCount -= 1;
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,35 @@
|
|||||||
package gg.malloc.defense.engine;
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
import gg.malloc.defense.model.Game;
|
|
||||||
import gg.malloc.defense.model.Arena;
|
import gg.malloc.defense.model.Arena;
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
import gg.malloc.defense.model.Spawner;
|
import gg.malloc.defense.model.Spawner;
|
||||||
import gg.malloc.defense.model.Wave;
|
|
||||||
|
import gg.malloc.defense.ui.BombCarrier;
|
||||||
|
import gg.malloc.defense.ui.BossBars;
|
||||||
|
|
||||||
import gg.malloc.defense.Plugin;
|
import gg.malloc.defense.Plugin;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Particle;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.SoundCategory;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
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.entity.EntityTargetEvent;
|
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent;
|
||||||
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.LivingEntity;
|
|
||||||
import org.bukkit.entity.ArmorStand;
|
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
|
||||||
|
|
||||||
public class GameRunner {
|
public class GameRunner {
|
||||||
Arena m_arena;
|
Arena m_arena;
|
||||||
@ -32,18 +37,11 @@ public class GameRunner {
|
|||||||
Stage m_stage;
|
Stage m_stage;
|
||||||
Plugin m_plugin;
|
Plugin m_plugin;
|
||||||
|
|
||||||
BossBar m_gameBar = 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;
|
|
||||||
|
|
||||||
MobManager m_mobs;
|
MobManager m_mobs;
|
||||||
WaveManager m_waves;
|
WaveManager m_waves;
|
||||||
PlayerManager m_players;
|
PlayerManager m_players;
|
||||||
|
|
||||||
LivingEntity m_bombTarget = null;
|
BombFuse m_bombFuse;
|
||||||
int m_bombHP = 100;
|
|
||||||
|
|
||||||
Logger m_log;
|
Logger m_log;
|
||||||
|
|
||||||
@ -55,126 +53,96 @@ public class GameRunner {
|
|||||||
GameOver
|
GameOver
|
||||||
}
|
}
|
||||||
|
|
||||||
class WaveManager {
|
TickTask m_fuseTask;
|
||||||
int m_currentWaveNum = 0;
|
TickTask m_countdownTask;
|
||||||
int m_currentBatch = 0;
|
TickTask m_bombSmokeTask;
|
||||||
Wave m_currentWave = null;
|
TickTask m_bombCrackleTask;
|
||||||
Game m_game;
|
|
||||||
|
|
||||||
WaveManager(Game game) {
|
|
||||||
m_game = game;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
m_currentWaveNum = 0;
|
|
||||||
m_currentBatch = 0;
|
|
||||||
m_currentWave = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wave currentWave() {
|
|
||||||
return m_currentWave;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentWaveNum() {
|
|
||||||
return m_currentWaveNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentBatchNum() {
|
|
||||||
return m_currentBatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
double progress() {
|
|
||||||
return (double)m_currentWaveNum / (double)m_game.getWaveCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isLastWave() {
|
|
||||||
return m_currentWaveNum >= m_game.getWaveCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isLastBatch() {
|
|
||||||
return m_currentBatch >= m_currentWave.batchCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nextBatch() {
|
|
||||||
m_currentBatch += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void next() {
|
|
||||||
m_currentWaveNum += 1;
|
|
||||||
m_currentBatch = 0;
|
|
||||||
m_currentWave = m_game.getWave(m_currentWaveNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
BossBars m_bars;
|
||||||
|
|
||||||
public GameRunner(Plugin plugin, Game game, Arena arena) {
|
public GameRunner(Plugin plugin, Game game, Arena arena) {
|
||||||
m_plugin = plugin;
|
m_plugin = plugin;
|
||||||
m_game = game;
|
m_game = game;
|
||||||
m_arena = arena;
|
m_arena = arena;
|
||||||
m_stage = Stage.Idle;
|
m_stage = Stage.Idle;
|
||||||
m_gameBar.setVisible(true);
|
m_mobs = new MobManager();
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
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();
|
||||||
|
m_bombFuse = new BombFuse();
|
||||||
|
m_bars = new BossBars(this, m_game, m_waves, m_players, m_bombFuse, m_mobs);
|
||||||
m_log = m_plugin.getLogger();
|
m_log = m_plugin.getLogger();
|
||||||
|
|
||||||
|
m_fuseTask = new TickTask(m_plugin, () -> {
|
||||||
|
if (m_bombFuse.isLit()) {
|
||||||
|
m_bombFuse.tickDecay();
|
||||||
|
m_bars.update();
|
||||||
|
}
|
||||||
|
}, 80);
|
||||||
|
|
||||||
|
m_countdownTask = new TickTask(m_plugin, () -> {
|
||||||
|
if (m_warmupCountdown == 0) {
|
||||||
|
requestTransition(Stage.Playing);
|
||||||
|
} else {
|
||||||
|
m_bars.update();
|
||||||
|
broadcastMessage("Starting game in " + m_warmupCountdown);
|
||||||
|
m_warmupCountdown--;
|
||||||
|
m_bars.setCountdownProgress((double)m_warmupCountdown / 10.0);
|
||||||
|
m_bars.update();
|
||||||
|
}
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
m_bombSmokeTask = new TickTask(m_plugin, () -> {
|
||||||
|
Location targetLoc = m_arena.bombTarget().getLocation();
|
||||||
|
m_arena.getWorld().spawnParticle(Particle.SMOKE_LARGE, targetLoc, 35, 4, 2, 4);
|
||||||
|
m_arena.getWorld().spawnParticle(Particle.SMALL_FLAME, targetLoc, 30, 3, 2, 3);
|
||||||
|
}, 5);
|
||||||
|
m_bombCrackleTask = new TickTask(m_plugin, () -> {
|
||||||
|
Location targetLoc = m_arena.bombTarget().getLocation();
|
||||||
|
m_arena.getWorld().playSound(targetLoc, Sound.BLOCK_CAMPFIRE_CRACKLE, SoundCategory.NEUTRAL, 1.0f, 1.0f);
|
||||||
|
}, 35);
|
||||||
}
|
}
|
||||||
|
|
||||||
int m_warmupCountdown = 0;
|
int m_warmupCountdown = 0;
|
||||||
|
|
||||||
public void handleEntityRetargeting(EntityTargetEvent evt) {
|
public void handleEntityRetargeting(EntityTargetEvent evt) {
|
||||||
if (m_mobs.contains(evt.getEntity()) && evt.getReason() == EntityTargetEvent.TargetReason.FORGOT_TARGET) {
|
m_mobs.handleEntityRetarget(evt);
|
||||||
evt.setTarget(m_bombTarget);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleEntityDamage(EntityDamageEvent evt) {
|
public void handleEntityDamage(EntityDamageEvent 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);
|
m_mobs.handleEntityDamage(evt);
|
||||||
|
if (m_mobs.bombWasHit()) {
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
m_log.info("Target attacked!");
|
||||||
|
entityEvt.getDamager().setGlowing(true);
|
||||||
|
m_bombFuse.tickLit();
|
||||||
|
m_arena.getWorld().playSound(m_arena.bombTarget().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.5f, 0.9f);
|
||||||
|
m_bars.update();
|
||||||
|
if (m_bombFuse.isExploded()) {
|
||||||
|
m_arena.getWorld().strikeLightningEffect(m_arena.bombTarget().getLocation());
|
||||||
|
m_arena.getWorld().playSound(m_arena.bombTarget().getLocation(), 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);
|
||||||
|
requestTransition(Stage.GameOver);
|
||||||
|
m_bombSmokeTask.start();
|
||||||
|
m_bombCrackleTask.start();
|
||||||
}
|
}
|
||||||
|
} else if (evt.getEntity() instanceof Player) {
|
||||||
|
Player player = (Player)evt.getEntity();
|
||||||
|
if (m_players.contains(player) && player.getHealth() - evt.getFinalDamage() <= 0) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
handlePlayerDeath(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void countdownTick() {
|
|
||||||
if (m_warmupCountdown == 0) {
|
|
||||||
requestTransition(Stage.Playing);
|
|
||||||
} else {
|
|
||||||
updateMobBars();
|
|
||||||
broadcastMessage("Starting game in " + m_warmupCountdown);
|
|
||||||
m_warmupCountdown--;
|
|
||||||
m_countdownTask = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {
|
|
||||||
countdownTick();
|
|
||||||
}, 20);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enterIdle() {
|
private boolean enterIdle() {
|
||||||
m_bombHP = 100;
|
m_bombFuse.reset();
|
||||||
m_waves.reset();
|
m_waves.reset();
|
||||||
m_mobs.clear();
|
m_mobs.clear();
|
||||||
|
m_bombSmokeTask.stop();
|
||||||
|
m_bombCrackleTask.stop();
|
||||||
m_players.requestTransitionForAll(PlayerManager.State.Idle);
|
m_players.requestTransitionForAll(PlayerManager.State.Idle);
|
||||||
if (m_bombTarget != null) {
|
m_fuseTask.stop();
|
||||||
m_bombTarget.remove();
|
m_countdownTask.stop();
|
||||||
m_bombTarget = null;
|
|
||||||
}
|
|
||||||
if (m_countdownTask != null) {
|
|
||||||
m_countdownTask.cancel();
|
|
||||||
m_countdownTask = null;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,25 +172,22 @@ public class GameRunner {
|
|||||||
|
|
||||||
private boolean enterCountdown() {
|
private boolean enterCountdown() {
|
||||||
m_warmupCountdown = 10;
|
m_warmupCountdown = 10;
|
||||||
countdownTick();
|
m_bars.setCountdownProgress(1.0);
|
||||||
|
m_countdownTask.start();
|
||||||
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_mobs.spawnTarget(m_arena.bombTarget().getLocation());
|
||||||
m_bombTarget = (LivingEntity)m_arena.getWorld().spawnEntity(m_arena.bombTarget().getLocation(), EntityType.ARMOR_STAND);
|
// TODO: Set helmet with custom model data
|
||||||
}
|
|
||||||
ArmorStand bombStand = (ArmorStand)m_bombTarget;
|
|
||||||
bombStand.setCustomName("Bomb Target");
|
|
||||||
bombStand.setVisible(true);
|
|
||||||
bombStand.setCustomNameVisible(true);
|
|
||||||
bombStand.setGlowing(true);
|
|
||||||
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_countdownTask.stop();
|
||||||
|
m_fuseTask.start();
|
||||||
spawnNextBatch();
|
spawnNextBatch();
|
||||||
broadcastTitle("Wave " + m_waves.currentWaveNum());
|
broadcastTitle("Wave " + m_waves.currentWaveNum());
|
||||||
return true;
|
return true;
|
||||||
@ -230,80 +195,28 @@ public class GameRunner {
|
|||||||
|
|
||||||
private boolean enterGameOver() {
|
private boolean enterGameOver() {
|
||||||
broadcastTitle("Game Over!");
|
broadcastTitle("Game Over!");
|
||||||
if (m_countdownTask != null) {
|
broadcastMessage("The bomb blew up!");
|
||||||
m_countdownTask.cancel();
|
m_countdownTask.stop();
|
||||||
m_countdownTask = null;
|
m_fuseTask.stop();
|
||||||
}
|
|
||||||
m_mobs.clear();
|
m_mobs.clear();
|
||||||
for(Player p : m_players.getPlayers()) {
|
for(Player p : new ArrayList<Player>(m_players.getPlayers())) {
|
||||||
removePlayer(p);
|
removePlayer(p);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMobBars() {
|
public Stage getStage() {
|
||||||
m_gameBar.setVisible(true);
|
return m_stage;
|
||||||
switch(m_stage) {
|
|
||||||
case Idle:
|
|
||||||
m_gameBar.setProgress(1.0);
|
|
||||||
m_gameBar.setTitle("Waiting for players...");
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
break;
|
|
||||||
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.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
m_waveBar.setVisible(true);
|
|
||||||
m_waveBar.setColor(BarColor.YELLOW);
|
|
||||||
m_waveBar.setTitle("Wave starting!");
|
|
||||||
m_waveBar.setProgress((double)m_warmupCountdown / (double)10);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
break;
|
|
||||||
case Playing:
|
|
||||||
m_gameBar.setProgress(m_waves.progress());
|
|
||||||
m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
|
||||||
m_gameBar.setColor(BarColor.PURPLE);
|
|
||||||
if (m_mobs.createdMobs() > 0) {
|
|
||||||
m_waveBar.setVisible(true);
|
|
||||||
m_waveBar.setColor(BarColor.GREEN);
|
|
||||||
m_waveBar.setTitle("Mobs remaining: " + m_mobs.remainingMobs());
|
|
||||||
m_waveBar.setProgress(m_mobs.progress());
|
|
||||||
} else {
|
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
}
|
|
||||||
m_bombBar.setVisible(true);
|
|
||||||
m_bombBar.setProgress((double)m_bombHP / (double)100);
|
|
||||||
break;
|
|
||||||
case GameOver:
|
|
||||||
m_gameBar.setColor(BarColor.RED);
|
|
||||||
m_gameBar.setProgress(1.0);
|
|
||||||
m_gameBar.setTitle("Game Over!");
|
|
||||||
m_waveBar.setVisible(false);
|
|
||||||
m_bombBar.setVisible(false);
|
|
||||||
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, m_players, m_bombTarget);
|
Spawner spawner = new GameSpawner(m_arena, m_mobs, m_players);
|
||||||
m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum());
|
m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum());
|
||||||
updateMobBars();
|
m_bars.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public 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_arena.getWorld().strikeLightningEffect(player.getLocation());
|
||||||
if (!m_players.isAnyoneAlive()) {
|
if (!m_players.isAnyoneAlive()) {
|
||||||
@ -316,9 +229,9 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void handleEntityDeath(Entity entity) {
|
public void handleEntityDeath(Entity entity) {
|
||||||
|
boolean wasCarrier = m_mobs.isBombCarrier(entity);
|
||||||
if (m_mobs.killMob(entity)) {
|
if (m_mobs.killMob(entity)) {
|
||||||
broadcastMessage("Killed game entity " + entity);
|
m_bars.update();
|
||||||
updateMobBars();
|
|
||||||
if (m_mobs.remainingMobs() <= 3) {
|
if (m_mobs.remainingMobs() <= 3) {
|
||||||
m_log.info("Starting next batch!");
|
m_log.info("Starting next batch!");
|
||||||
if (m_waves.isLastBatch()) {
|
if (m_waves.isLastBatch()) {
|
||||||
@ -381,7 +294,7 @@ public class GameRunner {
|
|||||||
if (attemptTransition(stage)) {
|
if (attemptTransition(stage)) {
|
||||||
m_log.info("Game transition: " + m_stage + " -> " + stage);
|
m_log.info("Game transition: " + m_stage + " -> " + stage);
|
||||||
m_stage = stage;
|
m_stage = stage;
|
||||||
updateMobBars();
|
m_bars.update();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
m_log.severe("Failed to complete transition: " + m_stage + " -> " + stage);
|
m_log.severe("Failed to complete transition: " + m_stage + " -> " + stage);
|
||||||
@ -389,10 +302,11 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addPlayer(Player p) {
|
public void addPlayer(Player p) {
|
||||||
|
if (m_players.contains(p)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_players.addPlayer(p);
|
m_players.addPlayer(p);
|
||||||
m_gameBar.addPlayer(p);
|
m_bars.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);
|
||||||
@ -405,8 +319,7 @@ public class GameRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removePlayer(Player p) {
|
public void removePlayer(Player p) {
|
||||||
m_gameBar.removePlayer(p);
|
m_bars.removePlayer(p);
|
||||||
m_waveBar.removePlayer(p);
|
|
||||||
m_players.removePlayer(p);
|
m_players.removePlayer(p);
|
||||||
if (m_players.isEmpty()) {
|
if (m_players.isEmpty()) {
|
||||||
requestTransition(Stage.Idle);
|
requestTransition(Stage.Idle);
|
||||||
@ -429,8 +342,4 @@ public class GameRunner {
|
|||||||
p.sendMessage(string);
|
p.sendMessage(string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerSpawnedMob(Entity entity) {
|
|
||||||
m_mobs.addEntity(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,32 +8,44 @@ 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.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
|
||||||
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;
|
PlayerManager m_players;
|
||||||
LivingEntity m_bombTarget;
|
|
||||||
int m_spawnIdx = 0;
|
int m_spawnIdx = 0;
|
||||||
|
|
||||||
public GameSpawner(Arena arena, MobManager manager, PlayerManager players, LivingEntity bombTarget) {
|
public GameSpawner(Arena arena, MobManager manager, PlayerManager players) {
|
||||||
m_arena = arena;
|
m_arena = arena;
|
||||||
m_manager = manager;
|
m_manager = manager;
|
||||||
m_players = players;
|
m_players = players;
|
||||||
m_bombTarget = bombTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entity spawnMob(EntityType type) {
|
public LivingEntity spawnBombCarrier(EntityType type) {
|
||||||
|
if (m_manager.bombCount() == 0) {
|
||||||
|
return m_manager.addBombCarrier(spawnMob(type));
|
||||||
|
} else {
|
||||||
|
return spawnMob(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LivingEntity spawnMob(EntityType type) {
|
||||||
Spawnpoint[] spawnpoints = m_arena.spawnpoints();
|
Spawnpoint[] 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);
|
Entity newMob = m_arena.getWorld().spawnEntity(spawnpoints[m_spawnIdx].getLocation(), type);
|
||||||
LivingEntity livingMob = (LivingEntity)newMob;
|
LivingEntity livingMob = (LivingEntity)newMob;
|
||||||
livingMob.setRemoveWhenFarAway(false);
|
livingMob.setRemoveWhenFarAway(false);
|
||||||
m_manager.addEntity(newMob);
|
m_manager.addEntity(livingMob);
|
||||||
m_spawnIdx += 1;
|
m_spawnIdx += 1;
|
||||||
((Mob)newMob).setTarget(m_bombTarget);
|
return livingMob;
|
||||||
return newMob;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,48 +2,61 @@ package gg.malloc.defense.engine;
|
|||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.ArmorStand;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Mob;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.event.entity.EntityTargetEvent;
|
||||||
|
|
||||||
import gg.malloc.defense.model.Game;
|
import gg.malloc.defense.model.Game;
|
||||||
|
|
||||||
|
import gg.malloc.defense.ui.BombCarrier;
|
||||||
|
|
||||||
public class MobManager {
|
public class MobManager {
|
||||||
HashSet<Entity> m_livingMobs = new HashSet<>();
|
HashSet<LivingEntity> m_livingMobs = new HashSet<>();
|
||||||
|
HashSet<LivingEntity> m_bombCarriers = new HashSet<>();
|
||||||
|
HashSet<LivingEntity> m_droppedBombs = new HashSet<>();
|
||||||
|
|
||||||
int m_createdMobs = 0;
|
int m_createdMobs = 0;
|
||||||
int m_killedMobs = 0;
|
int m_killedMobs = 0;
|
||||||
|
|
||||||
Game m_game;
|
|
||||||
|
|
||||||
MobManager(Game game) {
|
|
||||||
m_game = game;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(Entity entity) {
|
public boolean contains(Entity entity) {
|
||||||
return m_livingMobs.contains(entity);
|
return m_livingMobs.contains(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBombCarrier(Entity entity) {
|
||||||
|
return m_bombCarriers.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;
|
||||||
|
if (m_bombCarriers.remove(entity)) {
|
||||||
|
dropBomb(entity.getLocation());
|
||||||
|
}
|
||||||
return m_livingMobs.remove(entity);
|
return m_livingMobs.remove(entity);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
for(Entity e : m_livingMobs) {
|
for(LivingEntity e : m_livingMobs) {
|
||||||
e.remove();
|
e.remove();
|
||||||
}
|
}
|
||||||
|
for(LivingEntity e : m_droppedBombs) {
|
||||||
|
e.remove();
|
||||||
|
}
|
||||||
|
if (m_bombTarget != null) {
|
||||||
|
m_bombTarget.remove();
|
||||||
|
}
|
||||||
|
m_bombTarget = null;
|
||||||
|
m_droppedBombs.clear();
|
||||||
m_livingMobs.clear();
|
m_livingMobs.clear();
|
||||||
|
m_bombCarriers.clear();
|
||||||
m_createdMobs = 0;
|
m_createdMobs = 0;
|
||||||
m_killedMobs = 0;
|
m_killedMobs = 0;
|
||||||
}
|
}
|
||||||
@ -64,13 +77,106 @@ public class MobManager {
|
|||||||
return (double)m_killedMobs / (double)m_createdMobs;
|
return (double)m_killedMobs / (double)m_createdMobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEntity(Entity entity) {
|
public void addEntity(LivingEntity entity) {
|
||||||
//m_log.fine("Registered new mob " + entity);
|
|
||||||
m_livingMobs.add(entity);
|
m_livingMobs.add(entity);
|
||||||
|
Mob asMob = (Mob)entity;
|
||||||
|
if (m_droppedBombs.size() > 0) {
|
||||||
|
asMob.setTarget(m_droppedBombs.iterator().next());
|
||||||
|
} else {
|
||||||
|
asMob.setTarget(m_bombTarget);
|
||||||
|
}
|
||||||
m_createdMobs += 1;
|
m_createdMobs += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int bombCount() {
|
||||||
|
return m_bombCarriers.size() + m_droppedBombs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LivingEntity addBombCarrier(LivingEntity entity) {
|
||||||
|
m_bombCarriers.add(entity);
|
||||||
|
((Mob)entity).setTarget(m_bombTarget);
|
||||||
|
return new BombCarrier(entity).inHand();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBombCarrier(LivingEntity entity) {
|
||||||
|
m_bombCarriers.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean empty() {
|
public boolean empty() {
|
||||||
return m_livingMobs.size() == 0;
|
return m_livingMobs.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LivingEntity dropBomb(Location location) {
|
||||||
|
ArmorStand droppedBomb = (ArmorStand)(new BombCarrier((LivingEntity)location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND)).onHead());
|
||||||
|
droppedBomb.setCustomName("The Bomb");
|
||||||
|
droppedBomb.setVisible(true);
|
||||||
|
droppedBomb.setCustomNameVisible(true);
|
||||||
|
droppedBomb.setGlowing(true);
|
||||||
|
m_droppedBombs.add(droppedBomb);
|
||||||
|
for(LivingEntity ent : m_livingMobs) {
|
||||||
|
if (!m_bombCarriers.contains(ent)) {
|
||||||
|
((Mob)ent).setTarget(droppedBomb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return droppedBomb;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean m_bombWasHit = false;
|
||||||
|
|
||||||
|
public boolean bombWasHit() {
|
||||||
|
return m_bombWasHit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEntityDamage(EntityDamageEvent evt) {
|
||||||
|
Entity entity = evt.getEntity();
|
||||||
|
m_bombWasHit = false;
|
||||||
|
|
||||||
|
if (entity == m_bombTarget && evt instanceof EntityDamageByEntityEvent) {
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
evt.setCancelled(true);
|
||||||
|
if (m_bombCarriers.contains(entityEvt.getDamager())) {
|
||||||
|
m_bombWasHit = true;
|
||||||
|
}
|
||||||
|
} if (m_droppedBombs.contains(entity)) {
|
||||||
|
evt.setCancelled(true);
|
||||||
|
EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt;
|
||||||
|
if (m_livingMobs.contains(entityEvt.getDamager())) {
|
||||||
|
evt.getEntity().remove();
|
||||||
|
m_droppedBombs.remove(evt.getEntity());
|
||||||
|
addBombCarrier((LivingEntity)entityEvt.getDamager());
|
||||||
|
for(LivingEntity ent : m_livingMobs) {
|
||||||
|
if (!m_bombCarriers.contains(ent)) {
|
||||||
|
((Mob)ent).setTarget(m_bombTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m_livingMobs.contains(evt.getEntity())) {
|
||||||
|
//m_game.onMobDamaged(evt.getEntity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LivingEntity m_bombTarget;
|
||||||
|
|
||||||
|
public LivingEntity spawnTarget(Location location) {
|
||||||
|
assert(m_bombTarget == null);
|
||||||
|
m_bombTarget = (LivingEntity)location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND);
|
||||||
|
ArmorStand bombStand = (ArmorStand)m_bombTarget;
|
||||||
|
bombStand.setCustomName("Bomb Target");
|
||||||
|
bombStand.setVisible(true);
|
||||||
|
bombStand.setCustomNameVisible(true);
|
||||||
|
bombStand.setGlowing(true);
|
||||||
|
return m_bombTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleEntityRetarget(EntityTargetEvent evt) {
|
||||||
|
if (m_livingMobs.contains(evt.getEntity())) {
|
||||||
|
if (m_bombCarriers.contains(evt.getEntity())) {
|
||||||
|
evt.setTarget(m_bombTarget);
|
||||||
|
} if (m_droppedBombs.size() > 0) {
|
||||||
|
evt.setTarget(m_droppedBombs.iterator().next());
|
||||||
|
} else if (evt.getReason() == EntityTargetEvent.TargetReason.FORGOT_TARGET) {
|
||||||
|
evt.setTarget(m_bombTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,10 @@ public class PlayerManager {
|
|||||||
return allSuccess;
|
return allSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean contains(Player player) {
|
||||||
|
return m_playerStates.containsKey(player);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean requestTransition(Player player, State toState) {
|
public boolean requestTransition(Player player, State toState) {
|
||||||
State currentState = m_playerStates.get(player);
|
State currentState = m_playerStates.get(player);
|
||||||
if (currentState == toState) {
|
if (currentState == toState) {
|
||||||
|
40
src/main/java/gg/malloc/defense/engine/TickTask.java
Normal file
40
src/main/java/gg/malloc/defense/engine/TickTask.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.Plugin;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
public class TickTask {
|
||||||
|
Runnable m_runnable;
|
||||||
|
int m_interval;
|
||||||
|
BukkitTask m_task = null;
|
||||||
|
Plugin m_plugin;
|
||||||
|
boolean m_cancelled = false;
|
||||||
|
|
||||||
|
public TickTask(Plugin plugin, Runnable runnable, int interval) {
|
||||||
|
m_plugin = plugin;
|
||||||
|
m_runnable = runnable;
|
||||||
|
m_interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
m_cancelled = false;
|
||||||
|
if (m_task == null) {
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tick() {
|
||||||
|
m_runnable.run();
|
||||||
|
if (!m_cancelled) {
|
||||||
|
m_task = m_plugin.getServer().getScheduler().runTaskLater(m_plugin, () -> {tick();}, m_interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
m_cancelled = true;
|
||||||
|
if (m_task != null) {
|
||||||
|
m_task.cancel();
|
||||||
|
m_task = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/main/java/gg/malloc/defense/engine/WaveManager.java
Normal file
55
src/main/java/gg/malloc/defense/engine/WaveManager.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Wave;
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
|
|
||||||
|
public class WaveManager {
|
||||||
|
int m_currentWaveNum = 0;
|
||||||
|
int m_currentBatch = 0;
|
||||||
|
Wave m_currentWave = null;
|
||||||
|
Game m_game;
|
||||||
|
|
||||||
|
public WaveManager(Game game) {
|
||||||
|
m_game = game;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
m_currentWaveNum = 0;
|
||||||
|
m_currentBatch = 0;
|
||||||
|
m_currentWave = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Wave currentWave() {
|
||||||
|
return m_currentWave;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int currentWaveNum() {
|
||||||
|
return m_currentWaveNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int currentBatchNum() {
|
||||||
|
return m_currentBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double progress() {
|
||||||
|
return (double)m_currentWaveNum / (double)m_game.getWaveCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastWave() {
|
||||||
|
return m_currentWaveNum >= m_game.getWaveCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastBatch() {
|
||||||
|
return m_currentBatch >= m_currentWave.batchCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void nextBatch() {
|
||||||
|
m_currentBatch += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void next() {
|
||||||
|
m_currentWaveNum += 1;
|
||||||
|
m_currentBatch = 0;
|
||||||
|
m_currentWave = m_game.getWave(m_currentWaveNum);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,9 @@ import gg.malloc.defense.model.Spawner;
|
|||||||
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Mob;
|
||||||
|
import org.bukkit.attribute.AttributeModifier;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@ -55,6 +58,8 @@ public class ScaledWaves implements Game {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void spawnBatch(Spawner spawner, int batch) {
|
public void spawnBatch(Spawner spawner, int batch) {
|
||||||
|
double healthScale = -0.5;
|
||||||
|
AttributeModifier modifier = new AttributeModifier("Scaled Health", healthScale, AttributeModifier.Operation.MULTIPLY_SCALAR_1);
|
||||||
assert(m_mobsPerBatch > 0);
|
assert(m_mobsPerBatch > 0);
|
||||||
for(int i = 0; i < m_mobsPerBatch; i++) {
|
for(int i = 0; i < m_mobsPerBatch; i++) {
|
||||||
EntityType selectedType = null;
|
EntityType selectedType = null;
|
||||||
@ -67,8 +72,9 @@ public class ScaledWaves implements Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(selectedType != null);
|
assert(selectedType != null);
|
||||||
Entity newMob = spawner.spawnMob(selectedType);
|
Mob newMob = (Mob)spawner.spawnBombCarrier(selectedType);
|
||||||
newMob.setCustomName("Mob " + i + "/" + m_mobsPerBatch);
|
newMob.setCustomName("Mob " + i + "/" + m_mobsPerBatch);
|
||||||
|
newMob.getAttribute(Attribute.GENERIC_MAX_HEALTH).addModifier(modifier);
|
||||||
}
|
}
|
||||||
if (m_hasRavager) {
|
if (m_hasRavager) {
|
||||||
Entity newMob = spawner.spawnMob(EntityType.RAVAGER);
|
Entity newMob = spawner.spawnMob(EntityType.RAVAGER);
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package gg.malloc.defense.model;
|
package gg.malloc.defense.model;
|
||||||
|
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
|
||||||
public interface Spawner {
|
public interface Spawner {
|
||||||
Entity spawnMob(EntityType type);
|
LivingEntity spawnMob(EntityType type);
|
||||||
|
LivingEntity spawnBombCarrier(EntityType type);
|
||||||
}
|
}
|
||||||
|
37
src/main/java/gg/malloc/defense/ui/BombCarrier.java
Normal file
37
src/main/java/gg/malloc/defense/ui/BombCarrier.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
public class BombCarrier {
|
||||||
|
LivingEntity m_entity;
|
||||||
|
|
||||||
|
public BombCarrier(LivingEntity entity) {
|
||||||
|
m_entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LivingEntity inHand() {
|
||||||
|
EntityEquipment equipment = m_entity.getEquipment();
|
||||||
|
equipment.setItemInOffHand(makeBombHelmet());
|
||||||
|
//equipment.setItemInOffHandDropChance(0.0f);
|
||||||
|
return m_entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LivingEntity onHead() {
|
||||||
|
EntityEquipment equipment = m_entity.getEquipment();
|
||||||
|
equipment.setHelmet(makeBombHelmet());
|
||||||
|
//equipment.setHelmetDropChance(0.0f);
|
||||||
|
return m_entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStack makeBombHelmet() {
|
||||||
|
ItemStack bombItem = new ItemStack(Material.CARVED_PUMPKIN);
|
||||||
|
ItemMeta meta = bombItem.getItemMeta();
|
||||||
|
meta.setCustomModelData(33197);
|
||||||
|
bombItem.setItemMeta(meta);
|
||||||
|
return bombItem;
|
||||||
|
}
|
||||||
|
}
|
116
src/main/java/gg/malloc/defense/ui/BossBars.java
Normal file
116
src/main/java/gg/malloc/defense/ui/BossBars.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package gg.malloc.defense.ui;
|
||||||
|
|
||||||
|
import gg.malloc.defense.engine.GameRunner;
|
||||||
|
import gg.malloc.defense.engine.WaveManager;
|
||||||
|
import gg.malloc.defense.engine.PlayerManager;
|
||||||
|
import gg.malloc.defense.engine.MobManager;
|
||||||
|
import gg.malloc.defense.engine.BombFuse;
|
||||||
|
|
||||||
|
import gg.malloc.defense.model.Game;
|
||||||
|
|
||||||
|
import org.bukkit.boss.BossBar;
|
||||||
|
import org.bukkit.boss.BarStyle;
|
||||||
|
import org.bukkit.boss.BarColor;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class BossBars {
|
||||||
|
BossBar m_gameBar = 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 Fuse", BarColor.RED, BarStyle.SOLID);
|
||||||
|
|
||||||
|
GameRunner m_runner;
|
||||||
|
MobManager m_mobs;
|
||||||
|
WaveManager m_waves;
|
||||||
|
PlayerManager m_players;
|
||||||
|
BombFuse m_fuse;
|
||||||
|
Game m_game;
|
||||||
|
|
||||||
|
double m_countdownProgress;
|
||||||
|
|
||||||
|
public void setCountdownProgress(double v) {
|
||||||
|
m_countdownProgress = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossBars(GameRunner runner, Game game, WaveManager waves, PlayerManager players, BombFuse fuse, MobManager mobs) {
|
||||||
|
m_runner = runner;
|
||||||
|
m_game = game;
|
||||||
|
m_waves = waves;
|
||||||
|
m_players = players;
|
||||||
|
m_fuse = fuse;
|
||||||
|
m_mobs = mobs;
|
||||||
|
m_gameBar.setVisible(true);
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(Player p) {
|
||||||
|
m_gameBar.addPlayer(p);
|
||||||
|
m_waveBar.addPlayer(p);
|
||||||
|
m_bombBar.addPlayer(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePlayer(Player p) {
|
||||||
|
m_gameBar.removePlayer(p);
|
||||||
|
m_waveBar.removePlayer(p);
|
||||||
|
m_bombBar.removePlayer(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
switch(m_runner.getStage()) {
|
||||||
|
case Idle:
|
||||||
|
m_gameBar.setProgress(1.0);
|
||||||
|
m_gameBar.setTitle("Waiting for players...");
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
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.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.YELLOW);
|
||||||
|
m_waveBar.setTitle("Wave starting!");
|
||||||
|
m_waveBar.setProgress(m_countdownProgress);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
case Playing:
|
||||||
|
m_gameBar.setProgress(m_waves.progress());
|
||||||
|
m_gameBar.setTitle("Wave " + m_waves.currentWaveNum() + " / " + m_game.getWaveCount());
|
||||||
|
m_gameBar.setColor(BarColor.PURPLE);
|
||||||
|
if (m_mobs.createdMobs() > 0) {
|
||||||
|
m_waveBar.setVisible(true);
|
||||||
|
m_waveBar.setColor(BarColor.GREEN);
|
||||||
|
m_waveBar.setTitle("Mobs remaining: " + m_mobs.remainingMobs());
|
||||||
|
m_waveBar.setProgress(m_mobs.progress());
|
||||||
|
} else {
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
}
|
||||||
|
if (m_fuse.isLit()) {
|
||||||
|
m_bombBar.setVisible(true);
|
||||||
|
m_bombBar.setProgress(1.0 - m_fuse.getProgress());
|
||||||
|
} else {
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GameOver:
|
||||||
|
m_gameBar.setColor(BarColor.RED);
|
||||||
|
m_gameBar.setProgress(1.0);
|
||||||
|
m_gameBar.setTitle("Game Over!");
|
||||||
|
m_waveBar.setVisible(false);
|
||||||
|
m_bombBar.setVisible(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/main/java/gg/malloc/defense/ui/Items.java
Normal file
15
src/main/java/gg/malloc/defense/ui/Items.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package gg.malloc.defense.engine;
|
||||||
|
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
public class Items {
|
||||||
|
static ItemStack makeBombHelmet() {
|
||||||
|
ItemStack bombItem = new ItemStack(Material.CARVED_PUMPKIN);
|
||||||
|
ItemMeta meta = bombItem.getItemMeta();
|
||||||
|
meta.setCustomModelData(0);
|
||||||
|
bombItem.setItemMeta(meta);
|
||||||
|
return bombItem;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user