Reimplement wave AI, split out some UI code into a ui module, update TODO
This commit is contained in:
		
							
								
								
									
										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;
 | 
			
		||||
 | 
			
		||||
import gg.malloc.defense.model.Game;
 | 
			
		||||
import gg.malloc.defense.model.Arena;
 | 
			
		||||
import gg.malloc.defense.model.Game;
 | 
			
		||||
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 java.util.logging.Logger;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.Location;
 | 
			
		||||
import org.bukkit.Particle;
 | 
			
		||||
import org.bukkit.Sound;
 | 
			
		||||
import org.bukkit.SoundCategory;
 | 
			
		||||
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.EntityDamageByEntityEvent;
 | 
			
		||||
import org.bukkit.event.entity.EntityTargetEvent;
 | 
			
		||||
import org.bukkit.event.player.PlayerTeleportEvent;
 | 
			
		||||
import org.bukkit.entity.Entity;
 | 
			
		||||
import org.bukkit.entity.LivingEntity;
 | 
			
		||||
import org.bukkit.entity.ArmorStand;
 | 
			
		||||
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.scheduler.BukkitTask;
 | 
			
		||||
 | 
			
		||||
public class GameRunner {
 | 
			
		||||
  Arena m_arena;
 | 
			
		||||
@@ -32,18 +37,11 @@ public class GameRunner {
 | 
			
		||||
  Stage m_stage;
 | 
			
		||||
  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;
 | 
			
		||||
  WaveManager m_waves;
 | 
			
		||||
  PlayerManager m_players;
 | 
			
		||||
 | 
			
		||||
  LivingEntity m_bombTarget = null;
 | 
			
		||||
  int m_bombHP = 100;
 | 
			
		||||
  BombFuse m_bombFuse;
 | 
			
		||||
 | 
			
		||||
  Logger m_log;
 | 
			
		||||
 | 
			
		||||
@@ -55,126 +53,96 @@ public class GameRunner {
 | 
			
		||||
    GameOver
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  class WaveManager {
 | 
			
		||||
    int m_currentWaveNum = 0;
 | 
			
		||||
    int m_currentBatch = 0;
 | 
			
		||||
    Wave m_currentWave = null;
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  TickTask m_fuseTask;
 | 
			
		||||
  TickTask m_countdownTask;
 | 
			
		||||
  TickTask m_bombSmokeTask;
 | 
			
		||||
  TickTask m_bombCrackleTask;
 | 
			
		||||
 | 
			
		||||
  BossBars m_bars;
 | 
			
		||||
 | 
			
		||||
  public GameRunner(Plugin plugin, Game game, Arena arena) {
 | 
			
		||||
    m_plugin = plugin;
 | 
			
		||||
    m_game = game;
 | 
			
		||||
    m_arena = arena;
 | 
			
		||||
    m_stage = Stage.Idle;
 | 
			
		||||
    m_gameBar.setVisible(true);
 | 
			
		||||
    m_waveBar.setVisible(false);
 | 
			
		||||
    m_bombBar.setVisible(false);
 | 
			
		||||
    m_mobs = new MobManager(m_game);
 | 
			
		||||
    m_mobs = new MobManager();
 | 
			
		||||
    m_waves = new WaveManager(m_game);
 | 
			
		||||
    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_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;
 | 
			
		||||
 | 
			
		||||
  public void handleEntityRetargeting(EntityTargetEvent evt) {
 | 
			
		||||
    if (m_mobs.contains(evt.getEntity()) && evt.getReason() == EntityTargetEvent.TargetReason.FORGOT_TARGET) {
 | 
			
		||||
      evt.setTarget(m_bombTarget);
 | 
			
		||||
    }
 | 
			
		||||
    m_mobs.handleEntityRetarget(evt);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void handleEntityDamage(EntityDamageEvent evt) {
 | 
			
		||||
    if (evt.getEntity() == m_bombTarget && evt instanceof EntityDamageByEntityEvent) {
 | 
			
		||||
    m_mobs.handleEntityDamage(evt);
 | 
			
		||||
    if (m_mobs.bombWasHit()) {
 | 
			
		||||
      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);
 | 
			
		||||
        }
 | 
			
		||||
      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);
 | 
			
		||||
      }
 | 
			
		||||
      evt.setCancelled(true);
 | 
			
		||||
    } else {
 | 
			
		||||
      m_mobs.handleEntityDamage(evt);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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() {
 | 
			
		||||
    m_bombHP = 100;
 | 
			
		||||
    m_bombFuse.reset();
 | 
			
		||||
    m_waves.reset();
 | 
			
		||||
    m_mobs.clear();
 | 
			
		||||
    m_bombSmokeTask.stop();
 | 
			
		||||
    m_bombCrackleTask.stop();
 | 
			
		||||
    m_players.requestTransitionForAll(PlayerManager.State.Idle);
 | 
			
		||||
    if (m_bombTarget != null) {
 | 
			
		||||
      m_bombTarget.remove();
 | 
			
		||||
      m_bombTarget = null;
 | 
			
		||||
    }
 | 
			
		||||
    if (m_countdownTask != null) {
 | 
			
		||||
      m_countdownTask.cancel();
 | 
			
		||||
      m_countdownTask = null;
 | 
			
		||||
    }
 | 
			
		||||
    m_fuseTask.stop();
 | 
			
		||||
    m_countdownTask.stop();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -204,25 +172,22 @@ public class GameRunner {
 | 
			
		||||
 | 
			
		||||
  private boolean enterCountdown() {
 | 
			
		||||
    m_warmupCountdown = 10;
 | 
			
		||||
    countdownTick();
 | 
			
		||||
    m_bars.setCountdownProgress(1.0);
 | 
			
		||||
    m_countdownTask.start();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private boolean enterPlaying() {
 | 
			
		||||
    m_log.info("Starting wave " + m_waves.currentWaveNum());
 | 
			
		||||
    if (m_bombTarget == null) {
 | 
			
		||||
      m_bombTarget = (LivingEntity)m_arena.getWorld().spawnEntity(m_arena.bombTarget().getLocation(), EntityType.ARMOR_STAND);
 | 
			
		||||
    }
 | 
			
		||||
    ArmorStand bombStand = (ArmorStand)m_bombTarget;
 | 
			
		||||
    bombStand.setCustomName("Bomb Target");
 | 
			
		||||
    bombStand.setVisible(true);
 | 
			
		||||
    bombStand.setCustomNameVisible(true);
 | 
			
		||||
    bombStand.setGlowing(true);
 | 
			
		||||
    m_mobs.spawnTarget(m_arena.bombTarget().getLocation());
 | 
			
		||||
    // TODO: Set helmet with custom model data
 | 
			
		||||
    for(Player p : m_players.getPlayers()) {
 | 
			
		||||
        if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
 | 
			
		||||
          p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    m_countdownTask.stop();
 | 
			
		||||
    m_fuseTask.start();
 | 
			
		||||
    spawnNextBatch();
 | 
			
		||||
    broadcastTitle("Wave " + m_waves.currentWaveNum());
 | 
			
		||||
    return true;
 | 
			
		||||
@@ -230,80 +195,28 @@ public class GameRunner {
 | 
			
		||||
  
 | 
			
		||||
  private boolean enterGameOver() {
 | 
			
		||||
    broadcastTitle("Game Over!");
 | 
			
		||||
    if (m_countdownTask != null) {
 | 
			
		||||
      m_countdownTask.cancel();
 | 
			
		||||
      m_countdownTask = null;
 | 
			
		||||
    }
 | 
			
		||||
    broadcastMessage("The bomb blew up!");
 | 
			
		||||
    m_countdownTask.stop();
 | 
			
		||||
    m_fuseTask.stop();
 | 
			
		||||
    m_mobs.clear();
 | 
			
		||||
    for(Player p : m_players.getPlayers()) {
 | 
			
		||||
    for(Player p : new ArrayList<Player>(m_players.getPlayers())) {
 | 
			
		||||
      removePlayer(p);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void updateMobBars() {
 | 
			
		||||
    m_gameBar.setVisible(true);
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
  public Stage getStage() {
 | 
			
		||||
    return m_stage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void spawnNextBatch() {
 | 
			
		||||
    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());
 | 
			
		||||
    updateMobBars();
 | 
			
		||||
    m_bars.update();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void handlePlayerDeath(Player player) {
 | 
			
		||||
  private void handlePlayerDeath(Player player) {
 | 
			
		||||
    if (m_players.requestTransition(player, PlayerManager.State.Dead)) {
 | 
			
		||||
      m_arena.getWorld().strikeLightningEffect(player.getLocation());
 | 
			
		||||
      if (!m_players.isAnyoneAlive()) {
 | 
			
		||||
@@ -316,9 +229,9 @@ public class GameRunner {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void handleEntityDeath(Entity entity) {
 | 
			
		||||
    boolean wasCarrier = m_mobs.isBombCarrier(entity);
 | 
			
		||||
    if (m_mobs.killMob(entity)) {
 | 
			
		||||
      broadcastMessage("Killed game entity " + entity);
 | 
			
		||||
      updateMobBars();
 | 
			
		||||
      m_bars.update();
 | 
			
		||||
      if (m_mobs.remainingMobs() <= 3) {
 | 
			
		||||
        m_log.info("Starting next batch!");
 | 
			
		||||
        if (m_waves.isLastBatch()) {
 | 
			
		||||
@@ -381,7 +294,7 @@ public class GameRunner {
 | 
			
		||||
    if (attemptTransition(stage)) {
 | 
			
		||||
      m_log.info("Game transition: " + m_stage + " -> " + stage);
 | 
			
		||||
      m_stage = stage;
 | 
			
		||||
      updateMobBars();
 | 
			
		||||
      m_bars.update();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    m_log.severe("Failed to complete transition: " + m_stage + " -> " + stage);
 | 
			
		||||
@@ -389,10 +302,11 @@ public class GameRunner {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void addPlayer(Player p) {
 | 
			
		||||
    if (m_players.contains(p)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    m_players.addPlayer(p);
 | 
			
		||||
    m_gameBar.addPlayer(p);
 | 
			
		||||
    m_waveBar.addPlayer(p);
 | 
			
		||||
    m_bombBar.addPlayer(p);
 | 
			
		||||
    m_bars.addPlayer(p);
 | 
			
		||||
    if (m_stage == Stage.Idle || m_stage == Stage.Warmup) {
 | 
			
		||||
      if (m_players.requestTransition(p, PlayerManager.State.Playing)) {
 | 
			
		||||
        p.teleport(m_arena.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
 | 
			
		||||
@@ -405,8 +319,7 @@ public class GameRunner {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void removePlayer(Player p) {
 | 
			
		||||
    m_gameBar.removePlayer(p);
 | 
			
		||||
    m_waveBar.removePlayer(p);
 | 
			
		||||
    m_bars.removePlayer(p);
 | 
			
		||||
    m_players.removePlayer(p);
 | 
			
		||||
    if (m_players.isEmpty()) {
 | 
			
		||||
      requestTransition(Stage.Idle);
 | 
			
		||||
@@ -429,8 +342,4 @@ public class GameRunner {
 | 
			
		||||
      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.EntityType;
 | 
			
		||||
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 {
 | 
			
		||||
  Arena m_arena;
 | 
			
		||||
  MobManager m_manager;
 | 
			
		||||
  PlayerManager m_players;
 | 
			
		||||
  LivingEntity m_bombTarget;
 | 
			
		||||
  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_manager = manager;
 | 
			
		||||
    m_players = players;
 | 
			
		||||
    m_bombTarget = bombTarget;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @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();
 | 
			
		||||
    m_spawnIdx %= spawnpoints.length;
 | 
			
		||||
    //m_log.fine("Spawning " + type + " at " + spawnpoints[m_spawnIdx]);
 | 
			
		||||
    Entity newMob = m_arena.getWorld().spawnEntity(spawnpoints[m_spawnIdx].getLocation(), type);
 | 
			
		||||
    LivingEntity livingMob = (LivingEntity)newMob;
 | 
			
		||||
    livingMob.setRemoveWhenFarAway(false);
 | 
			
		||||
    m_manager.addEntity(newMob);
 | 
			
		||||
    m_manager.addEntity(livingMob);
 | 
			
		||||
    m_spawnIdx += 1;
 | 
			
		||||
    ((Mob)newMob).setTarget(m_bombTarget);
 | 
			
		||||
    return newMob;
 | 
			
		||||
    return livingMob;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,48 +2,61 @@ package gg.malloc.defense.engine;
 | 
			
		||||
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
 | 
			
		||||
import org.bukkit.Location;
 | 
			
		||||
import org.bukkit.entity.ArmorStand;
 | 
			
		||||
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.EntityDamageByEntityEvent;
 | 
			
		||||
import org.bukkit.event.entity.EntityTargetEvent;
 | 
			
		||||
 | 
			
		||||
import gg.malloc.defense.model.Game;
 | 
			
		||||
 | 
			
		||||
import gg.malloc.defense.ui.BombCarrier;
 | 
			
		||||
 | 
			
		||||
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_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) {
 | 
			
		||||
    return m_livingMobs.contains(entity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean isBombCarrier(Entity entity) {
 | 
			
		||||
    return m_bombCarriers.contains(entity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean killMob(Entity entity) {
 | 
			
		||||
    if (m_livingMobs.contains(entity)) {
 | 
			
		||||
      m_killedMobs += 1;
 | 
			
		||||
      if (m_bombCarriers.remove(entity)) {
 | 
			
		||||
        dropBomb(entity.getLocation());
 | 
			
		||||
      }
 | 
			
		||||
      return m_livingMobs.remove(entity);
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void clear() {
 | 
			
		||||
    for(Entity e : m_livingMobs) {
 | 
			
		||||
    for(LivingEntity e : m_livingMobs) {
 | 
			
		||||
      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_bombCarriers.clear();
 | 
			
		||||
    m_createdMobs = 0;
 | 
			
		||||
    m_killedMobs = 0;
 | 
			
		||||
  }
 | 
			
		||||
@@ -64,13 +77,106 @@ public class MobManager {
 | 
			
		||||
    return (double)m_killedMobs / (double)m_createdMobs;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public void addEntity(Entity entity) {
 | 
			
		||||
    //m_log.fine("Registered new mob " + entity); 
 | 
			
		||||
  public void addEntity(LivingEntity 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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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() {
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean contains(Player player) {
 | 
			
		||||
    return m_playerStates.containsKey(player);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public boolean requestTransition(Player player, State toState) {
 | 
			
		||||
    State currentState = m_playerStates.get(player);
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user