From 99197d92bb805c6a7417a106563af95b620b6946 Mon Sep 17 00:00:00 2001 From: Torrie Fischer Date: Sun, 15 May 2022 18:16:35 +0200 Subject: [PATCH] Colors, basic mob AI categories, end world hunger, UX buffs --- TODO.md | 42 ++++-- .../gg/malloc/defense/GameEventHandler.java | 17 +++ .../defense/commands/JoinGameCommand.java | 6 +- .../defense/commands/LeaveGameCommand.java | 4 +- .../defense/commands/ListGamesCommand.java | 29 +++- .../defense/commands/SetStageCommand.java | 20 +-- .../gg/malloc/defense/engine/GameRunner.java | 77 ++++++++--- .../gg/malloc/defense/engine/GameSpawner.java | 21 +-- .../gg/malloc/defense/engine/MobManager.java | 130 +++++++++++++----- .../malloc/defense/engine/PlayerManager.java | 9 +- .../gg/malloc/defense/games/ScaledWaves.java | 13 +- .../gg/malloc/defense/ui/BombCarrier.java | 2 - src/main/java/gg/malloc/defense/ui/Items.java | 8 ++ 13 files changed, 275 insertions(+), 103 deletions(-) diff --git a/TODO.md b/TODO.md index 77fb4eb..e749fa8 100644 --- a/TODO.md +++ b/TODO.md @@ -14,20 +14,32 @@ [X] Join games [X] Drop back to lobby on game over [X] Grist drops -[ ] Chat colors + clickables -[ ] Title colors -[ ] Command tab completion +[X] Chat colors + clickables +[X] Title colors +[X] Command tab completion +[X] Mob AI categories +[X] Never hungry +[X] Mobs don't drop bomb items + +# QOL + +[ ] Players can't pick up bomb items +[ ] "Get ready" nag +[ ] "Click here to leave" at end of game +[ ] Hidden armor stands +[ ] Small/nonexistent prop collision boxes + +# Malloc beta map + [ ] Lobby with instructions -[ ] Item shoppes -[ ] Mob category AI +[X] Item shoppes [ ] Indestructible weapons/armor -[ ] Never hungry # Scaled waves -[ ] Limit ranged mobs and Ravagers to non-bomb-carrier state -[ ] Weaker mobs, more of them -[ ] Mob AI categories +[X] Limit ranged mobs and Ravagers to non-bomb-carrier state +[X] Weaker mobs, more of them +[X] Mob AI categories # UX @@ -39,7 +51,7 @@ [X] "Player $X is ready" message in chat [X] Clickable /ready in chat [ ] Post-Round summary in chat -[ ] Clickable join links in /list +[X] Clickable join links in /list [ ] Sidebar [ ] Coin pickup messages in action bar [ ] Target catches on fire more it is lit @@ -64,9 +76,9 @@ [X] Mob tracking should prioritize bomb [X] Mobs recover dropped bombs [X] Bomb carriers are slower -[ ] Coin drops -[ ] Mob categories -[ ] Mobs split between bomb and player priorities +[X] Coin drops +[X] Mob categories +[X] Mobs split between bomb and player priorities [ ] Bonus coins for complete coin pickup [ ] Infinite weapons + armor [ ] Ammo/health spawns @@ -77,7 +89,7 @@ [X] Randomized spawn locations [X] Weighted distributions [X] Batch overlap -[ ] Spawnpoint categories +[X] Spawnpoint categories [ ] Scripted batch overlap/timings [ ] Scripted spawn locations [ ] Scripted waypoint paths @@ -103,7 +115,7 @@ [ ] Instancing [ ] Respawn during games [ ] Player revival items -[ ] Clear inventory on join/leave +[X] Clear inventory on join/leave # Powerups diff --git a/src/main/java/gg/malloc/defense/GameEventHandler.java b/src/main/java/gg/malloc/defense/GameEventHandler.java index a20be7e..0d1baec 100644 --- a/src/main/java/gg/malloc/defense/GameEventHandler.java +++ b/src/main/java/gg/malloc/defense/GameEventHandler.java @@ -4,6 +4,7 @@ import gg.malloc.defense.engine.GameRunner; import java.util.Collection; import java.util.HashSet; +import java.util.ArrayList; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -11,7 +12,10 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityCombustEvent; import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.LootGenerateEvent; +import org.bukkit.inventory.ItemStack; import org.bukkit.entity.Player; public class GameEventHandler implements Listener { @@ -61,4 +65,17 @@ public class GameEventHandler implements Listener { r.handleEntityDamage(evt); } } + + @EventHandler + public void onLootGenerated(LootGenerateEvent evt) { + if (evt.getEntity() != null) { + ArrayList emptyLoot = new ArrayList<>(); + evt.setLoot(emptyLoot); + } + } + + @EventHandler + public void onFoodLevelChange(FoodLevelChangeEvent evt) { + evt.setCancelled(true); + } } diff --git a/src/main/java/gg/malloc/defense/commands/JoinGameCommand.java b/src/main/java/gg/malloc/defense/commands/JoinGameCommand.java index 2ed0c2b..c3497af 100644 --- a/src/main/java/gg/malloc/defense/commands/JoinGameCommand.java +++ b/src/main/java/gg/malloc/defense/commands/JoinGameCommand.java @@ -24,10 +24,14 @@ public class JoinGameCommand implements TabExecutor { if (args.length == 1) { String proposal = args[0].toLowerCase(); for(String arena : m_plugin.arenaNames()) { - if (proposal.startsWith(arena.toLowerCase())) { + if (arena.toLowerCase().startsWith(proposal.toLowerCase())) { ret.add(arena); } } + } else if (args.length == 0) { + for(String arena : m_plugin.arenaNames()) { + ret.add(arena); + } } return ret; } diff --git a/src/main/java/gg/malloc/defense/commands/LeaveGameCommand.java b/src/main/java/gg/malloc/defense/commands/LeaveGameCommand.java index 17934ce..9ba2b12 100644 --- a/src/main/java/gg/malloc/defense/commands/LeaveGameCommand.java +++ b/src/main/java/gg/malloc/defense/commands/LeaveGameCommand.java @@ -22,13 +22,13 @@ public class LeaveGameCommand implements CommandExecutor { Player player = (Player)sender; GameRunner runner = m_plugin.getRunnerForPlayer(player); if (runner == null) { - sender.sendMessage("You ae not currently in a game."); + sender.sendMessage("You are not currently in a game."); return true; } runner.removePlayer(player); m_plugin.returnPlayerToLobby(player); } else { - sender.sendMessage("Only players may use htis command."); + sender.sendMessage("Only players may use this command."); } return true; } diff --git a/src/main/java/gg/malloc/defense/commands/ListGamesCommand.java b/src/main/java/gg/malloc/defense/commands/ListGamesCommand.java index 617b5bb..01d6c22 100644 --- a/src/main/java/gg/malloc/defense/commands/ListGamesCommand.java +++ b/src/main/java/gg/malloc/defense/commands/ListGamesCommand.java @@ -5,6 +5,11 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.ComponentBuilder; + import gg.malloc.defense.engine.GameRunner; import gg.malloc.defense.Plugin; @@ -20,14 +25,28 @@ public class ListGamesCommand implements CommandExecutor { public boolean onCommand(CommandSender sender, Command command, String s, String[] args) { sender.sendMessage("Available games:"); for(String arenaName : m_plugin.arenaNames()) { - String arenaDescription = arenaName + ": "; + String stageName = "Ready to play"; + ChatColor stageColor = ChatColor.WHITE; if (m_plugin.hasRunnerForArenaName(arenaName)) { GameRunner runner = m_plugin.getRunnerForArenaName(arenaName); - arenaDescription += runner.getStage().toString(); - } else { - arenaDescription += "Loadable"; + switch(runner.getStage()) { + case Idle: stageColor = ChatColor.WHITE;stageName = "Open";break; + case Warmup: stageColor = ChatColor.YELLOW;stageName = "" + runner.getPlayers().size() + " players warming up";break; + case Countdown: stageColor = ChatColor.YELLOW;stageName = "" + runner.getPlayers().size() + " players warming up";break; + case Playing: stageColor = ChatColor.GOLD;stageName = "" + runner.getPlayers().size() + " players";break; + case GameOver: stageColor = ChatColor.RED;stageName = "Cleaning up...";break; + } } - sender.sendMessage(arenaDescription); + + + BaseComponent[] message = new ComponentBuilder() + .append(" " + arenaName).color(ChatColor.LIGHT_PURPLE).bold(true) + .append(" - ").color(ChatColor.WHITE) + .append(stageName).color(stageColor).italic(true) + .append(" [Join] ").color(ChatColor.GOLD).bold(true) + .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/malloc-defense:join " + arenaName)) + .create(); + sender.spigot().sendMessage(message); } return true; } diff --git a/src/main/java/gg/malloc/defense/commands/SetStageCommand.java b/src/main/java/gg/malloc/defense/commands/SetStageCommand.java index 8c87fac..b920e95 100644 --- a/src/main/java/gg/malloc/defense/commands/SetStageCommand.java +++ b/src/main/java/gg/malloc/defense/commands/SetStageCommand.java @@ -22,20 +22,24 @@ public class SetStageCommand implements TabExecutor { public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { ArrayList ret = new ArrayList<>(); if (args.length == 1) { - String proposal = args[0]; - for(String arena : m_plugin.arenaNames()) { - if (proposal.startsWith(arena)) { - ret.add(arena); + String proposal = args[0].toLowerCase(); + GameRunner.Stage stages[] = GameRunner.Stage.Idle.getDeclaringClass().getEnumConstants(); + for(GameRunner.Stage stage : stages) { + if (stage.toString().toLowerCase().startsWith(proposal)) { + ret.add(stage.toString()); } } } 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()); + for(String arena : m_plugin.arenaNames()) { + if (arena.toLowerCase().startsWith(proposal)) { + ret.add(arena); } } + } else if (args.length == 0) { + for(String arena : m_plugin.arenaNames()) { + ret.add(arena); + } } return ret; } diff --git a/src/main/java/gg/malloc/defense/engine/GameRunner.java b/src/main/java/gg/malloc/defense/engine/GameRunner.java index 508a228..a558680 100644 --- a/src/main/java/gg/malloc/defense/engine/GameRunner.java +++ b/src/main/java/gg/malloc/defense/engine/GameRunner.java @@ -19,9 +19,12 @@ import gg.malloc.defense.Plugin; import java.util.logging.Logger; import java.util.ArrayList; import java.util.HashSet; +import java.util.Collection; import org.bukkit.Bukkit; +import org.bukkit.GameRule; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.SoundCategory; @@ -32,11 +35,11 @@ import org.bukkit.event.entity.EntityTargetEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.entity.Entity; 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.Projectile; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; import org.bukkit.inventory.ItemStack; public class GameRunner { @@ -75,6 +78,24 @@ public class GameRunner { return new Location(m_world, waypoint.getX(), waypoint.getY(), waypoint.getZ()); } + void validateGameRule(GameRule rule, boolean value) { + if (!m_world.getGameRuleValue(rule).equals(value)) { + m_log.warning("Game rule " + rule + " is not " + value); + } + } + + void validateGameRules() { + validateGameRule(GameRule.DO_MOB_LOOT, false); + validateGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false); + validateGameRule(GameRule.DISABLE_RAIDS, true); + validateGameRule(GameRule.DO_DAYLIGHT_CYCLE, false); + validateGameRule(GameRule.DO_ENTITY_DROPS, false); + validateGameRule(GameRule.DO_FIRE_TICK, false); + validateGameRule(GameRule.DO_INSOMNIA, false); + validateGameRule(GameRule.DO_WEATHER_CYCLE, false); + validateGameRule(GameRule.SPECTATORS_GENERATE_CHUNKS, false); + } + public GameRunner(Plugin plugin, Game game, Arena arena, World world) { m_plugin = plugin; m_game = game; @@ -124,15 +145,15 @@ public class GameRunner { .create(); broadcastMessage(message); m_gameEndCountdown--; - m_bars.setCountdownProgress((double)m_gameEndCountdown / 30.0); + m_bars.setCountdownProgress((double)m_gameEndCountdown / 60.0); m_bars.update(); } }, 20); m_bombSmokeTask = new TickTask(m_plugin, () -> { Location targetLoc = getLocation(m_arena.bombTarget()); - m_world.spawnParticle(Particle.SMOKE_LARGE, targetLoc, 35, 4, 2, 4); - m_world.spawnParticle(Particle.SMALL_FLAME, targetLoc, 30, 3, 2, 3); + m_world.spawnParticle(Particle.SMOKE_LARGE, targetLoc, 135, 4, 2, 4, 1); + m_world.spawnParticle(Particle.SMALL_FLAME, targetLoc, 130, 3, 2, 3, 1); }, 5); m_bombCrackleTask = new TickTask(m_plugin, () -> { Location targetLoc = getLocation(m_arena.bombTarget()); @@ -149,7 +170,7 @@ public class GameRunner { public void handleEntityDamage(EntityDamageEvent evt) { m_mobs.handleEntityDamage(evt); - if (m_mobs.bombWasHit()) { + if (m_mobs.bombWasHit() && !m_bombFuse.isExploded()) { EntityDamageByEntityEvent entityEvt = (EntityDamageByEntityEvent)evt; m_log.info("Target attacked!"); entityEvt.getDamager().setGlowing(true); @@ -175,9 +196,14 @@ public class GameRunner { if (entityEvt.getDamager() instanceof Player) { evt.setCancelled(true); return; + } else if (entityEvt.getDamager() instanceof Projectile) { + Projectile asProjectile = (Projectile)entityEvt.getDamager(); + if (asProjectile.getShooter() instanceof Player) { + evt.setCancelled(true); + } } } - if (m_players.contains(player) && player.getHealth() - evt.getFinalDamage() <= 0) { + if (m_players.contains(player) && player.getHealth() - evt.getFinalDamage() <= 0 && m_stage == Stage.Playing) { evt.setCancelled(true); handlePlayerDeath(player); } @@ -197,6 +223,10 @@ public class GameRunner { return true; } + public Collection getPlayers() { + return m_players.getPlayers(); + } + private boolean enterWarmup() { m_waves.next(); for(Player p : m_players.getPlayers()) { @@ -204,12 +234,13 @@ public class GameRunner { p.teleport(m_world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); } m_players.setReady(p, false); + m_players.healPlayer(p); } BaseComponent[] message = new ComponentBuilder() .append("Click").color(ChatColor.LIGHT_PURPLE) - .append("[Here]").color(ChatColor.GOLD) + .append(" [Here] ").color(ChatColor.GOLD).bold(true) .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/malloc-defense:ready")) - .append(" when ready.").color(ChatColor.LIGHT_PURPLE) + .append("when ready.").color(ChatColor.LIGHT_PURPLE) .create(); broadcastMessage(message); m_mobs.clear(); @@ -233,11 +264,11 @@ public class GameRunner { .append(p.getName()).bold(true) .append(" is ready!").color(ChatColor.AQUA).italic(true) .create(); + broadcastMessage(message); } m_players.setReady(p, isReady); } m_bars.update(); - broadcastMessage(message); if (m_players.isEveryoneReady()) { requestTransition(Stage.Countdown); } @@ -270,7 +301,7 @@ public class GameRunner { m_lobbyReturnTask.start(); m_countdownTask.stop(); m_fuseTask.stop(); - m_mobs.clear(); + //m_mobs.clear(); return true; } @@ -280,7 +311,7 @@ public class GameRunner { private void spawnNextBatch() { m_log.info("Spawning batch " + m_waves.currentBatchNum()); - Spawner spawner = new GameSpawner(m_world, m_arena, m_mobs, m_players); + Spawner spawner = new GameSpawner(m_world, m_arena, m_mobs); m_waves.currentWave().spawnBatch(spawner, m_waves.currentBatchNum()); m_bars.update(); } @@ -305,9 +336,9 @@ public class GameRunner { public void handleEntityDeath(Entity entity) { boolean wasCarrier = m_mobs.isBombCarrier(entity); if (m_mobs.killMob(entity)) { - int coinsToDrop = 300; + int coinsToDrop = 60; while(coinsToDrop > 0) { - int droppedCoins = Math.min(coinsToDrop, 64); + int droppedCoins = Math.min(coinsToDrop, 5); ItemStack coins = Items.makeCoins(); coins.setAmount(droppedCoins); coinsToDrop -= 64; @@ -360,9 +391,9 @@ public class GameRunner { case Idle: return !m_players.isEmpty() && to == Stage.Warmup; case Warmup: - return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown; + return to == Stage.Playing || to == Stage.Idle || to == Stage.Countdown || to == Stage.GameOver; case Countdown: - return to == Stage.Playing || to == Stage.Idle || to == Stage.Warmup; + return to == Stage.Playing || to == Stage.Idle || to == Stage.Warmup || to == Stage.GameOver; case Playing: return to == Stage.Warmup || to == Stage.GameOver || to == Stage.Idle; case GameOver: @@ -392,13 +423,13 @@ public class GameRunner { void sendStageTitle(Player p) { switch(m_stage) { case Warmup: - p.sendTitle("Warmup", "Prepare yourself for wave " + m_waves.currentWaveNum(), 10, 70, 20); + p.sendTitle(ChatColor.YELLOW.toString() + "Warmup", ChatColor.LIGHT_PURPLE.toString() + "Prepare yourself for wave " + m_waves.currentWaveNum()); break; case Playing: - p.sendTitle("Wave " + m_waves.currentWaveNum(), "", 10, 70, 20); + p.sendTitle(ChatColor.AQUA.toString() + "Wave " + m_waves.currentWaveNum(), ""); break; case GameOver: - p.sendTitle("Game over!", "", 10, 70, 20); + p.sendTitle(ChatColor.RED.toString() + "Game over!", ""); } } @@ -406,6 +437,7 @@ public class GameRunner { if (m_players.contains(p)) { return; } + p.getInventory().clear(); m_players.addPlayer(p); m_bars.addPlayer(p); if (m_stage == Stage.Idle || m_stage == Stage.Warmup) { @@ -414,7 +446,7 @@ public class GameRunner { } BaseComponent[] message = new ComponentBuilder() .append(p.getName()).bold(true) - .append("Has joined the game").color(ChatColor.AQUA) + .append(" has joined the game").color(ChatColor.AQUA) .create(); broadcastMessage(message); sendStageTitle(p); @@ -425,6 +457,7 @@ public class GameRunner { } public void removePlayer(Player p) { + p.getInventory().clear(); m_bars.removePlayer(p); m_players.removePlayer(p); if (m_players.isEmpty()) { @@ -444,7 +477,7 @@ public class GameRunner { void broadcastTitle(String title, String subtitle) { for(Player p : m_players.getPlayers()) { - p.sendTitle(title, subtitle, 10, 70, 20); + p.sendTitle(title, subtitle); } } diff --git a/src/main/java/gg/malloc/defense/engine/GameSpawner.java b/src/main/java/gg/malloc/defense/engine/GameSpawner.java index d531ba0..8cd5ef0 100644 --- a/src/main/java/gg/malloc/defense/engine/GameSpawner.java +++ b/src/main/java/gg/malloc/defense/engine/GameSpawner.java @@ -20,38 +20,39 @@ import org.bukkit.attribute.AttributeModifier; public class GameSpawner implements Spawner { Arena m_arena; MobManager m_manager; - PlayerManager m_players; int m_spawnIdx = 0; World m_world; - public GameSpawner(World world, Arena arena, MobManager manager, PlayerManager players) { + public GameSpawner(World world, Arena arena, MobManager manager) { m_world = world; m_arena = arena; m_manager = manager; - m_players = players; } @Override public LivingEntity spawnBombCarrier(EntityType type) { if (m_manager.bombCount() == 0) { - return m_manager.addBombCarrier(spawnMob(type)); + return m_manager.addBombCarrier(spawnMob(type, MobManager.Goal.BombCarrier)); } else { - return spawnMob(type); + return spawnMob(type, MobManager.Goal.BombCarrier); } } @Override public LivingEntity spawnMob(EntityType type) { + return spawnMob(type, MobManager.Goal.PlayerHarassment); + } + + public Mob spawnMob(EntityType type, MobManager.Goal goal) { Waypoint[] spawnpoints = m_arena.spawnpoints(); m_spawnIdx %= spawnpoints.length; //m_log.fine("Spawning " + type + " at " + spawnpoints[m_spawnIdx]); 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; - livingMob.setRemoveWhenFarAway(false); - m_manager.addEntity(livingMob); + Mob newMob = (Mob)m_world.spawnEntity(loc, type); + newMob.setRemoveWhenFarAway(false); + m_manager.addEntity(newMob, goal); m_spawnIdx += 1; - return livingMob; + return newMob; } } diff --git a/src/main/java/gg/malloc/defense/engine/MobManager.java b/src/main/java/gg/malloc/defense/engine/MobManager.java index 1ae1807..721794d 100644 --- a/src/main/java/gg/malloc/defense/engine/MobManager.java +++ b/src/main/java/gg/malloc/defense/engine/MobManager.java @@ -1,25 +1,37 @@ package gg.malloc.defense.engine; import java.util.HashSet; +import java.util.HashMap; import org.bukkit.Location; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.GameMode; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.entity.Mob; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.inventory.EntityEquipment; import gg.malloc.defense.model.Game; import gg.malloc.defense.ui.BombCarrier; +import gg.malloc.defense.ui.Items; public class MobManager { - HashSet m_livingMobs = new HashSet<>(); - HashSet m_bombCarriers = new HashSet<>(); + LivingEntity m_bombTarget; + HashSet m_livingMobs = new HashSet<>(); + HashSet m_bombCarriers = new HashSet<>(); HashSet m_droppedBombs = new HashSet<>(); + HashMap m_mobGoals = new HashMap<>(); + + public enum Goal { + BombCarrier, + PlayerHarassment + } int m_createdMobs = 0; int m_killedMobs = 0; @@ -44,7 +56,7 @@ public class MobManager { } public void clear() { - for(LivingEntity e : m_livingMobs) { + for(Mob e : m_livingMobs) { e.remove(); } for(LivingEntity e : m_droppedBombs) { @@ -77,14 +89,76 @@ public class MobManager { return (double)m_killedMobs / (double)m_createdMobs; } - 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); + Player getNearestPlayer(Location location) { + Player nearestPlayer = null; + for(Player p : location.getWorld().getPlayers()) { + if (p.getGameMode() == GameMode.SPECTATOR) { + continue; + } else if (nearestPlayer == null) { + nearestPlayer = p; + } else if (location.distance(p.getLocation()) < location.distance(nearestPlayer.getLocation())) { + nearestPlayer = p; + } } + return nearestPlayer; + } + + LivingEntity getNearestBombObjective(Location location) { + LivingEntity nearest = null; + if (m_droppedBombs.size() > 0) { + for(LivingEntity ent : m_droppedBombs) { + if (nearest == null) { + nearest = ent; + } else if (location.distance(ent.getLocation()) < location.distance(nearest.getLocation())) { + nearest = ent; + } + } + } else if (m_bombCarriers.size() > 0) { + for(LivingEntity ent : m_bombCarriers) { + if (nearest == null) { + nearest = ent; + } else if (location.distance(ent.getLocation()) < location.distance(nearest.getLocation())) { + nearest = ent; + } + } + } + if (nearest == null) { + nearest = m_bombTarget; + } + return nearest; + } + + Player getBiggestPlayerThreat(Location location) { + Entity mostImportantObjective = getNearestBombObjective(location); + return getNearestPlayer(mostImportantObjective.getLocation()); + } + + LivingEntity getMobTarget(Mob mob) { + switch(m_mobGoals.get(mob)) { + case PlayerHarassment: + if (mob.getTarget() instanceof Player) { + return mob.getTarget(); + } else { + Player nearestThreat = getBiggestPlayerThreat(mob.getLocation()); + return nearestThreat; + } + case BombCarrier: + if (m_bombCarriers.contains(mob)) { + return m_bombTarget; + } else if (m_droppedBombs.size() > 0) { + return m_droppedBombs.iterator().next(); + } else { + return getBiggestPlayerThreat(mob.getLocation()); + } + default: + return m_bombTarget; + } + } + + public void addEntity(Mob entity, Goal goal) { + m_livingMobs.add(entity); + m_mobGoals.put(entity, goal); + entity.setTarget(getMobTarget(entity)); m_createdMobs += 1; } @@ -92,13 +166,13 @@ public class MobManager { return m_bombCarriers.size() + m_droppedBombs.size(); } - public LivingEntity addBombCarrier(LivingEntity entity) { + public Mob addBombCarrier(Mob entity) { m_bombCarriers.add(entity); - ((Mob)entity).setTarget(m_bombTarget); - return new BombCarrier(entity).inHand(); + entity.setTarget(m_bombTarget); + return (Mob)(new BombCarrier(entity).inHand()); } - public void removeBombCarrier(LivingEntity entity) { + public void removeBombCarrier(Mob entity) { m_bombCarriers.remove(entity); } @@ -109,13 +183,13 @@ public class MobManager { 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.setVisible(false); droppedBomb.setCustomNameVisible(true); droppedBomb.setGlowing(true); m_droppedBombs.add(droppedBomb); - for(LivingEntity ent : m_livingMobs) { + for(Mob ent : m_livingMobs) { if (!m_bombCarriers.contains(ent)) { - ((Mob)ent).setTarget(droppedBomb); + ent.setTarget(getMobTarget(ent)); } } return droppedBomb; @@ -143,11 +217,9 @@ public class MobManager { 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); - } + addBombCarrier((Mob)entityEvt.getDamager()); + for(Mob ent : m_livingMobs) { + ent.setTarget(getMobTarget(ent)); } } } else if (m_livingMobs.contains(evt.getEntity())) { @@ -155,28 +227,22 @@ public class MobManager { } } - 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.setVisible(false); bombStand.setCustomNameVisible(true); bombStand.setGlowing(true); + EntityEquipment equipment = bombStand.getEquipment(); + equipment.setHelmet(Items.makeBombTarget()); 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); - } + evt.setTarget(getMobTarget((Mob)evt.getEntity())); } } } diff --git a/src/main/java/gg/malloc/defense/engine/PlayerManager.java b/src/main/java/gg/malloc/defense/engine/PlayerManager.java index 35bd426..1491354 100644 --- a/src/main/java/gg/malloc/defense/engine/PlayerManager.java +++ b/src/main/java/gg/malloc/defense/engine/PlayerManager.java @@ -57,12 +57,16 @@ public class PlayerManager { return true; } + public void healPlayer(Player player) { + player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()); + player.setFoodLevel(40); + } + // Respawn player public boolean enterPlaying(Player player) { //m_log.fine("Respawning player " + player); player.setGameMode(Bukkit.getDefaultGameMode()); - player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()); - player.setFoodLevel(20); + healPlayer(player); return true; } @@ -107,6 +111,7 @@ public class PlayerManager { public boolean removePlayer(Player player) { //m_log.info("Removing player " + player); + healPlayer(player); requestTransition(player, State.Idle); m_playerStates.remove(player); m_playerReadyStates.remove(player); diff --git a/src/main/java/gg/malloc/defense/games/ScaledWaves.java b/src/main/java/gg/malloc/defense/games/ScaledWaves.java index c6e0112..9825d2b 100644 --- a/src/main/java/gg/malloc/defense/games/ScaledWaves.java +++ b/src/main/java/gg/malloc/defense/games/ScaledWaves.java @@ -46,7 +46,7 @@ public class ScaledWaves implements Game { MobWave(HashMap weights, int totalCount, int batches, boolean hasRavager) { m_batches = batches; - m_mobsPerBatch = Math.max(1, totalCount / batches); + m_mobsPerBatch = Math.max(1, totalCount / batches) * 3; m_spawnWeights = weights; m_hasRavager = hasRavager; } @@ -58,7 +58,7 @@ public class ScaledWaves implements Game { @Override public void spawnBatch(Spawner spawner, int batch) { - double healthScale = -0.5; + double healthScale = -0.7; AttributeModifier modifier = new AttributeModifier("Scaled Health", healthScale, AttributeModifier.Operation.MULTIPLY_SCALAR_1); assert(m_mobsPerBatch > 0); for(int i = 0; i < m_mobsPerBatch; i++) { @@ -72,9 +72,14 @@ public class ScaledWaves implements Game { } } assert(selectedType != null); - Mob newMob = (Mob)spawner.spawnBombCarrier(selectedType); + Mob newMob; + if (selectedType == EntityType.ZOMBIE) { + newMob = (Mob)spawner.spawnBombCarrier(selectedType); + newMob.getAttribute(Attribute.GENERIC_MAX_HEALTH).addModifier(modifier); + } else { + newMob = (Mob)spawner.spawnMob(selectedType); + } newMob.setCustomName("Mob " + i + "/" + m_mobsPerBatch); - newMob.getAttribute(Attribute.GENERIC_MAX_HEALTH).addModifier(modifier); } if (m_hasRavager) { Entity newMob = spawner.spawnMob(EntityType.RAVAGER); diff --git a/src/main/java/gg/malloc/defense/ui/BombCarrier.java b/src/main/java/gg/malloc/defense/ui/BombCarrier.java index 822177e..2a48eff 100644 --- a/src/main/java/gg/malloc/defense/ui/BombCarrier.java +++ b/src/main/java/gg/malloc/defense/ui/BombCarrier.java @@ -16,14 +16,12 @@ public class BombCarrier { public LivingEntity inHand() { EntityEquipment equipment = m_entity.getEquipment(); equipment.setItemInOffHand(Items.makeBombHelmet()); - //equipment.setItemInOffHandDropChance(0.0f); return m_entity; } public LivingEntity onHead() { EntityEquipment equipment = m_entity.getEquipment(); equipment.setHelmet(Items.makeBombHelmet()); - //equipment.setHelmetDropChance(0.0f); return m_entity; } } diff --git a/src/main/java/gg/malloc/defense/ui/Items.java b/src/main/java/gg/malloc/defense/ui/Items.java index c169093..f1ac571 100644 --- a/src/main/java/gg/malloc/defense/ui/Items.java +++ b/src/main/java/gg/malloc/defense/ui/Items.java @@ -27,4 +27,12 @@ public class Items { bombItem.setItemMeta(meta); return bombItem; } + + public static ItemStack makeBombTarget() { + ItemStack bombItem = new ItemStack(Material.CARVED_PUMPKIN); + ItemMeta meta = bombItem.getItemMeta(); + meta.setCustomModelData(35197); + bombItem.setItemMeta(meta); + return bombItem; + } }