diff --git a/MobArena.jar b/MobArena.jar index 9fc61f3..5793b3e 100644 Binary files a/MobArena.jar and b/MobArena.jar differ diff --git a/src/com/garbagemule/MobArena/Arena.java b/src/com/garbagemule/MobArena/Arena.java index d255009..536bcdf 100644 --- a/src/com/garbagemule/MobArena/Arena.java +++ b/src/com/garbagemule/MobArena/Arena.java @@ -36,6 +36,7 @@ import org.bukkit.util.config.Configuration; import com.garbagemule.MobArena.MAMessages.Msg; import com.garbagemule.MobArena.util.WaveUtils; +import com.garbagemule.MobArena.waves.BossWave; import com.garbagemule.MobArena.waves.Wave; import com.garbagemule.MobArena.waves.Wave.WaveBranch; @@ -71,6 +72,7 @@ public class Arena //protected TreeSet recurrentWaves; protected TreeSet singleWaves; protected TreeSet recurrentWaves; + protected BossWave bossWave; // // // NEW IMPLEMENTATION @@ -247,23 +249,23 @@ public class Arena } public void forceEnd() - { - if (!running) - return; - + { Bukkit.getServer().getScheduler().cancelTask(spawnTaskId); for (Player p : getAllPlayers()) playerLeave(p); - + for (Entity e : monsters) e.remove(); + if (bossWave != null) + bossWave.clear(); + arenaPlayers.clear(); lobbyPlayers.clear(); readyPlayers.clear(); - monsters.clear(); + cleanup(); spawnTaskId = -1; } @@ -295,7 +297,8 @@ public class Arena MAUtils.clearInventory(p); restoreInvAndGiveRewards(p); - if (log.players.get(p) != null) log.players.get(p).lastWave = spawnThread.getWave() - 1; + if (log != null && log.players.get(p) != null) + log.players.get(p).lastWave = spawnThread.getWave() - 1; movePlayerToEntry(p); finishWithPlayer(p); endArena(); @@ -403,8 +406,7 @@ public class Arena public void restoreInvAndGiveRewards(final Player p) { - final List rewards = log.players.get(p).rewards; - //final List rewards = rewardMap.get(p); + final List rewards = log != null ? log.players.get(p).rewards : new LinkedList(); final boolean hadRewards = rewardedPlayers.contains(p); Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, @@ -556,6 +558,8 @@ public class Arena private void removeMonsters() { + if (bossWave != null) + bossWave.clear(); for (LivingEntity e : monsters) e.remove(); for (LivingEntity e : explodingSheep) @@ -865,10 +869,20 @@ public class Arena return name; } + public MobArena getPlugin() + { + return plugin; + } + public World getWorld() { return world; } + + public void setBossWave(BossWave bossWave) + { + this.bossWave = bossWave; + } public List getClasses() { @@ -880,6 +894,24 @@ public class Arena return spawnpoints.values(); } + public Location getBossSpawnpoint() + { + Location result = null; + + for (Map.Entry entry : spawnpoints.entrySet()) + { + if (!entry.getKey().matches("^*boss*$")) continue; + + result = entry.getValue(); + break; + } + + if (result != null) + return result; + + return spawnpoints.values().iterator().next(); + } + public int getPlayerCount() { return spawnThread.getPlayerCount(); diff --git a/src/com/garbagemule/MobArena/MACommands.java b/src/com/garbagemule/MobArena/MACommands.java index 8bebff5..eeef246 100644 --- a/src/com/garbagemule/MobArena/MACommands.java +++ b/src/com/garbagemule/MobArena/MACommands.java @@ -504,7 +504,7 @@ public class MACommands implements CommandExecutor return true; } - if (arena.arenaPlayers.isEmpty()) + if (arena.getAllPlayers().isEmpty()) { MAUtils.tellPlayer(sender, MAMessages.get(Msg.FORCE_END_EMPTY)); return true; diff --git a/src/com/garbagemule/MobArena/MAListener.java b/src/com/garbagemule/MobArena/MAListener.java index a28d897..5352887 100644 --- a/src/com/garbagemule/MobArena/MAListener.java +++ b/src/com/garbagemule/MobArena/MAListener.java @@ -42,17 +42,17 @@ import com.garbagemule.MobArena.MAMessages.Msg; public class MAListener implements ArenaListener { - private MobArena plugin; - private Arena arena; - - public MAListener(Arena arena, MobArena plugin) - { - this.arena = arena; - this.plugin = plugin; - } - - public void onBlockBreak(BlockBreakEvent event) - { + private MobArena plugin; + private Arena arena; + + public MAListener(Arena arena, MobArena plugin) + { + this.arena = arena; + this.plugin = plugin; + } + + public void onBlockBreak(BlockBreakEvent event) + { if (!arena.inRegion(event.getBlock().getLocation()) || arena.edit || (!arena.protect && arena.running)) return; @@ -74,10 +74,10 @@ public class MAListener implements ArenaListener } event.setCancelled(true); - } + } - public void onBlockPlace(BlockPlaceEvent event) - { + public void onBlockPlace(BlockPlaceEvent event) + { if (!arena.inRegion(event.getBlock().getLocation()) || arena.edit) return; @@ -94,19 +94,19 @@ public class MAListener implements ArenaListener // If the arena isn't running, or if the player isn't in the arena, cancel. event.setCancelled(true); - } + } - public void onCreatureSpawn(CreatureSpawnEvent event) - { + public void onCreatureSpawn(CreatureSpawnEvent event) + { if (!arena.inRegion(event.getLocation()) || event.getSpawnReason() == SpawnReason.CUSTOM) return; // If running == true, setCancelled(false), and vice versa. event.setCancelled(!arena.running); - } + } - public void onEntityExplode(EntityExplodeEvent event) - { + public void onEntityExplode(EntityExplodeEvent event) + { if (!arena.monsters.contains(event.getEntity()) && !arena.inRegionRadius(event.getLocation(), 10)) return; @@ -187,16 +187,16 @@ public class MAListener implements ArenaListener } } }, arena.repairDelay); - } + } - public void onEntityCombust(EntityCombustEvent event) - { + public void onEntityCombust(EntityCombustEvent event) + { if (arena.monsters.contains(event.getEntity())) event.setCancelled(true); - } + } - public void onEntityTarget(EntityTargetEvent event) - { + public void onEntityTarget(EntityTargetEvent event) + { if (!arena.running || event.isCancelled()) return; @@ -232,10 +232,10 @@ public class MAListener implements ArenaListener event.setCancelled(true); return; } - } + } - public void onEntityRegainHealth(EntityRegainHealthEvent event) - { + public void onEntityRegainHealth(EntityRegainHealthEvent event) + { if (!arena.running) return; if (!(event.getEntity() instanceof Player) || !arena.arenaPlayers.contains((Player)event.getEntity())) @@ -243,10 +243,10 @@ public class MAListener implements ArenaListener if (event.getRegainReason() == RegainReason.REGEN) event.setCancelled(true); - } + } - public void onEntityDeath(EntityDeathEvent event) - { + public void onEntityDeath(EntityDeathEvent event) + { if (event.getEntity() instanceof Player) { Player p = (Player) event.getEntity(); @@ -272,77 +272,95 @@ public class MAListener implements ArenaListener arena.resetIdleTimer(); return; } - } + } - public void onEntityDamage(EntityDamageEvent event) - { - if (!arena.running) return; - - EntityDamageByEntityEvent e = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null; - Entity damager = (e != null) ? e.getDamager() : null; - Entity damagee = event.getEntity(); - - // Pet wolf - if (damagee instanceof Wolf && arena.pets.contains(damagee)) - { - if (damager == null) - { - damagee.setFireTicks(32768); - event.setCancelled(true); - return; - } - else if (damager instanceof Player) - event.setCancelled(true); - else - event.setDamage(0); - - return; - } - // Arena player - else if (damagee instanceof Player) - { - if (arena.lobbyPlayers.contains(damagee)) - event.setCancelled(true); - else if (!arena.arenaPlayers.contains(damagee)) - return; - else if (!arena.detDamage && event.getCause() == DamageCause.BLOCK_EXPLOSION) - event.setCancelled(true); - else if (damager instanceof Player && !arena.pvp) - { - // if 'inLobby' fails, and 'not inArena' fails, 'inArena' is true + public void onEntityDamage(EntityDamageEvent event) + { + if (!arena.running) return; + + EntityDamageByEntityEvent e = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null; + Entity damager = (e != null) ? e.getDamager() : null; + Entity damagee = event.getEntity(); + + // Pet wolf + if (damagee instanceof Wolf && arena.pets.contains(damagee)) + { + if (damager == null) + { + damagee.setFireTicks(32768); event.setCancelled(true); return; - } - - if (!event.isCancelled()) - arena.log.players.get((Player) damagee).dmgTaken += event.getDamage(); - } - // Other LivingEntity - else if (arena.monsters.contains(damagee)) - { - if (damager instanceof Player) - { - if (!arena.arenaPlayers.contains(damager)) - { - event.setCancelled(true); - return; - } - - arena.log.players.get((Player) damager).dmgDone += event.getDamage(); - arena.log.players.get((Player) damager).hits++; - } - else if (damager instanceof Wolf && arena.pets.contains(damager)) - { - event.setDamage(1); - arena.log.players.get((Player) ((Wolf) damager).getOwner()).dmgDone += event.getDamage(); - } - else if (damager instanceof LivingEntity) - { - if (!arena.monsterInfight) - event.setCancelled(true); - } - } - } + } + else if (damager instanceof Player) + event.setCancelled(true); + else + event.setDamage(0); + + return; + } + // Arena player + else if (damagee instanceof Player) + { + if (arena.lobbyPlayers.contains(damagee)) + event.setCancelled(true); + else if (!arena.arenaPlayers.contains(damagee)) + return; + else if (!arena.detDamage && event.getCause() == DamageCause.BLOCK_EXPLOSION) + event.setCancelled(true); + else if (damager instanceof Player && !arena.pvp) + { + // if 'inLobby' fails, and 'not inArena' fails, 'inArena' is true + event.setCancelled(true); + return; + } + + if (!event.isCancelled()) + arena.log.players.get((Player) damagee).dmgTaken += event.getDamage(); + } + // Other LivingEntity + else if (arena.monsters.contains(damagee)) + { + if (damager instanceof Player) + { + if (!arena.arenaPlayers.contains(damager)) + { + event.setCancelled(true); + return; + } + + arena.log.players.get((Player) damager).dmgDone += event.getDamage(); + arena.log.players.get((Player) damager).hits++; + } + else if (damager instanceof Wolf && arena.pets.contains(damager)) + { + event.setDamage(1); + arena.log.players.get((Player) ((Wolf) damager).getOwner()).dmgDone += event.getDamage(); + } + else if (damager instanceof LivingEntity) + { + if (!arena.monsterInfight) + event.setCancelled(true); + } + + // Boss + if (arena.bossWave != null && damagee.equals(arena.bossWave.getEntity())) + { + // Subtract boss health, and reset actual entity health + arena.bossWave.subtractHealth(event.getDamage()); + arena.bossWave.getEntity().setHealth(200); + + // Set damage to 1 for knockback and feedback + event.setDamage(1); + + // If the boss is dead, remove the entity and create an explosion! + if (arena.bossWave.getHealth() <= 0) + { + arena.bossWave.clear(); + arena.bossWave = null; + } + } + } + } public void onPlayerAnimation(PlayerAnimationEvent event) { @@ -352,8 +370,8 @@ public class MAListener implements ArenaListener arena.log.players.get(event.getPlayer()).swings++; } - public void onPlayerDropItem(PlayerDropItemEvent event) - { + public void onPlayerDropItem(PlayerDropItemEvent event) + { if (arena.running && arena.shareInArena) return; Player p = event.getPlayer(); @@ -368,26 +386,26 @@ public class MAListener implements ArenaListener MAUtils.tellPlayer(p, MAMessages.get(Msg.LOBBY_DROP_ITEM)); event.setCancelled(true); - } + } - public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) - { + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) + { if (!arena.readyPlayers.contains(event.getPlayer()) && !arena.arenaPlayers.contains(event.getPlayer())) return; if (!arena.running) { - event.getBlockClicked().getRelative(event.getBlockFace()).setTypeId(0); + event.getBlockClicked().getRelative(event.getBlockFace()).setTypeId(0); event.setCancelled(true); return; } Block liquid = event.getBlockClicked().getRelative(event.getBlockFace()); arena.blocks.add(liquid); - } + } - public void onPlayerInteract(PlayerInteractEvent event) - { + public void onPlayerInteract(PlayerInteractEvent event) + { if (!arena.arenaPlayers.contains(event.getPlayer()) && !arena.lobbyPlayers.contains(event.getPlayer())) return; @@ -453,30 +471,30 @@ public class MAListener implements ArenaListener return; } - } + } - public void onPlayerQuit(PlayerQuitEvent event) - { + public void onPlayerQuit(PlayerQuitEvent event) + { Player p = event.getPlayer(); if (!arena.enabled || (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p))) return; plugin.getAM().arenaMap.remove(p); arena.playerLeave(p); - } + } - public void onPlayerKick(PlayerKickEvent event) - { + public void onPlayerKick(PlayerKickEvent event) + { Player p = event.getPlayer(); if (!arena.enabled || (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p))) return; plugin.getAM().arenaMap.remove(p); arena.playerLeave(p); - } + } - public void onPlayerTeleport(PlayerTeleportEvent event) - { + public void onPlayerTeleport(PlayerTeleportEvent event) + { if (arena.edit || !arena.enabled || !arena.setup || arena.allowWarp) return; @@ -492,7 +510,7 @@ public class MAListener implements ArenaListener { if (arena.inRegion(from)) { - if (to.equals(arena.arenaLoc) || to.equals(arena.lobbyLoc) || to.equals(arena.spectatorLoc) || to.equals(old)) + if (arena.inRegion(to) || to.equals(arena.arenaLoc) || to.equals(arena.lobbyLoc) || to.equals(arena.spectatorLoc) || to.equals(old)) return; MAUtils.tellPlayer(p, MAMessages.get(Msg.WARP_FROM_ARENA)); @@ -518,10 +536,10 @@ public class MAListener implements ArenaListener event.setCancelled(true); return; } - } + } - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) - { + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) + { Player p = event.getPlayer(); if (!arena.arenaPlayers.contains(p) && !arena.lobbyPlayers.contains(p)) @@ -536,5 +554,5 @@ public class MAListener implements ArenaListener event.setCancelled(true); MAUtils.tellPlayer(p, MAMessages.get(Msg.MISC_COMMAND_NOT_ALLOWED)); - } + } } diff --git a/src/com/garbagemule/MobArena/MobArena.java b/src/com/garbagemule/MobArena/MobArena.java index ccfd58f..c28bebf 100644 --- a/src/com/garbagemule/MobArena/MobArena.java +++ b/src/com/garbagemule/MobArena/MobArena.java @@ -76,8 +76,6 @@ public class MobArena extends JavaPlugin public void onDisable() { - // TODO: Re-implement this! - /* // Force all arenas to end. for (Arena arena : am.arenas) arena.forceEnd(); @@ -90,7 +88,7 @@ public class MobArena extends JavaPlugin Methods = null; System.out.println("[MobArena] Payment method was disabled. No longer accepting payments."); } - */ + System.out.println("[MobArena] disabled."); } @@ -137,6 +135,7 @@ public class MobArena extends JavaPlugin pm.registerEvent(Event.Type.ENTITY_DEATH, entityListener, Priority.Lowest, this); // Lowest because of Tombstone pm.registerEvent(Event.Type.ENTITY_REGAIN_HEALTH, entityListener, Priority.Normal, this); pm.registerEvent(Event.Type.ENTITY_EXPLODE, entityListener, Priority.Highest, this); + pm.registerEvent(Event.Type.EXPLOSION_PRIME, entityListener, Priority.Normal, this); pm.registerEvent(Event.Type.ENTITY_COMBUST, entityListener, Priority.Normal, this); pm.registerEvent(Event.Type.ENTITY_TARGET, entityListener, Priority.Normal, this); pm.registerEvent(Event.Type.CREATURE_SPAWN, entityListener, Priority.Highest, this); diff --git a/src/com/garbagemule/MobArena/util/WaveUtils.java b/src/com/garbagemule/MobArena/util/WaveUtils.java index 4bac3a7..2e76077 100644 --- a/src/com/garbagemule/MobArena/util/WaveUtils.java +++ b/src/com/garbagemule/MobArena/util/WaveUtils.java @@ -137,28 +137,34 @@ public class WaveUtils return null; // TODO: Generate waves properly. These are place-holders! - Wave result; + Wave result = null; if (branch == WaveBranch.RECURRENT) { int frequency = config.getInt(path + "frequency", 0); int priority = config.getInt(path + "priority", 0); int wave = config.getInt(path + "wave", frequency); - //if (type == WaveType.DEFAULT) + if (type == WaveType.DEFAULT) result = new DefaultWave(arena, name, wave, frequency, priority, config, path); - result.setGrowth(WaveGrowth.OLD); - //else - // result = new SpecialWave(arena, name, wave, frequency, priority, config, path); + else if (type == WaveType.SPECIAL) + result = new SpecialWave(arena, name, wave, frequency, priority, config, path); + else if (type == WaveType.SWARM) + result = new SwarmWave(arena, name, wave, frequency, priority, config, path); + else if (type == WaveType.BOSS) + result = new BossWave(arena, name, wave, frequency, priority, config, path); } else { int wave = config.getInt(path + "wave", 0); - //if (type == WaveType.DEFAULT) + if (type == WaveType.DEFAULT) result = new DefaultWave(arena, name, wave, config, path); - result.setGrowth(WaveGrowth.OLD); - //else - // result = new SpecialWave(arena, name, wave, config, path); + else if (type == WaveType.SPECIAL) + result = new SpecialWave(arena, name, wave, config, path); + else if (type == WaveType.SWARM) + result = new SwarmWave(arena, name, wave, config, path); + else if (type == WaveType.BOSS) + result = new BossWave(arena, name, wave, config, path); } return result; } @@ -249,7 +255,6 @@ public class WaveUtils for (String monster : monsters) { - //if (getEnumFromString(CreatureType.class, monster) != null) if (getEnumFromString(MACreature.class, monster) != null) continue; @@ -275,7 +280,7 @@ public class WaveUtils MAUtils.error("Missing monster type in '" + path); return false; } - else if (getEnumFromString(CreatureType.class, monster) == null) + else if (getEnumFromString(MACreature.class, monster) == null) { MAUtils.error("Invalid monster type '" + monster + "' in " + path); return false; @@ -304,7 +309,7 @@ public class WaveUtils MAUtils.error("Missing monster type in '" + path); return false; } - else if (getEnumFromString(CreatureType.class, monster) == null) + else if (getEnumFromString(MACreature.class, monster) == null) { MAUtils.error("Invalid monster type '" + monster + "' in " + path); return false; @@ -316,7 +321,7 @@ public class WaveUtils { for (String ability : abilities.split(",")) { - if (BossAbility.fromString(ability.trim().toUpperCase()) != null) + if (BossAbility.fromString(ability.trim().replaceAll("-", "_").toUpperCase()) != null) continue; MAUtils.error("Invalid boss ability '" + ability + "' in " + path); diff --git a/src/com/garbagemule/MobArena/waves/AbstractWave.java b/src/com/garbagemule/MobArena/waves/AbstractWave.java index f4e86cb..bf689bf 100644 --- a/src/com/garbagemule/MobArena/waves/AbstractWave.java +++ b/src/com/garbagemule/MobArena/waves/AbstractWave.java @@ -1,8 +1,16 @@ package com.garbagemule.MobArena.waves; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.entity.Creature; +import org.bukkit.entity.LivingEntity; import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; public abstract class AbstractWave implements Wave { @@ -66,6 +74,52 @@ public abstract class AbstractWave implements Wave return false; } + /** + * Spawn an MACreature in the given location. The actual LivingEntity + * spawned is returned. + * @param creature The MACreature to spawn + * @param loc The location to spawn the MACreature in + * @return The resulting LivingEntity + */ + public LivingEntity spawnMonster(MACreature creature, Location loc) + { + // Spawn and add to collection + LivingEntity e = creature.spawn(getWorld(), loc); + getArena().addMonster(e); + + // Grab a random target. + if (e instanceof Creature) + { + Creature c = (Creature) e; + c.setTarget(WaveUtils.getClosestPlayer(getArena(), e)); + } + + return e; + } + + /** + * Helper method for spawning a bunch of monsters over a + * collection of spawnpoints. Used by wave types DEFAULT, + * SPECIAL and SWARM. + * @param monsters A collection of monsters to spawn, and how many of each + * @param spawnpoints The spawnpoints to spawn the monsters over + */ + public void spawnAll(Map monsters, List spawnpoints) + { + Random random = new Random(); + int spawnpointCount = spawnpoints.size(); + int index = random.nextInt(spawnpointCount); + + for (Map.Entry entry : monsters.entrySet()) + { + for (int i = 0; i < entry.getValue(); i++) + { + spawnMonster(entry.getKey(), spawnpoints.get(index % spawnpointCount)); + index++; + } + } + } + // GETTERS public Arena getArena() { @@ -134,6 +188,7 @@ public abstract class AbstractWave implements Wave return "[name=" + waveName + ", wave=" + wave + ", frequency=" + frequency + - ", priority=" + priority + "]"; + ", priority=" + priority + + ", type=" + type + "]"; } } diff --git a/src/com/garbagemule/MobArena/waves/BossWave.java b/src/com/garbagemule/MobArena/waves/BossWave.java index a0a2507..1be97c2 100644 --- a/src/com/garbagemule/MobArena/waves/BossWave.java +++ b/src/com/garbagemule/MobArena/waves/BossWave.java @@ -1,23 +1,116 @@ package com.garbagemule.MobArena.waves; -import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import java.util.Set; -import org.bukkit.Location; +import org.bukkit.Bukkit; import org.bukkit.entity.Creature; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.config.Configuration; -import com.garbagemule.MobArena.waves.Wave.BossAbility; +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.MAUtils; +import com.garbagemule.MobArena.util.WaveUtils; -public class BossWave // TODO: implement/extend something? -{ - private Creature boss; - private Set abilities; +public class BossWave extends AbstractWave// TODO: implement/extend something? +{ + private MACreature boss; + private LivingEntity bossCreature; + private List abilities; private Set adds; - private int health; + private BossHealth bossHealth; + private int healthAmount; + private List taskList; - public BossWave(Creature boss) + // Recurrent + public BossWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) { - this.boss = boss; + super(arena, name, wave, frequency, priority); + load(config, path); + } + + // Single + public BossWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave); + load(config, path); + } + + private void load(Configuration config, String path) + { + setType(WaveType.BOSS); + taskList = new LinkedList(); + abilities = new LinkedList(); + + // Get monster and health + boss = MACreature.fromString(config.getString(path + "monster")); + bossHealth = WaveUtils.getEnumFromString(BossHealth.class, config.getString(path + "health"), BossHealth.MEDIUM); + + // Get abilities + String abilities = config.getString(path + "abilities"); + if (abilities != null) + { + for (String a : abilities.split(",")) + { + String ability = a.trim().replaceAll("-", "_").toUpperCase(); + System.out.println(ability); + addAbility(BossAbility.fromString(ability)); + } + } + } + + public void spawn(int wave) + { + // Spawn the boss and set the arena + bossCreature = boss.spawn(getWorld(), getArena().getBossSpawnpoint()); + if (bossCreature instanceof Creature) + ((Creature) bossCreature).setTarget(MAUtils.getClosestPlayer(bossCreature, getArena())); + getArena().addMonster(bossCreature); + getArena().setBossWave(this); + + // Set the health stuff + bossCreature.setHealth(200); + healthAmount = bossHealth.getAmount(getArena().getPlayerCount()); + + startAbilityTasks(); + } + + private void startAbilityTasks() + { + final int abilityCount = abilities.size(); + + int i = 1; + for (final BossAbility ability : abilities) + { + // Schedule the task + int abilityTask = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(getArena().getPlugin(), + new Runnable() + { + public void run() + { + ability.activate(getArena(), bossCreature); + } + }, 50*i, 50*abilityCount); + + // Add the task to the task list for cancelling later, and increment counter + taskList.add(abilityTask); + i++; + } + } + + public void cancelAbilityTasks() + { + for (Integer i : taskList) + Bukkit.getServer().getScheduler().cancelTask(i); + } + + public void clear() + { + cancelAbilityTasks(); + getArena().setBossWave(null); + getWorld().createExplosion(bossCreature.getLocation(), 1); + bossCreature.damage(32768); } public void addAbility(BossAbility ability) @@ -30,21 +123,23 @@ public class BossWave // TODO: implement/extend something? adds.add(creature); } - public void setHealth(int health) + public int getHealth() { - this.health = health; + return healthAmount; } - - public void spawn(int wave, Collection spawnpoints) + + public LivingEntity getEntity() { - // Spawn boss and adds - // Something like this, perhaps? Pseudo-code - // LivingEntity b = spawnCreature(bossType, random location) - // boss = (Creature) b; - // boss.setHealth(health); - // for (String a : ablts) - // abilities.add(BossAbility.fromString(a)); - // for (int i = 0; i < addCount; i++) - // adds.add(spawnCreature(addType, bossLocation); + return bossCreature; + } + + public void setHealth(int healthAmount) + { + this.healthAmount = healthAmount; + } + + public void subtractHealth(int amount) + { + healthAmount -= amount; } } diff --git a/src/com/garbagemule/MobArena/waves/DefaultWave.java b/src/com/garbagemule/MobArena/waves/DefaultWave.java index 8eba3c3..ba160bc 100644 --- a/src/com/garbagemule/MobArena/waves/DefaultWave.java +++ b/src/com/garbagemule/MobArena/waves/DefaultWave.java @@ -1,99 +1,75 @@ package com.garbagemule.MobArena.waves; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; -import java.util.TreeMap; import org.bukkit.Location; -import org.bukkit.entity.Creature; -import org.bukkit.entity.LivingEntity; import org.bukkit.util.config.Configuration; import com.garbagemule.MobArena.Arena; import com.garbagemule.MobArena.util.WaveUtils; -public class DefaultWave extends AbstractWave -{ - private int totalProbability = 0; - private Map probabilities = new TreeMap(); +public class DefaultWave extends NormalWave +{ + // Recurrent + public DefaultWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority, config, path); + load(config, path, WaveType.DEFAULT); + } - // Recurrent - public DefaultWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) - { - super(arena, name, wave, frequency, priority); - load(config, path); - } - - // Single - public DefaultWave(Arena arena, String name, int wave, Configuration config, String path) - { - super(arena, name, wave); - load(config, path); - } - - /** - * Prepare the wave for spawning by initializing the variables and - * populating the collections needed. - * @param config The config-file - * @param path The absolute path of the wave - */ - public void load(Configuration config, String path) - { - // Extract the monster probabilities and calculate the sum - totalProbability = 0; - int prob; - for (String m : config.getKeys(path + "monsters")) - { - prob = config.getInt(path + "monsters." + m, 1); - totalProbability += prob; - probabilities.put(totalProbability, MACreature.fromString(m)); - } - } + // Single + public DefaultWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave, config, path); + load(config, path, WaveType.DEFAULT); + } + /** + * Default waves spawn an amount of random monsters, picked from a + * map of probabilities. The amount to spawn depends on the wave + * number and player count. + */ public void spawn(int wave) { // Get the valid spawnpoints, and initialize counter List validSpawnpoints = WaveUtils.getValidSpawnpoints(getArena().getSpawnpoints(), getArena().getLivingPlayers()); - int noOfSpawnpoints = validSpawnpoints.size(); - + // Initialize the total amount of mobs to spawn int totalToSpawn = getGrowth().getAmount(wave, getArena().getPlayerCount()); - // Allocate some variables + // Spawn all the monsters + spawnAll(getMonstersToSpawn(totalToSpawn), validSpawnpoints); + System.out.println("WAVE SPAWN! Wave: " + wave + ", name: " + getName() + ", type: " + getType()); + } + + private Map getMonstersToSpawn(int totalToSpawn) + { + Map result = new HashMap(); Random random = new Random(); int randomNumber; - Location loc; + MACreature creature; - // Spawn monsters for (int i = 0; i < totalToSpawn; i++) { - // Grab the next location. - loc = validSpawnpoints.get(i % noOfSpawnpoints); + randomNumber = random.nextInt(getTotalProbability()); - // Grab a random number. - randomNumber = random.nextInt(totalProbability); - - // Find the monster that corresponds to the random number, and spawn it - for (Map.Entry entry : probabilities.entrySet()) + // Find the monster that corresponds to the random number, and increment its value + for (Map.Entry entry : getProbabilityMap().entrySet()) { if (randomNumber > entry.getKey()) continue; - // Spawn and add to collection - LivingEntity e = entry.getValue().spawn(getWorld(), loc); - getArena().addMonster(e); - - // Grab a random target. - if (e instanceof Creature) - { - Creature c = (Creature) e; - c.setTarget(WaveUtils.getClosestPlayer(getArena(), e)); - } - + creature = entry.getValue(); + if (result.containsKey(entry.getValue())) + result.put(creature, result.get(creature) + 1); + else + result.put(creature, 1); break; } } - System.out.println("WAVE SPAWN! Wave: " + wave + ", name: " + getName() + ", type: " + getType()); + return result; } } diff --git a/src/com/garbagemule/MobArena/waves/MACreature.java b/src/com/garbagemule/MobArena/waves/MACreature.java index d6a12eb..20850dd 100644 --- a/src/com/garbagemule/MobArena/waves/MACreature.java +++ b/src/com/garbagemule/MobArena/waves/MACreature.java @@ -13,20 +13,20 @@ import com.garbagemule.MobArena.util.WaveUtils; public enum MACreature { // Default creatures - ZOMBIES(CreatureType.ZOMBIE), - SKELETONS(CreatureType.SKELETON), - SPIDERS(CreatureType.SPIDER), - CREEPERS(CreatureType.CREEPER), - WOLVES(CreatureType.WOLF), + ZOMBIE(CreatureType.ZOMBIE), ZOMBIES(CreatureType.ZOMBIE), + SKELETON(CreatureType.SKELETON), SKELETONS(CreatureType.SKELETON), + SPIDER(CreatureType.SPIDER), SPIDERS(CreatureType.SPIDER), + CREEPER(CreatureType.CREEPER), CREEPERS(CreatureType.CREEPER), + WOLF(CreatureType.WOLF), WOLVES(CreatureType.WOLF), // Special creatures - ZOMBIE_PIGMEN(CreatureType.PIG_ZOMBIE), - POWERED_CREEPERS(CreatureType.CREEPER), - ANGRY_WOLVES(CreatureType.WOLF), - HUMANS(CreatureType.MONSTER), - SLIMES(CreatureType.SLIME), - GIANTS(CreatureType.GIANT), - GHASTS(CreatureType.GHAST); + ZOMBIE_PIGMAN(CreatureType.PIG_ZOMBIE), ZOMBIE_PIGMEN(CreatureType.PIG_ZOMBIE), + POWERED_CREEPER(CreatureType.CREEPER), POWERED_CREEPERS(CreatureType.CREEPER), + ANGRY_WOLF(CreatureType.WOLF), ANGRY_WOLVES(CreatureType.WOLF), + HUMAN(CreatureType.MONSTER), HUMANS(CreatureType.MONSTER), + SLIME(CreatureType.SLIME), SLIMES(CreatureType.SLIME), + GIANT(CreatureType.GIANT), GIANTS(CreatureType.GIANT), + GHAST(CreatureType.GHAST), GHASTS(CreatureType.GHAST); // Misc // EXPLODING_SHEEP(CreatureType.SHEEP), // Explode (power: 1) when close enough to players diff --git a/src/com/garbagemule/MobArena/waves/NormalWave.java b/src/com/garbagemule/MobArena/waves/NormalWave.java new file mode 100644 index 0000000..3488392 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/NormalWave.java @@ -0,0 +1,91 @@ +package com.garbagemule.MobArena.waves; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + +public abstract class NormalWave extends AbstractWave +{ + private int totalProbability = 0; + private Map probabilities = new TreeMap(); + + // Recurrent + public NormalWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority); + } + + // Single + public NormalWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave); + } + + /** + * Prepare the wave for spawning by initializing the variables and + * populating the collections needed. + * @param config The config-file + * @param path The absolute path of the wave + * @param type DEFAULT or SPECIAL + */ + public void load(Configuration config, String path, WaveType type) + { + // Set type and (for DEFAULT) growth. + setType(type); + if (type == WaveType.DEFAULT) + setGrowth(WaveUtils.getEnumFromString(WaveGrowth.class, config.getString(path + "growth"), WaveGrowth.OLD)); + + // Load monsters + int prob; + List monsters = config.getKeys(path + "monsters"); + if (monsters != null && !monsters.isEmpty()) + { + for (String m : config.getKeys(path + "monsters")) + { + prob = config.getInt(path + "monsters." + m, 1); + incTotalProbability(prob); + getProbabilityMap().put(getTotalProbability(), MACreature.fromString(m)); + } + } + else + { + if (type == WaveType.DEFAULT) + { + getProbabilityMap().put(10, MACreature.ZOMBIES); + getProbabilityMap().put(10, MACreature.SKELETONS); + getProbabilityMap().put(10, MACreature.SPIDERS); + getProbabilityMap().put(10, MACreature.CREEPERS); + getProbabilityMap().put(10, MACreature.WOLVES); + } + else if (type == WaveType.SPECIAL) + { + getProbabilityMap().put(10, MACreature.POWERED_CREEPERS); + getProbabilityMap().put(10, MACreature.ANGRY_WOLVES); + getProbabilityMap().put(10, MACreature.ZOMBIE_PIGMEN); + getProbabilityMap().put(10, MACreature.SLIMES); + } + } + } + + public int getTotalProbability() + { + return totalProbability; + } + + public void incTotalProbability(int value) + { + totalProbability += value; + } + + public Map getProbabilityMap() + { + return probabilities; + } + + public abstract void spawn(int wave); +} diff --git a/src/com/garbagemule/MobArena/waves/SpecialWave.java b/src/com/garbagemule/MobArena/waves/SpecialWave.java index 4b98d58..4a2f1d4 100644 --- a/src/com/garbagemule/MobArena/waves/SpecialWave.java +++ b/src/com/garbagemule/MobArena/waves/SpecialWave.java @@ -1,23 +1,69 @@ package com.garbagemule.MobArena.waves; -import com.garbagemule.MobArena.Arena; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; -public class SpecialWave extends AbstractWave -{ +import org.bukkit.Location; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + +public class SpecialWave extends NormalWave +{ // Recurrent - public SpecialWave(Arena arena, String name, int wave, int frequency, int priority) + public SpecialWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) { - super(arena, name, wave, frequency, priority); + super(arena, name, wave, frequency, priority, config, path); + load(config, path, WaveType.SPECIAL); } // Single - public SpecialWave(Arena arena, String name, int wave) + public SpecialWave(Arena arena, String name, int wave, Configuration config, String path) { - super(arena, name, wave); + super(arena, name, wave, config, path); + load(config, path, WaveType.SPECIAL); } public void spawn(int wave) { + // Get the valid spawnpoints, and initialize counter + List validSpawnpoints = WaveUtils.getValidSpawnpoints(getArena().getSpawnpoints(), getArena().getLivingPlayers()); + + // Spawn all the monsters + spawnAll(getMonstersToSpawn(getArena().getPlayerCount()), validSpawnpoints); System.out.println("WAVE SPAWN! Wave: " + wave + ", name: " + getName() + ", type: " + getType()); } + + private Map getMonstersToSpawn(int playerCount) + { + Map result = new HashMap(); + + Random random = new Random(); + int randomNumber = random.nextInt(getTotalProbability()); + for (Map.Entry entry : getProbabilityMap().entrySet()) + { + if (randomNumber > entry.getKey()) continue; + + int amount; + switch (entry.getValue()) + { + case POWERED_CREEPERS: + case ZOMBIE_PIGMEN: + case ANGRY_WOLVES: amount = playerCount * 2; break; + case SLIMES: amount = playerCount * 4; break; + case HUMANS: amount = playerCount + 2; break; + case GIANTS: + case GHASTS: amount = 2; + default: amount = playerCount + 1; break; + } + + result.put(entry.getValue(), amount); + break; + } + + return result; + } } diff --git a/src/com/garbagemule/MobArena/waves/SwarmWave.java b/src/com/garbagemule/MobArena/waves/SwarmWave.java new file mode 100644 index 0000000..60618c2 --- /dev/null +++ b/src/com/garbagemule/MobArena/waves/SwarmWave.java @@ -0,0 +1,58 @@ +package com.garbagemule.MobArena.waves; + +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.config.Configuration; + +import com.garbagemule.MobArena.Arena; +import com.garbagemule.MobArena.util.WaveUtils; + +public class SwarmWave extends AbstractWave +{ + private MACreature monster; + private SwarmAmount amount; + + // Recurrent + public SwarmWave(Arena arena, String name, int wave, int frequency, int priority, Configuration config, String path) + { + super(arena, name, wave, frequency, priority); + load(config, path); + } + + // Single + public SwarmWave(Arena arena, String name, int wave, Configuration config, String path) + { + super(arena, name, wave); + load(config, path); + } + + private void load(Configuration config, String path) + { + // Get the monster type + monster = MACreature.fromString(config.getString(path + "monster")); + + // And the amount + amount = WaveUtils.getEnumFromString(SwarmAmount.class, config.getString(path + "amount"), SwarmAmount.LOW); + } + + public void spawn(int wave) + { + // Get the valid spawnpoints, and initialize counter + List validSpawnpoints = WaveUtils.getValidSpawnpoints(getArena().getSpawnpoints(), getArena().getLivingPlayers()); + + // Spawn the hellians! + spawnAll(monster, amount.getAmount(getArena().getPlayerCount()), validSpawnpoints); + } + + public void spawnAll(MACreature monster, int amount, List spawnpoints) + { + int spawnpointCount = spawnpoints.size(); + for (int i = 0; i < amount; i++) + { + LivingEntity e = spawnMonster(monster, spawnpoints.get(i % spawnpointCount)); + e.setHealth(1); + } + } +} diff --git a/src/com/garbagemule/MobArena/waves/Wave.java b/src/com/garbagemule/MobArena/waves/Wave.java index 157531e..11ecae9 100644 --- a/src/com/garbagemule/MobArena/waves/Wave.java +++ b/src/com/garbagemule/MobArena/waves/Wave.java @@ -1,5 +1,17 @@ package com.garbagemule.MobArena.waves; +import java.util.LinkedList; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.entity.Creature; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import com.garbagemule.MobArena.Arena; import com.garbagemule.MobArena.util.WaveUtils; public interface Wave @@ -48,17 +60,118 @@ public interface Wave public enum BossAbility { - ARROWS, FIREBALLS, RING_OF_FIRE; + ARROWS, FIREBALLS, FIRE_AURA, THROW_TARGET, THROW_NEARBY, FETCH_TARGET, FETCH_DISTANT; public static BossAbility fromString(String string) { return WaveUtils.getEnumFromString(BossAbility.class, string); } + + public void activate(Arena arena, LivingEntity boss) + { + LivingEntity target = getTarget(boss); + Location bLoc = boss.getLocation(); + Location loc; + + switch (this) + { + // Fire an arrow in the direction the boss is facing. + case ARROWS: + System.out.println("Shooting arrow"); + boss.shootArrow(); + break; + // Hurl a fireball in the direction the boss is facing. + case FIREBALLS: + System.out.println("Shooting fireball"); + loc = bLoc.add(bLoc.getDirection().normalize().multiply(2).toLocation(boss.getWorld(), bLoc.getYaw(), bLoc.getPitch())); + Fireball fireball = boss.getWorld().spawn(loc, Fireball.class); + fireball.setIsIncendiary(false); + break; + // Set fire to all players nearby. + case FIRE_AURA: + System.out.println("Fire aura"); + for (Player p : getNearbyPlayers(arena, boss, 5)) + p.setFireTicks(20); + break; + // Throw target back + case THROW_TARGET: + System.out.println("Throw target"); + if (target != null) + { + loc = target.getLocation(); + Vector v = new Vector(loc.getX() - bLoc.getX(), 0, loc.getZ() - bLoc.getZ()); + target.setVelocity(v.normalize().setY(0.8)); + } + break; + // Throw nearby players back + case THROW_NEARBY: + System.out.println("Throw nearby"); + for (Player p : getNearbyPlayers(arena, boss, 5)) + { + loc = p.getLocation(); + Vector v = new Vector(loc.getX() - bLoc.getX(), 0, loc.getZ() - bLoc.getZ()); + p.setVelocity(v.normalize().setY(0.8)); + } + break; + // Warp target to boss + case FETCH_TARGET: + System.out.println("Fetch target"); + if (target != null) target.teleport(boss); + break; + // Warp nearby players to boss + case FETCH_DISTANT: + System.out.println("Fetch distant"); + for (Player p : getDistantPlayers(arena, boss, 8)) + p.teleport(boss); + default: + break; + } + } + + private LivingEntity getTarget(LivingEntity entity) + { + if (entity instanceof Creature) + { + LivingEntity target = ((Creature) entity).getTarget(); + if (target instanceof Player) + return target; + } + return null; + } + + private List getNearbyPlayers(Arena arena, Entity boss, int x) + { + List result = new LinkedList(); + for (Entity e : boss.getNearbyEntities(x, x, x)) + if (arena.getLivingPlayers().contains(e)) + result.add((Player) e); + return result; + } + + private List getDistantPlayers(Arena arena, Entity boss, int x) + { + List result = new LinkedList(); + for (Player p : arena.getLivingPlayers()) + if (p.getLocation().distanceSquared(boss.getLocation()) > x*x) + result.add(p); + return result; + } } public enum BossHealth { - LOW, MEDIUM, HIGH; + LOW(5), MEDIUM(8), HIGH(12), PSYCHO(20); + private int multiplier; + + private BossHealth(int multiplier) + { + this.multiplier = multiplier; + } + + public int getAmount(int playerCount) + { + return (playerCount + 1) * 20 * multiplier; + } public static BossHealth fromString(String string) { @@ -68,7 +181,18 @@ public interface Wave public enum SwarmAmount { - LOW, MEDIUM, HIGH, PSYCHO; + LOW(10), MEDIUM(20), HIGH(30), PSYCHO(50); + private int multiplier; + + private SwarmAmount(int multiplier) + { + this.multiplier = multiplier; + } + + public int getAmount(int playerCount) + { + return Math.max(1, playerCount / 2) * multiplier; + } public static SwarmAmount fromString(String string) { @@ -119,6 +243,12 @@ public interface Wave * @return The name */ public String getName(); + + /** + * Get the arena of this wave. + * @return The arena + */ + public Arena getArena(); /** * Set the wave's growth