diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java index 836ce77b8..342933ae3 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java @@ -1,6 +1,5 @@ package mineplex.game.clans; -import org.bukkit.Location; import org.bukkit.World; import org.bukkit.craftbukkit.v1_7_R4.CraftWorld; import org.bukkit.plugin.java.JavaPlugin; @@ -32,7 +31,6 @@ import mineplex.core.teleport.Teleport; import mineplex.core.updater.FileUpdater; import mineplex.core.updater.Updater; import mineplex.game.clans.clans.ClansManager; -import mineplex.game.clans.clans.worldevent.WorldEventManager; import mineplex.game.clans.items.GearManager; import mineplex.game.clans.shop.building.BuildingShop; import mineplex.game.clans.shop.pvp.PvpShop; @@ -43,7 +41,8 @@ public class Clans extends JavaPlugin //Modules private CoreClientManager _clientManager; - private DonationManager _donationManager; + private DonationManager _donationManager; + private ClansManager _clansManager; @Override @@ -97,11 +96,11 @@ public class Clans extends JavaPlugin new FriendManager(this, _clientManager, preferenceManager, portal); new InventoryManager(this, _clientManager); - ClansManager clans = new ClansManager(this, serverStatusManager.getCurrentServerName(), _clientManager, _donationManager, blockRestore, teleport, webServerAddress); + _clansManager = new ClansManager(this, serverStatusManager.getCurrentServerName(), _clientManager, _donationManager, blockRestore, teleport, webServerAddress); new Recipes(this); new Farming(this); - new BuildingShop(clans, _clientManager, _donationManager); - new PvpShop(clans, _clientManager, _donationManager); + new BuildingShop(_clansManager, _clientManager, _donationManager); + new PvpShop(_clansManager, _clientManager, _donationManager); // Disable spigot item merging for (World world : getServer().getWorlds()) @@ -119,4 +118,11 @@ public class Clans extends JavaPlugin MinecraftServer.getServer().getPropertyManager().setProperty("debug", true); } + + @Override + public void onDisable() + { + // Need to notify WorldEventManager of server shutdown, this seemed like the only decent way to do it + _clansManager.onDisable(); + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java index 6ac30790b..dd071fcfd 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java @@ -586,4 +586,12 @@ public class ClansManager extends MiniClientPlugin implements IRelat { return TIME_ZONE; } + + @Override + public void disable() + { + // Kind of confusing, Clans.java calls this so that we can pass the disable event to WorldEventManager + // This is so that we can prevent any permanent world changes with events + _worldEvent.onDisable(); + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/scoreboard/ClansScoreboardManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/scoreboard/ClansScoreboardManager.java index 2d47d3880..2b7a2f3a1 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/scoreboard/ClansScoreboardManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/scoreboard/ClansScoreboardManager.java @@ -36,8 +36,6 @@ public class ClansScoreboardManager extends MiniPlugin ScoreboardData data = _scoreboardManager.getData("default", true); - data.writeEmpty(); - data.writeElement(new ScoreboardElementClan(_clansManager)); data.writeEmpty(); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/ConcreteWorldEventFactory.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/ConcreteWorldEventFactory.java index 098972609..0b1514332 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/ConcreteWorldEventFactory.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/ConcreteWorldEventFactory.java @@ -1,7 +1,15 @@ package mineplex.game.clans.clans.worldevent; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + import org.bukkit.Location; +import net.minecraft.server.v1_7_R4.World; + import mineplex.game.clans.clans.worldevent.event.WorldEvent; import mineplex.game.clans.clans.worldevent.event.boss.slime.SlimeBoss; import mineplex.game.clans.clans.worldevent.event.kinghill.KingHill; @@ -9,10 +17,17 @@ import mineplex.game.clans.clans.worldevent.event.kinghill.KingHill; public class ConcreteWorldEventFactory implements WorldEventFactory { private WorldEventManager _eventManager; + private List> _eventClasses; + private Random _random; public ConcreteWorldEventFactory(WorldEventManager eventManager) { _eventManager = eventManager; + _random = new Random(); + + _eventClasses = new ArrayList>(); + _eventClasses.add(SlimeBoss.class); + _eventClasses.add(KingHill.class); } @Override @@ -20,10 +35,10 @@ public class ConcreteWorldEventFactory implements WorldEventFactory { if (name.equalsIgnoreCase("slime")) { - return new SlimeBoss(_eventManager, _eventManager.getDamage(), location); + return new SlimeBoss(_eventManager, location); } else if (name.equalsIgnoreCase("kinghill")) { - return new KingHill(_eventManager, _eventManager.getDamage(), location); + return new KingHill(_eventManager, location); } else { @@ -34,6 +49,18 @@ public class ConcreteWorldEventFactory implements WorldEventFactory @Override public WorldEvent random(Location location) { - return null; + WorldEvent worldEvent = null; + Class clazz = _eventClasses.get(_random.nextInt(_eventClasses.size())); + + try + { + worldEvent = clazz.getConstructor(WorldEventManager.class, Location.class).newInstance(_eventManager, location); + } + catch (Exception e) + { + e.printStackTrace(); + } + + return worldEvent; } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/EventTerrainFinder.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/EventTerrainFinder.java new file mode 100644 index 000000000..0f20cb4bd --- /dev/null +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/EventTerrainFinder.java @@ -0,0 +1,179 @@ +package mineplex.game.clans.clans.worldevent; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import mineplex.core.common.util.UtilBlock; +import mineplex.core.common.util.UtilMath; +import mineplex.game.clans.clans.ClansManager; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; + +public class EventTerrainFinder +{ + private WorldEventManager _eventManager; + private ClansManager _clansManager; + + public EventTerrainFinder(WorldEventManager eventManager, ClansManager clansManager) + { + _eventManager = eventManager; + _clansManager = clansManager; + } + + public double posNeg() + { + if (Math.random() > 0.5) + return -1; + return 1; + } + + public Location findArea(World world, int size, int vert) + { + for (int i=0 ; i<20 ; i++) + { + int x = 0; + int z = 0; + + //X Side + if (Math.random() > 0.5) + { + x = (int) (posNeg() * ((400 + size) + UtilMath.r(200 - (size*2)))); + z = (int) (posNeg() * UtilMath.r(600 - size)); + } + //Z Side + else + { + z = (int) (posNeg() * ((400 + size) + UtilMath.r(200 - (size*2)))); + x = (int) (posNeg() * UtilMath.r(600 - size)); + } + + Location loc = UtilBlock.getHighest(world, x, z).getLocation(); + + int total = ((size*2)+1)*((size*2)+1); + + int liquid = 0; + HashMap heights = new HashMap(); + + HashSet chunks = new HashSet(); + boolean wilderness = true; + + //Gather Data + for (x=-size ; x<=size && wilderness ; x++) + for (z=-size ; z<=size && wilderness ; z++) + { + Block block = UtilBlock.getHighest(world, loc.getBlockX()+x, loc.getBlockZ()+z); + + if (!chunks.contains(block.getChunk())) + if (_clansManager.getClanUtility().isClaimed(block.getLocation())) + { + chunks.add(block.getChunk()); + wilderness = false; + break; + } + + //Liquid + if (block.getRelative(BlockFace.DOWN).isLiquid() || block.isLiquid()) + liquid++; + + //Height + int heightDiff = block.getY() - loc.getBlockY(); + if (!heights.containsKey(heightDiff)) heights.put(heightDiff, 1); + else heights.put(heightDiff, heights.get(heightDiff) + 1); + } + + if (!wilderness) + continue; + + //Too Watery + if ((double)liquid/(double)total > 0.25) + continue; + + //Too Height Variable + int withinHeight = 0; + for (int h=-vert ; h<=vert ; h++) + { + if (!heights.containsKey(h)) + continue; + + withinHeight += heights.get(h); + } + + if ((double)withinHeight/(double)total < 0.9) + continue; + + //Success + return loc; + } + + return null; + } + + public Location locateSpace(Location areaSource, int areaRadius, int xArea, int yArea, int zArea, boolean replaceBlocks, boolean aboveOther, Set otherBlock) + { + for (int i=0 ; i<20 ; i++) + { + int x = UtilMath.r(areaRadius*2) - areaRadius + areaSource.getBlockX(); + int z = UtilMath.r(areaRadius*2) - areaRadius + areaSource.getBlockZ(); + + Block block = UtilBlock.getHighest(areaSource.getWorld(), x, z); + + if (!aboveOther) + if (otherBlock.contains(block.getRelative(BlockFace.DOWN))) + continue; + + boolean valid = true; + + int overlaps = 0; + + //Previous + for (x=-xArea ; x<=xArea ; x++) + { + for (z=-zArea ; z<=zArea ; z++) + { + for (int y=0 ; y<=yArea ; y++) + { + //Check Blocks + Block cur = areaSource.getWorld().getBlockAt(block.getX()+x, block.getY()+y, block.getZ()+z); + + if (cur.getRelative(BlockFace.DOWN).isLiquid()) + { + valid = false; + break; + } + + if (otherBlock.contains(cur)) + { + valid = false; + break; + } + + //Check Area + if (!UtilBlock.airFoliage(cur)) + overlaps += 1; + } + + if (!valid) + break; + } + + if (!valid) + break; + } + + if (!replaceBlocks && overlaps > 0) + continue; + + if (!valid) + continue; + + return block.getLocation(); + } + + return null; + } +} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/WorldEventManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/WorldEventManager.java index 7b280fd2c..52cc202cc 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/WorldEventManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/WorldEventManager.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Random; import java.util.Set; import org.bukkit.Bukkit; @@ -20,6 +21,9 @@ import org.bukkit.plugin.java.JavaPlugin; import mineplex.core.MiniPlugin; import mineplex.core.common.util.C; +import mineplex.core.common.util.UtilPlayer; +import mineplex.core.common.util.UtilServer; +import mineplex.core.common.util.UtilWorld; import mineplex.core.scoreboard.ScoreboardManager; import mineplex.core.scoreboard.elements.ScoreboardElement; import mineplex.core.updater.UpdateType; @@ -35,17 +39,26 @@ public class WorldEventManager extends MiniPlugin implements ScoreboardElement private final WorldEventFactory _factory; private final List _events; + private Random _random; private ClansManager _clansManager; + private EventTerrainFinder _terrainFinder; private DamageManager _damageManager; + private long _lastEventEnd; + private long _nextEventStart; + public WorldEventManager(JavaPlugin plugin, ClansManager clansManager, DamageManager damageManager) { super("World Event", plugin); + _random = new Random(); + _terrainFinder = new EventTerrainFinder(this, clansManager); _clansManager = clansManager; _damageManager = damageManager; _factory = new ConcreteWorldEventFactory(this); _events = new LinkedList(); + _lastEventEnd = System.currentTimeMillis(); + _nextEventStart = System.currentTimeMillis() + 30000; } @Override @@ -63,6 +76,8 @@ public class WorldEventManager extends MiniPlugin implements ScoreboardElement if (event.getType() != UpdateType.SEC) return; + boolean removed = false; + Iterator iterator = _events.iterator(); while (iterator.hasNext()) { @@ -72,8 +87,44 @@ public class WorldEventManager extends MiniPlugin implements ScoreboardElement worldEvent.cleanup(); HandlerList.unregisterAll(worldEvent); iterator.remove(); + + removed = true; + _lastEventEnd = System.currentTimeMillis(); } } + + if (removed && _events.size() == 0) + { + _nextEventStart = generateNextEventTime(); + } + } + + @EventHandler + public void startEvent(UpdateEvent event) + { + if (event.getType() != UpdateType.SEC) + return; + + if (_events.size() == 0 && System.currentTimeMillis() > _nextEventStart && UtilServer.getPlayers().length > 0) + { + Bukkit.broadcastMessage("Starting new event from random..."); + startRandomEvent(); + } + } + + private void startRandomEvent() + { + Location location = _terrainFinder.findArea(Bukkit.getWorlds().get(0), 30, 30); + if (location != null) + { + WorldEvent event = _factory.random(location); + initializeEvent(event); + } + else + { + // Try again in 5 minutes + _nextEventStart = System.currentTimeMillis() + 300000; + } } private void initializeEvent(WorldEvent event) @@ -102,6 +153,8 @@ public class WorldEventManager extends MiniPlugin implements ScoreboardElement HandlerList.unregisterAll(event); iterator.remove(); } + + _nextEventStart = generateNextEventTime(); } public ClansManager getClans() @@ -120,19 +173,29 @@ public class WorldEventManager extends MiniPlugin implements ScoreboardElement addCommand(new WorldEventCommand(this)); } + private long generateNextEventTime() + { + // 45 Minutes + 0 - 15 Minutes + long waitTime = 2700000L + _random.nextInt(900000); + return System.currentTimeMillis() + waitTime; + } + @Override public ArrayList getLines(ScoreboardManager manager, Player player) { ArrayList output = new ArrayList(); + if (_events.size() > 0) + output.add(C.cAqua + C.Bold + "Event"); + Iterator iterator = _events.iterator(); while (iterator.hasNext()) { WorldEvent event = iterator.next(); Location eventLocation = event.getCenterLocation(); String locationString = eventLocation.getBlockX() + ", " + eventLocation.getBlockY() + ", " + eventLocation.getBlockZ(); - output.add(C.cGreen + "Event: " + C.cWhite + event.getName()); - output.add(C.cGreen + "Location: " + C.cWhite + locationString); + output.add(" " + C.cWhite + event.getName()); + output.add(" " + C.cWhite + locationString); ArrayList scoreboardLines = event.getScoreboardLines(); if (scoreboardLines != null) output.addAll(event.getScoreboardLines()); @@ -146,6 +209,7 @@ public class WorldEventManager extends MiniPlugin implements ScoreboardElement @EventHandler public void gear(PlayerCommandPreprocessEvent event) { + // TODO: Remove if (event.getMessage().equalsIgnoreCase("/dgear")) { PlayerInventory i = event.getPlayer().getInventory(); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/creature/EventCreature.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/creature/EventCreature.java index 5bf5de4b1..a0d3929a9 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/creature/EventCreature.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/creature/EventCreature.java @@ -246,5 +246,7 @@ public abstract class EventCreature implements Listener updateEntityHealth(); applyDamage(event.GetDamage()); + + _event.updateLastActive(); } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/WorldEvent.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/WorldEvent.java index 559fb20ad..e59c1176f 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/WorldEvent.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/WorldEvent.java @@ -26,6 +26,9 @@ import mineplex.minecraft.game.core.damage.DamageManager; public abstract class WorldEvent implements Listener { + // 20 Minutes + private static final int ACTIVE_TIMEOUT = 1200000; + private WorldEventManager _eventManager; private DamageManager _damageManager; @@ -35,16 +38,17 @@ public abstract class WorldEvent implements Listener private EventMap _map; private Random _random; private int _ticks; + private long _lastActive; // Creatures private List _creatures; // Block Restore private HashMap _blocks; - public WorldEvent(WorldEventManager eventManager, DamageManager damageManager, String name, Location cornerLocation) + public WorldEvent(WorldEventManager eventManager, String name, Location cornerLocation) { _eventManager = eventManager; - _damageManager = damageManager; + _damageManager = eventManager.getDamage(); _name = name; _state = EventState.PREPARE; @@ -55,6 +59,7 @@ public abstract class WorldEvent implements Listener _creatures = new ArrayList(); _blocks = new HashMap(); + _lastActive = System.currentTimeMillis(); } public final void start() @@ -176,6 +181,16 @@ public abstract class WorldEvent implements Listener _blocks.clear(); } + public long getLastActive() + { + return _lastActive; + } + + public void updateLastActive() + { + _lastActive = System.currentTimeMillis(); + } + public void setMap(EventMap map, final Runnable onComplete) { _map = map; @@ -211,6 +226,11 @@ public abstract class WorldEvent implements Listener return null; } + public int getRandomRange(int min, int max) + { + return min + _random.nextInt(max - min); + } + @EventHandler public void tick(UpdateEvent event) { @@ -220,4 +240,15 @@ public abstract class WorldEvent implements Listener _ticks++; customTick(); } + + @EventHandler + public void endInactive(UpdateEvent event) + { + if (event.getType() != UpdateType.SEC) + return; + + long diff = System.currentTimeMillis() - getLastActive(); + if (diff > ACTIVE_TIMEOUT) + setState(EventState.END); + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/SlimeBoss.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/SlimeBoss.java index 3d839b14b..ee42ca3f4 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/SlimeBoss.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/SlimeBoss.java @@ -23,9 +23,9 @@ public class SlimeBoss extends WorldEvent private static final int MAX_SIZE = 10; private static final int MIN_SIZE = 1; - public SlimeBoss(WorldEventManager eventManager, DamageManager damageManager, Location corner) + public SlimeBoss(WorldEventManager eventManager, Location corner) { - super(eventManager, damageManager, "Slime King", corner); + super(eventManager, "Slime King", corner); Schematic schematic = null; diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/ability/RocketAbility.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/ability/RocketAbility.java index 4dd9d6d62..c5a605b18 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/ability/RocketAbility.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/boss/slime/ability/RocketAbility.java @@ -95,7 +95,7 @@ public class RocketAbility extends SlimeAbility implements IThrown private void fireRocket(Player target) { Location loc = getSlime().getEntity().getEyeLocation(); - loc.add(loc.getDirection().normalize().multiply(2)); + loc.add(UtilAlg.getTrajectory2d(loc, target.getLocation()).multiply(2)); Slime projectile = loc.getWorld().spawn(loc, Slime.class); projectile.setSize(1); _shots.add(new ShotData(projectile, target)); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/kinghill/KingHill.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/kinghill/KingHill.java index e802b4ce0..1de0eccdd 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/kinghill/KingHill.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/worldevent/event/kinghill/KingHill.java @@ -6,13 +6,16 @@ import java.util.HashMap; import java.util.List; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.entity.Player; import mineplex.core.common.data.BlockData; import mineplex.core.common.schematic.SchematicRunnable; +import mineplex.core.common.util.C; import mineplex.core.common.util.Callback; import mineplex.core.common.util.UtilServer; +import mineplex.core.common.util.UtilTime; import mineplex.game.clans.clans.ClanInfo; import mineplex.game.clans.clans.ClansManager; import mineplex.game.clans.clans.worldevent.WorldEventManager; @@ -43,25 +46,26 @@ public class KingHill extends WorldEvent private HashMap _scoreMap; private HillData _hill; private List _restoreData; + private long _nextLootDrop; + private int _lootDropCount; - public KingHill(WorldEventManager eventManager, DamageManager damageManager, Location centerLocation) + public KingHill(WorldEventManager eventManager, Location centerLocation) { - super(eventManager, damageManager, "King of the Hill", centerLocation); + super(eventManager, "King of the Hill", centerLocation); _clansManager = eventManager.getClans(); _scoreMap = new HashMap(); _hill = LOADED_HILLS.get(0); + _nextLootDrop = System.currentTimeMillis() + getRandomRange(300000, 600000); } @Override protected void customStart() { - Bukkit.broadcastMessage("attempting to spawn in hill... please wait!"); SchematicRunnable runnable = new SchematicRunnable(_clansManager.getPlugin(), _hill.getSchematic(), getCenterLocation().getBlock(), new Callback>() { @Override public void run(List data) { - System.out.println("CALLBACK"); setState(EventState.LIVE); _restoreData = data; } @@ -85,6 +89,16 @@ public class KingHill extends WorldEvent if (getTicks() % 1 == 0) tickHill(); + + if (System.currentTimeMillis() > _nextLootDrop) + { + // Drop Loot! + _lootDropCount++; + _nextLootDrop = System.currentTimeMillis() + getRandomRange(300000, 600000); + + if (_lootDropCount >= 4) + setState(EventState.END); + } } private void tickHill() @@ -102,6 +116,8 @@ public class KingHill extends WorldEvent clanCount++; lastClan = playerClan; } + + updateLastActive(); } } @@ -122,6 +138,14 @@ public class KingHill extends WorldEvent } } + @Override + public ArrayList getScoreboardLines() + { + ArrayList list = new ArrayList(1); + list.add(" Loot in " + UtilTime.convertString(_nextLootDrop - System.currentTimeMillis(), 1, UtilTime.TimeUnit.FIT)); + return list; + } + private static class CaptureData { public int TicksOnHill;