diff --git a/CastleSiege.java b/CastleSiege.java new file mode 100644 index 0000000..d971773 --- /dev/null +++ b/CastleSiege.java @@ -0,0 +1,568 @@ +package com.cramer.castlesiege; + +import com.cramer.castlesiege.events.CastleSiegeEvents; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitPlayer; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.regions.Region; +import io.lumine.xikage.mythicmobs.MythicMobs; +import io.lumine.xikage.mythicmobs.mobs.ActiveMob; +import io.lumine.xikage.mythicmobs.mobs.MobManager; +import io.lumine.xikage.mythicmobs.mobs.MythicMob; +import org.bukkit.*; +import org.bukkit.block.data.BlockData; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scoreboard.*; + +import java.util.HashMap; +import java.util.Random; + +public class CastleSiege extends JavaPlugin +{ + public static int totalMobs = 0; + public static int totalVillagers = 12; + + private static boolean siegeOngoing = false; + private boolean regionSelected = false; + private static int timerScheduler = 0; + private static int minuteScheduler = 0; + private static int wave = 1; + private int eTime = 0; + private int xMin = 0; + private int xMax = 1; + private int zMin = 0; + private int zMax = 1; + private int y = 60; + private Region region; + private static BossBar bossBar; + private HashMap waveNames = createWaveNamesMap(); + private HashMap waveStartsMap = createWaveStartsMap(); + private HashMap> mobs = createWavesMap(); + + @Override + public void onEnable() + { + getServer().getPluginManager().registerEvents(new CastleSiegeEvents(), this); + Bukkit.getPluginCommand("siege").setExecutor(this); + getServer().getConsoleSender().sendMessage(ChatColor.GREEN + "[CastleSiege]: Plugin is enabled"); + } + + @Override + public void onDisable() + { + getServer().getConsoleSender().sendMessage(ChatColor.RED + "[CastleSiege]: Plugin is disabled"); + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) + { + if (cmd.getName().equalsIgnoreCase("siege")) + { + if (args.length > 0) + { + if (args[0].equalsIgnoreCase("start")) + { + if (!siegeOngoing) { + if (regionSelected) { + if (args.length > 1) + { + try { + int a = Integer.parseInt(args[1]); + StartGame(a); + } + catch (Exception e) + { + StartGame(1); + } + } + else { + StartGame(1); + } + } + else + { + sender.sendMessage("You must set a region for mobs to spawn. Select with WorldEdit and use /siege setregion."); + } + } + else + { + sender.sendMessage("Siege already in progress. End with /siege stop"); + } + return true; + } + else if (args[0].equalsIgnoreCase("stop")) + { + if (siegeOngoing) { + StopGame(); + Bukkit.broadcastMessage(ChatColor.GREEN + "[CastleSiege] The Siege has ended. Rejoice!"); + } + else + { + sender.sendMessage("There is no ongoing siege. Start one with /siege start"); + } + return true; + } + else if (args[0].equalsIgnoreCase("setregion")) + { + if (sender instanceof Player) + { + Player p = (Player) sender; + BukkitPlayer bPlayer = BukkitAdapter.adapt(p); + try { + region = WorldEdit.getInstance().getSessionManager().get(bPlayer).getSelection(bPlayer.getWorld()); + xMin = region.getMinimumPoint().getX(); + xMax = region.getMaximumPoint().getX() + 1; + zMin = region.getMinimumPoint().getZ(); + zMax = region.getMaximumPoint().getZ() + 1; + y = region.getMaximumPoint().getY() + 1; + regionSelected = true; + } catch (IncompleteRegionException e) { + sender.sendMessage("Invalid region. Use WorldEdit to select a region."); + } + } + else + { + sender.sendMessage("This command must be run by a player."); + } + return true; + } + } + } + + return false; + } + + + void GiveGear() + { + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.getGameMode() != GameMode.CREATIVE) { + p.getInventory().clear(); + p.getInventory().setHelmet(new ItemStack(Material.IRON_HELMET)); + p.getInventory().setChestplate(new ItemStack(Material.IRON_CHESTPLATE)); + p.getInventory().setLeggings(new ItemStack(Material.IRON_LEGGINGS)); + p.getInventory().setBoots(new ItemStack(Material.IRON_BOOTS)); + p.getInventory().setItem(0, new ItemStack(Material.WOODEN_SWORD)); + p.getInventory().setItem(1, new ItemStack(Material.APPLE, 5)); + } + // Clear everyone's EXP + p.setLevel(0); + p.setExp(0); + } + } + + void StartGame(int startWave) + { + totalMobs = 0; + totalVillagers = 12; + GiveGear(); + Bukkit.broadcastMessage(ChatColor.RED + "[CastleSiege] The Siege has begun! Prepare yourselves..."); + wave = startWave; + eTime = waveStartsMap.get(wave) - 1; + siegeOngoing = true; + // Every minute the scheduler will check if a new wave should start or not + minuteScheduler = Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() { + @Override + public void run() { + eTime += 1; + if (eTime == 0 || eTime == 2 || eTime == 4 || eTime == 6 || eTime == 8 || eTime == 11 || eTime == 13 + || eTime == 15 || eTime == 17 || eTime == 19 || eTime == 22 || eTime == 24 || eTime == 26 + || eTime == 28 || eTime == 30 || eTime == 35 || eTime == 37 || eTime == 39 || eTime == 41 + || eTime == 43 || eTime == 46 || eTime == 48 || eTime == 50 || eTime == 52 || eTime == 54 + || eTime == 57 || eTime == 59 || eTime == 61 || eTime == 63 || eTime == 65) { + if (eTime == 65) { + Bukkit.broadcastMessage(ChatColor.DARK_RED + "Final Wave..."); + StartWave(); + EndGame(); + } + else { + StartWave(); + } + } + } + }, 0L, 1200L); + } + + void StartWave() + { + Bukkit.broadcastMessage(ChatColor.DARK_RED + "Wave " + wave + "!"); + wave++; + bossBar = Bukkit.createBossBar("Wave " + (wave - 1) + ": " + waveNames.get(wave - 1), BarColor.RED, BarStyle.SOLID); + int waveTimeTemp = 120; + if ((((wave - 1) % 5) == 0) && (wave != 2)) + { + // All waves that are factors of 5 will have a 3 minute timer + bossBar.setColor(BarColor.PURPLE); + waveTimeTemp = 180; + if ((wave - 1) == 15) + { + // Wave 15 has a 5 minute timer + waveTimeTemp = 300; + } + } + final int waveTime = waveTimeTemp; + final double waveTime2 = waveTimeTemp; + // Every second the scheduler will update the boss bar to show the remaining time left in the wave + timerScheduler = Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() { + int seconds = waveTime; + @Override + public void run() { + if ((seconds -= 1) == 0) + { + StopTimer(); + } + else + { + bossBar.setProgress(seconds / waveTime2); + } + } + }, 0, 20); + // On the final wave, there is no timer + if (wave - 1 == 30) + { + Bukkit.getScheduler().cancelTask(timerScheduler); + } + bossBar.setVisible(true); + for (Player p : Bukkit.getOnlinePlayers()) { + bossBar.addPlayer(p); + } + Random random = new Random(); + HashMap thisWave = mobs.get(wave - 1); + // For each mob type in a given wave, it will spawn the appropriate number at random locations in the region + for (String mob : thisWave.keySet()) + { + int numMobs = thisWave.get(mob); + if (wave != 16 && wave != 31) + { + // Besides on the boss waves, scale the number of mobs appropriately for the player count + numMobs *= (3 + (Bukkit.getOnlinePlayers().size() - 5)*(0.3)); + } + for (int i = 0; i < numMobs; i++) + { + // Select a random block from the region + int xPos = random.nextInt(xMax - xMin) + xMin; + int zPos = random.nextInt(zMax - zMin) + zMin; + Location spawnLoc = new Location(Bukkit.getServer().getWorld("ZombieSiege"), xPos, y, zPos); + MobManager mobManager = MythicMobs.inst().getMobManager(); + mobManager.loadMobs(); + final MythicMob mm = mobManager.getMythicMob(mob); + mm.spawn(io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter.adapt(spawnLoc), 1); + } + UpdateScore(); + } + // MythicMobs has to reload after the mobs are spawned or they will not function correctly + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mm reload"); + } + + // Ends the siege prematurely from a command + void StopGame() + { + Bukkit.getScheduler().cancelTask(minuteScheduler); + StopTimer(); + totalMobs = 0; + UpdateScore(); + siegeOngoing = false; + } + + // Ends the siege properly, is only called on the final wave + void EndGame() + { + Bukkit.getScheduler().cancelTask(minuteScheduler); + Bukkit.getScheduler().cancelTask(timerScheduler); + } + + // Runs when the final boss is defeated + public static void WinGame() + { + bossBar.removeAll(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mm m killall"); + totalMobs = 0; + UpdateScore(); + siegeOngoing = false; + Bukkit.broadcastMessage(ChatColor.GREEN + "Congratulations! You have defeated the Necromancer and successfully" + + " defended against the siege."); + } + + // Ends the siege when all villagers die + static void LoseGame() + { + Bukkit.getScheduler().cancelTask(minuteScheduler); + StopTimer(); + Bukkit.broadcastMessage(ChatColor.DARK_RED + "Game over! You survived " + (wave - 1) + " waves."); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mm m killall"); + siegeOngoing = false; + } + + static void StopTimer() + { + bossBar.removeAll(); + Bukkit.getScheduler().cancelTask(timerScheduler); + } + + // Updates the Scoreboard which tracks zombie and villager count + public static void UpdateScore() + { + if (siegeOngoing) { + ScoreboardManager m = Bukkit.getScoreboardManager(); + Scoreboard b = m.getNewScoreboard(); + + Objective o = b.registerNewObjective("mobCount", "dummy", "Zombies: "); + o.setDisplaySlot(DisplaySlot.SIDEBAR); + o.setDisplayName(ChatColor.DARK_GREEN + "Zombie Siege"); + + if (totalVillagers <= 0) { + totalMobs = 0; + LoseGame(); + } + + Score mobCount = o.getScore("Zombies:"); + mobCount.setScore(totalMobs); + Score villagerCount = o.getScore("Villagers:"); + villagerCount.setScore(totalVillagers); + + for (Player p : Bukkit.getOnlinePlayers()) { + p.setScoreboard(b); + } + } + } + + HashMap createWaveNamesMap() { + HashMap m = new HashMap<>(); + m.put(1, "Basic"); + m.put(2, "Test Run"); + m.put(3, "Ramping Up"); + m.put(4, "Mayhem"); + m.put(5, "Buffed Mayhem"); + m.put(6, "Hot Dog"); + m.put(7, "Vomiting Tactics"); + m.put(8, "Meat Shields"); + m.put(9, "Too Many"); + m.put(10, "Mega Mayhem"); + m.put(11, "Popping Off"); + m.put(12, "Boomie"); + m.put(13, "Rangers"); + m.put(14, "Calm Before the Storm"); + m.put(15, "The Big One"); + m.put(16, "Can't Reach Me!"); + m.put(17, "Who's There?"); + m.put(18, "Tougher Shields"); + m.put(19, "Lethal Distraction"); + m.put(20, "Happy 20th!"); + m.put(21, "Trojan Horse"); + m.put(22, "Mega Shields"); + m.put(23, "Ranger Playhouse"); + m.put(24, "Melee Only"); + m.put(25, "The End is Near"); + m.put(26, "Had Enough?"); + m.put(27, "Target Practice"); + m.put(28, "Castle Rush"); + m.put(29, "Weakening"); + m.put(30, "Goodbye!"); + return m; + } + + HashMap createWaveStartsMap() + { + HashMap m = new HashMap<>(); + m.put(1, 0); + m.put(2, 2); + m.put(3, 4); + m.put(4, 6); + m.put(5, 8); + m.put(6, 11); + m.put(7, 13); + m.put(8, 15); + m.put(9, 17); + m.put(10, 19); + m.put(11, 22); + m.put(12, 24); + m.put(13, 26); + m.put(14, 28); + m.put(15, 30); + m.put(16, 35); + m.put(17, 37); + m.put(18, 39); + m.put(19, 41); + m.put(20, 43); + m.put(21, 46); + m.put(22, 48); + m.put(23, 50); + m.put(24, 52); + m.put(25, 54); + m.put(26, 57); + m.put(27, 59); + m.put(28, 61); + m.put(29, 63); + m.put(30, 65); + return m; + } + + HashMap> createWavesMap() + { + HashMap> m = new HashMap<>(); + HashMap wave1 = new HashMap<>(); + HashMap wave2 = new HashMap<>(); + HashMap wave3 = new HashMap<>(); + HashMap wave4 = new HashMap<>(); + HashMap wave5 = new HashMap<>(); + HashMap wave6 = new HashMap<>(); + HashMap wave7 = new HashMap<>(); + HashMap wave8 = new HashMap<>(); + HashMap wave9 = new HashMap<>(); + HashMap wave10 = new HashMap<>(); + HashMap wave11 = new HashMap<>(); + HashMap wave12 = new HashMap<>(); + HashMap wave13 = new HashMap<>(); + HashMap wave14 = new HashMap<>(); + HashMap wave15 = new HashMap<>(); + HashMap wave16 = new HashMap<>(); + HashMap wave17 = new HashMap<>(); + HashMap wave18 = new HashMap<>(); + HashMap wave19 = new HashMap<>(); + HashMap wave20 = new HashMap<>(); + HashMap wave21 = new HashMap<>(); + HashMap wave22 = new HashMap<>(); + HashMap wave23 = new HashMap<>(); + HashMap wave24 = new HashMap<>(); + HashMap wave25 = new HashMap<>(); + HashMap wave26 = new HashMap<>(); + HashMap wave27 = new HashMap<>(); + HashMap wave28 = new HashMap<>(); + HashMap wave29 = new HashMap<>(); + HashMap wave30 = new HashMap<>(); + wave1.put("NormalZombie", 15); + wave2.put("NormalZombie", 10); + wave2.put("FastZombie", 5); + wave3.put("NormalZombie", 10); + wave3.put("FastZombie", 3); + wave3.put("BuffZombie", 3); + wave4.put("NormalZombie", 10); + wave4.put("FastZombie", 3); + wave4.put("BuffZombie", 3); + wave4.put("FlameZombie", 3); + wave5.put("NormalZombie", 10); + wave5.put("FastZombie", 10); + wave5.put("BuffZombie", 5); + wave5.put("FlameZombie", 5); + wave6.put("BuffZombie", 5); + wave6.put("FlameZombie", 5); + wave6.put("SplitterZombie", 5); + wave7.put("BuffZombie", 5); + wave7.put("SplitterZombie", 6); + wave7.put("SpittingZombie", 4); + wave7.put("FlameZombie", 5); + wave8.put("BuffZombie", 10); + wave8.put("SpittingZombie", 10); + wave9.put("SplitterZombie", 15); + wave10.put("BuffZombie", 10); + wave10.put("SpittingZombie", 5); + wave10.put("FastZombie", 5); + wave10.put("FlameZombie", 5); + wave10.put("LeapingZombie", 5); + wave11.put("LeapingZombie", 5); + wave11.put("FlameZombie", 5); + wave11.put("SpittingZombie", 10); + wave12.put("SplitterZombie", 10); + wave12.put("FlameZombie", 5); + wave12.put("BomberZombie", 5); + wave13.put("BomberZombie", 10); + wave13.put("SpittingZombie", 10); + wave14.put("LeapingZombie", 10); + wave14.put("NormalZombie", 10); + wave15.put("GiantZombie", 1); + wave16.put("SpittingZombie", 10); + wave16.put("MountingZombie", 5); + wave17.put("MagicianZombie", 5); + wave17.put("BomberZombie", 5); + wave17.put("LeapingZombie", 5); + wave18.put("ArmoredZombie", 10); + wave18.put("BomberZombie", 5); + wave18.put("SpittingZombie", 5); + wave19.put("ArmoredZombie", 10); + wave19.put("MagicianZombie", 10); + wave20.put("ArmoredZombie", 10); + wave20.put("BomberZombie", 5); + wave20.put("SpittingZombie", 5); + wave20.put("FlameZombie", 5); + wave20.put("LeapingZombie", 5); + wave20.put("MountingZombie", 5); + wave21.put("SplitterZombie", 10); + wave21.put("MountingZombie", 10); + wave21.put("MagicianZombie", 5); + wave22.put("ArmoredZombie", 10); + wave22.put("FireballZombie", 10); + wave23.put("FireballZombie", 5); + wave23.put("BomberZombie", 5); + wave23.put("SpittingZombie", 5); + wave23.put("MountingZombie", 10); + wave24.put("ArmoredZombie", 10); + wave24.put("FlameZombie", 5); + wave24.put("FastZombie", 10); + wave24.put("SplitterZombie", 5); + wave25.put("ArmoredZombie", 15); + wave25.put("FireballZombie", 10); + wave25.put("BomberZombie", 5); + wave25.put("FastZombie", 10); + wave25.put("MountingZombie", 10); + wave26.put("ArmoredZombie", 20); + wave27.put("FireballZombie", 20); + wave28.put("FastZombie", 20); + wave28.put("MagicianZombie", 5); + wave29.put("FlameZombie", 10); + wave29.put("MountingZombie", 10); + wave30.put("Necromancer", 1); + m.put(1, wave1); + m.put(2, wave2); + m.put(3, wave3); + m.put(4, wave4); + m.put(5, wave5); + m.put(6, wave6); + m.put(7, wave7); + m.put(8, wave8); + m.put(9, wave9); + m.put(10, wave10); + m.put(11, wave11); + m.put(12, wave12); + m.put(13, wave13); + m.put(14, wave14); + m.put(15, wave15); + m.put(16, wave16); + m.put(17, wave17); + m.put(18, wave18); + m.put(19, wave19); + m.put(20, wave20); + m.put(21, wave21); + m.put(22, wave22); + m.put(23, wave23); + m.put(24, wave24); + m.put(25, wave25); + m.put(26, wave26); + m.put(27, wave27); + m.put(28, wave28); + m.put(29, wave29); + m.put(30, wave30); + return m; + } +} + + + + + + + + + + + + diff --git a/CastleSiegeEvents.java b/CastleSiegeEvents.java new file mode 100644 index 0000000..2b916b9 --- /dev/null +++ b/CastleSiegeEvents.java @@ -0,0 +1,112 @@ +package com.cramer.castlesiege.events; + +import com.cramer.castlesiege.CastleSiege; +import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicMobSpawnEvent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.scoreboard.*; + +public class CastleSiegeEvents implements Listener +{ + @EventHandler + public void onEntityExplode(EntityExplodeEvent e) + { + e.blockList().clear(); + } + + // Disable block placement except for creative mode users and fire charges + @EventHandler + public void onPlace(BlockPlaceEvent event) { + if (event.getPlayer().getGameMode().equals(GameMode.CREATIVE)) return; + if (event.getItemInHand().getType() == Material.FIRE_CHARGE) + { + return; + } + event.setCancelled(true); + } + + // Disable block breaks except for creative mode users and fire + @EventHandler + public void onBreak(BlockBreakEvent event) + { + if (event.getPlayer().getGameMode().equals(GameMode.CREATIVE)) return; + if (event.getBlock().getType() == Material.FIRE) + { + return; + } + event.setCancelled(true); + } + + // Give the player a Scoreboard when they join the game + @EventHandler + public void onPlayerJoin(PlayerJoinEvent e) + { + Player p = e.getPlayer(); + ScoreboardManager m = Bukkit.getScoreboardManager(); + Scoreboard b = m.getNewScoreboard(); + + Objective o = b.registerNewObjective("mobCount", "dummy", "Zombies: "); + o.setDisplaySlot(DisplaySlot.SIDEBAR); + o.setDisplayName(ChatColor.DARK_GREEN + "Zombie Siege"); + + Score mobCount = o.getScore("Zombies:"); + mobCount.setScore(CastleSiege.totalMobs); + Score villagerCount = o.getScore("Villagers:"); + villagerCount.setScore(CastleSiege.totalVillagers); + + p.setScoreboard(b); + } + + // Decrease the counter on the Scoreboard every time an entity dies, besides players + @EventHandler + public void onEntityDeath(EntityDeathEvent e) + { + if (e.getEntityType() != EntityType.PLAYER) + { + if (e.getEntityType() == EntityType.VILLAGER) { + CastleSiege.totalVillagers -= 1; + } + else { + CastleSiege.totalMobs -= 1; + if (e.getEntity().getEquipment().getHelmet().getType() == Material.WITHER_SKELETON_SKULL) + { + CastleSiege.WinGame(); + } + } + CastleSiege.UpdateScore(); + } + } + + // Update the zombie count when a new zombie is spawned + @EventHandler + public void onMythicMobSpawn(MythicMobSpawnEvent e) + { + CastleSiege.totalMobs++; + CastleSiege.UpdateScore(); + } + + // Disable zombie villager transformations, instead kill the villager + @EventHandler + public void onEntityTransform(EntityTransformEvent e) + { + e.getTransformedEntity().remove(); + } + + // Disable block burning + @EventHandler + public void onBlockBurn(BlockBurnEvent e) + { + e.setCancelled(true); + } +} \ No newline at end of file diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..127ac55 --- /dev/null +++ b/plugin.yml @@ -0,0 +1,10 @@ +name: CastleSiege +version: 1.0 +author: Jason Cramer +main: com.cramer.castlesiege.CastleSiege +api-version: 1.16 +depend: [MythicMobs, WorldEdit] +commands: + siege: + description: The base command for siege. + usage: /siege \ No newline at end of file