diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/GameType.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/GameType.java index 7cefa2340..80a5490f1 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/GameType.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/GameType.java @@ -242,7 +242,7 @@ public enum GameType { Pair.create(MinecraftVersion.Version1_8, "http://file.mineplex.com/ResStrikeGames18.zip"), Pair.create(MinecraftVersion.Version1_9, "http://file.mineplex.com/ResStrikeGames19.zip") - }, false), + }, true), Event(EventGame.class, GameDisplay.Event, new GameType[]{ GameType.BaconBrawl, GameType.Barbarians, GameType.Bridge, GameType.Build, GameType.Build, diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyale.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyale.java index c4695a3b7..be32bb923 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyale.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyale.java @@ -16,6 +16,8 @@ import mineplex.core.itemstack.ItemBuilder; import mineplex.core.recharge.Recharge; import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; +import mineplex.minecraft.game.core.combat.CombatComponent; +import mineplex.minecraft.game.core.combat.event.CombatDeathEvent; import mineplex.minecraft.game.core.damage.CustomDamageEvent; import nautilus.game.arcade.ArcadeManager; import nautilus.game.arcade.GameType; @@ -29,6 +31,7 @@ import nautilus.game.arcade.game.modules.chest.ChestLootPool; import nautilus.game.arcade.game.modules.compass.CompassModule; import nautilus.game.arcade.kit.Kit; import org.bukkit.Bukkit; +import org.bukkit.Color; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; @@ -42,9 +45,22 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.EnchantingInventory; +import org.bukkit.inventory.FurnaceInventory; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -55,27 +71,55 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class BattleRoyale extends Game { - private static final long PREPARE_TIME = TimeUnit.SECONDS.toMillis(20); + private static final long PREPARE_TIME = TimeUnit.SECONDS.toMillis(30); + private static final int MIN_CORD = 100; private static final int MAX_CORD = 1000; private static final int SPAWN_Y = 130; private static final int WORLD_SIZE_BUFFER = 300; private static final int MIN_DISTANCE_APART_FOR_SPAWNS_SQUARED = 100; + private static final long MIN_DRAGON_TIME = TimeUnit.SECONDS.toMillis(5); private static final long MAX_DRAGON_TIME = TimeUnit.SECONDS.toMillis(60); private static final long BORDER_TIME = TimeUnit.MINUTES.toSeconds(20); + protected static final long SUPPLY_DROP_TIME = TimeUnit.MINUTES.toMillis(5); + private static final int MAX_DROPS_PER_GAME = 3; - private final Map _playerData = new HashMap<>(70); + private static final ItemStack SMALL_BACKPACK = new ItemBuilder(Material.CHEST) + .setTitle(C.cGreen + "Small Backpack") + .addLore("Clicking this will unlock a new row in your inventory!") + .build(); + private static final ItemStack LARGE_BACKPACK = new ItemBuilder(Material.ENDER_CHEST) + .setTitle(C.cGreen + "Large Backpack") + .addLore("Clicking this will unlock two new rows in your inventory!") + .build(); + + private static final ItemStack SMALL_HEALTH_POT = new ItemBuilder(Material.POTION) + .setTitle(C.cGreen + "Small Health Pot") + .addPotionEffect(new PotionEffect(PotionEffectType.HEAL, 1, 0)) + .build(); + + private static final ItemStack LARGE_HEALTH_POT = new ItemBuilder(Material.POTION) + .setTitle(C.cGreen + "Large Health Pot") + .addPotionEffect(new PotionEffect(PotionEffectType.HEAL, 1, 1)) + .build(); + + protected final Map _playerData = new HashMap<>(70); protected GunModule _gunModule; protected WorldBorder _border; private boolean _colouredMessage; + protected BattleRoyaleSupplyDrop _supplyDrop; + protected long _lastSupplyDrop; + private int _totalDrops; + public BattleRoyale(ArcadeManager manager, GameType gameType, Kit[] kits, String[] gameDesc) { super(manager, gameType, kits, gameDesc); // TODO REMOVE THIS TeleportsDisqualify = false; + TickPerTeleport = 20; PrepareTime = PREPARE_TIME; PrepareFreeze = false; @@ -114,14 +158,23 @@ public abstract class BattleRoyale extends Game new CompassModule() .register(this); + + manager.GetCreature().SetDisableCustomDrops(true); } @Override public void ParseData() { + List chestSpawns = new ArrayList<>(500); + chestSpawns.addAll(WorldData.GetDataLocs("ORANGE")); + chestSpawns.addAll(WorldData.GetDataLocs("GREEN")); + chestSpawns.addAll(WorldData.GetDataLocs("YELLOW")); + chestSpawns.addAll(WorldData.GetDataLocs("BLUE")); + new ChestLootModule() + .destoryAfterOpened(20) .spawnNearbyDataPoints() - .registerChestType("Standard", WorldData.GetDataLocs("ORANGE"), + .registerChestType("Standard", chestSpawns, // Guns new ChestLootPool() @@ -130,22 +183,76 @@ public abstract class BattleRoyale extends Game .addItem(buildFromGun(GunStats.DEAGLE)) .addItem(buildFromGun(GunStats.P250)) .addItem(buildFromGun(GunStats.P2000)) + .addItem(buildFromGun(GunStats.P90), 0.5) + .addItem(buildFromGun(GunStats.PPBIZON), 0.5) + .addItem(buildFromGun(GunStats.GALIL), 0.2) + .addItem(buildFromGun(GunStats.FAMAS), 0.2) .addItem(buildFromGun(GunStats.AK47), 0.2) .addItem(buildFromGun(GunStats.M4A4), 0.2) + .addItem(buildFromGun(GunStats.SG553), 0.2) + .addItem(buildFromGun(GunStats.AUG), 0.2) + .addItem(buildFromGun(GunStats.SSG08), 0.2) + .addItem(buildFromGun(GunStats.NOVA), 0.2) + .addItem(buildFromGun(GunStats.XM1014), 0.2) .setProbability(0.4) , + // Grenades + new ChestLootPool() + .addItem(buildGrenade(Material.CARROT_ITEM, "Flash Bang")) + .addItem(buildGrenade(Material.APPLE, "High Explosive")) + .addItem(buildGrenade(Material.POTATO_ITEM, "Smoke")) + .addItem(buildGrenade(Material.PORK, "Incendiary"), 0.5) + .addItem(buildGrenade(Material.GRILLED_PORK, "Molotov"), 0.5) + .setProbability(0.2) + , + // Weapons new ChestLootPool() - .addItem(new ItemStack(Material.WOOD_SWORD)) - .addItem(new ItemStack(Material.STONE_SWORD), 0.3) - .setProbability(0.5) + .addItem(new ItemStack(Material.BOW)) + .setProbability(0.05) + , + + // Ammo + new ChestLootPool() + .addItem(new ItemStack(Material.ARROW), 1, 8) + .setProbability(0.2) + .setAmountsPerChest(1, 3) + , + + // Medical + new ChestLootPool() + .addItem(SMALL_HEALTH_POT) + .addItem(LARGE_HEALTH_POT) + .setProbability(0.2) + .setAmountsPerChest(1, 2) , // Armour new ChestLootPool() - .addItem(new ItemStack(Material.LEATHER_HELMET)) - .addItem(new ItemStack(Material.LEATHER_CHESTPLATE)) + .addItem(new ItemBuilder(Material.LEATHER_HELMET) + .setTitle(C.cRed + "Red Baseball Cap") + .setColor(Color.RED) + .build()) + .addItem(new ItemBuilder(Material.LEATHER_HELMET) + .setTitle(C.cAqua + "Blue Baseball Cap") + .setColor(Color.BLUE) + .build()) + .addItem(new ItemBuilder(Material.CHAINMAIL_HELMET) + .setTitle(C.cDGreen + "Tactical Helmet") + .build()) + .addItem(new ItemBuilder(Material.IRON_HELMET) + .setTitle(C.cDGreen + "Motorcycle Helmet") + .build()) + .addItem(new ItemBuilder(Material.LEATHER_CHESTPLATE) + .setTitle(C.cDGreen + "Wooden Body Armour") + .build()) + .addItem(new ItemBuilder(Material.CHAINMAIL_CHESTPLATE) + .setTitle(C.cDGreen + "Plated Body Armour") + .build()) + .addItem(new ItemBuilder(Material.IRON_CHESTPLATE) + .setTitle(C.cDGreen + "Laminated Tactical Body Armour") + .build()) .addItem(new ItemStack(Material.LEATHER_LEGGINGS)) .addItem(new ItemStack(Material.LEATHER_BOOTS)) , @@ -167,9 +274,59 @@ public abstract class BattleRoyale extends Game // Misc new ChestLootPool() + .addItem(new ItemStack(Material.STRING), 1, 2, 0.5) .addItem(new ItemStack(Material.STICK), 1, 2) + .addItem(new ItemStack(Material.FLINT), 1, 2) + .addItem(new ItemStack(Material.FEATHER), 1, 2) + .addItem(SMALL_BACKPACK, 0.5) + .addItem(LARGE_BACKPACK, 0.2) .setProbability(0.2) + .setAmountsPerChest(1, 2) ) + .registerChestType("Supply Drop", new ArrayList<>(0), + + // Guns + new ChestLootPool() + .addItem(buildFromGun(GunStats.AUG)) + .addItem(buildFromGun(GunStats.AK47)) + .addItem(buildFromGun(GunStats.M4A4)) + .addItem(buildFromGun(GunStats.XM1014)) + .addItem(buildFromGun(GunStats.AWP)) + .setAmountsPerChest(1, 2) + , + // Backpack + new ChestLootPool() + .addItem(SMALL_BACKPACK) + .addItem(LARGE_BACKPACK) + , + + // Armour + new ChestLootPool() + .addItem(new ItemBuilder(Material.IRON_HELMET) + .setTitle(C.cDGreen + "Motorcycle Helmet") + .build()) + .addItem(new ItemBuilder(Material.IRON_CHESTPLATE) + .setTitle(C.cDGreen + "Laminated Tactical Body Armour") + .build()) + , + + // Grenades + new ChestLootPool() + .addItem(buildGrenade(Material.CARROT_ITEM, "Flash Bang")) + .addItem(buildGrenade(Material.APPLE, "High Explosive")) + .addItem(buildGrenade(Material.POTATO_ITEM, "Smoke")) + .addItem(buildGrenade(Material.PORK, "Incendiary"), 0.5) + .addItem(buildGrenade(Material.GRILLED_PORK, "Molotov"), 0.5) + .setAmountsPerChest(1, 2) + , + + // Medical + new ChestLootPool() + .addItem(SMALL_HEALTH_POT) + .addItem(LARGE_HEALTH_POT) + .setAmountsPerChest(1, 2) + + ) .register(this); WorldData.MinX = -MAX_CORD; @@ -187,6 +344,19 @@ public abstract class BattleRoyale extends Game .build(); } + private ItemStack buildGrenade(Material material, String name) + { + return new ItemBuilder(material) + .setTitle(C.cDGreenB + name) + .build(); + } + + @Override + @EventHandler + public void ScoreboardUpdate(UpdateEvent event) + { + } + @EventHandler public void prepare(GameStateChangeEvent event) { @@ -252,7 +422,7 @@ public abstract class BattleRoyale extends Game index.getAndIncrement(); } - }, 100, 2); + }, 200, 2); } private Location getPlayerSpawn(int initialXZ) @@ -296,7 +466,7 @@ public abstract class BattleRoyale extends Game while (attempts++ < 20) { - Location location = UtilAlg.getRandomLocation(GetSpectatorLocation(), 200, 0, 200); + Location location = UtilAlg.getRandomLocation(GetSpectatorLocation(), WORLD_SIZE_BUFFER, 0, WORLD_SIZE_BUFFER); Block block = location.getBlock(); while (!UtilBlock.solid(block)) @@ -309,10 +479,7 @@ public abstract class BattleRoyale extends Game continue; } - if (UtilBlock.airFoliage(UtilBlock.getHighest(location.getWorld(), location.getBlock()))) - { - return location; - } + return location; } return SpectatorSpawn; @@ -326,12 +493,23 @@ public abstract class BattleRoyale extends Game return; } + _lastSupplyDrop = System.currentTimeMillis(); + CreatureAllowOverride = true; + ItemStack locked = new ItemBuilder(Material.STAINED_GLASS_PANE, (byte) 15) + .setTitle(C.cGray + "Locked") + .build(); + _playerData.forEach((player, battleRoyalePlayer) -> { battleRoyalePlayer.removeCage(); battleRoyalePlayer.spawnDragon(); + + for (int i = 18; i < player.getInventory().getSize(); i++) + { + player.getInventory().setItem(i, locked); + } }); CreatureAllowOverride = false; @@ -363,7 +541,7 @@ public abstract class BattleRoyale extends Game EnderDragon dragon = royalePlayer.getDragon(); Chicken chicken = royalePlayer.getChicken(); - if (dragon == null || chicken == null) + if (dragon == null || !dragon.isValid() || chicken == null || !chicken.isValid()) { continue; } @@ -371,23 +549,21 @@ public abstract class BattleRoyale extends Game UtilTextBottom.display((_colouredMessage ? C.cGreenB : C.cWhiteB) + "PRESS YOUR SNEAK KEY TO DISMOUNT YOUR DRAGON", player); if (dragon.getPassenger() == null || chicken.getPassenger() == null) { - if (!UtilTime.elapsed(GetStateTime(), 4000)) + if (!UtilTime.elapsed(GetStateTime(), MIN_DRAGON_TIME)) { - player.sendMessage(F.main("Game", "Did you accidentally press sneak? Don't worry I'll put you back on your dragon.")); + player.sendMessage(F.main("Game", "Did you accidentally press sneak? It's too soon to jump! Don't worry I'll put you back on your dragon.")); dragon.setPassenger(chicken); chicken.setPassenger(player); continue; } dismountDragon(player, royalePlayer); - iterator.remove(); } } if (!Damage && UtilTime.elapsed(GetStateTime(), MAX_DRAGON_TIME)) { _playerData.forEach(this::dismountDragon); - _playerData.clear(); Announce(C.cRedB + "Grace Period Over!", false); @@ -398,12 +574,17 @@ public abstract class BattleRoyale extends Game Damage = true; HungerSet = -1; - _border.setSize(100, BORDER_TIME); + _border.setSize(MIN_CORD, BORDER_TIME); } } private void dismountDragon(Player player, BattleRoyalePlayer royalePlayer) { + if (!royalePlayer.getDragon().isValid()) + { + return; + } + // Recharge this so that players won't take fall damage for the next 10 seconds Recharge.Instance.useForce(player, "Fall Damage", TimeUnit.SECONDS.toMillis(10)); player.playSound(player.getLocation(), Sound.BLAZE_DEATH, 1, 0.6F); @@ -434,9 +615,241 @@ public abstract class BattleRoyale extends Game } } + @Override + public void disable() + { + super.disable(); + + if (_supplyDrop != null) + { + _supplyDrop.cleanup(); + } + } + + @EventHandler + public void updateSupplyDrop(UpdateEvent event) + { + if (event.getType() != UpdateType.FAST || !IsLive()) + { + return; + } + + if (_totalDrops < MAX_DROPS_PER_GAME && UtilTime.elapsed(_lastSupplyDrop, SUPPLY_DROP_TIME)) + { + _lastSupplyDrop = System.currentTimeMillis(); + + List locations = WorldData.GetDataLocs("RED"); + Location location = null; + int attempts = 0; + + while (location == null || attempts++ < 20) + { + location = UtilAlg.Random(locations); + + if (UtilWorld.inWorldBorder(location)) + { + break; + } + } + + _supplyDrop = new BattleRoyaleSupplyDrop(this, location); + _totalDrops++; + Announce(C.cGoldB + "A New Supply Drop Will Spawn At " + C.cYellow + UtilWorld.locToStrClean(_supplyDrop.getDropLocation()) + C.cGold + "!"); + } + else if (_supplyDrop != null && _supplyDrop.isOpened()) + { + _supplyDrop = null; + } + } + + @EventHandler + public void playerDeath(CombatDeathEvent event) + { + Player player = (Player) event.GetEvent().getEntity(); + CombatComponent killer = event.GetLog().GetKiller(); + + if (killer.IsPlayer()) + { + Player killerPlayer = UtilPlayer.searchExact(killer.getUniqueIdOfEntity()); + + if (killerPlayer != null) + { + BattleRoyalePlayer royalePlayer = _playerData.get(killerPlayer); + + if (royalePlayer != null) + { + royalePlayer.incrementKills(); + UtilTextBottom.display(C.cRedB + royalePlayer.getKills() + " Kill" + (royalePlayer.getKills() == 1 ? "" : "s")); + } + } + } + + List attackers = event.GetLog().GetAttackers(); + + for (CombatComponent attacker : attackers) + { + if (!attacker.IsPlayer() || killer.equals(attacker)) + { + continue; + } + + Player attackerPlayer = UtilPlayer.searchExact(attacker.getUniqueIdOfEntity()); + + if (attackerPlayer == null) + { + continue; + } + + BattleRoyalePlayer royalePlayer = _playerData.get(attackerPlayer); + + if (royalePlayer != null) + { + attackerPlayer.sendMessage(F.main("Game", "You assisted in killing " + F.name(player.getName()) + ".")); + royalePlayer.incrementAssists(); + } + } + } + + @EventHandler + public void preventLockedInventoryClick(InventoryClickEvent event) + { + Player player = (Player) event.getWhoClicked(); + ItemStack itemStack = event.getCurrentItem(); + + if (event.getClickedInventory() == null || itemStack == null) + { + return; + } + + if (itemStack.getType() == Material.STAINED_GLASS_PANE) + { + event.setCancelled(true); + player.playSound(player.getLocation(), Sound.ITEM_BREAK, 1, 0.6F); + } + } + + @EventHandler + public void clickBackpack(InventoryClickEvent event) + { + Player player = (Player) event.getWhoClicked(); + ItemStack itemStack = event.getCurrentItem(); + + if (event.getClickedInventory() == null || itemStack == null) + { + return; + } + + int slots = 0; + + if (itemStack.isSimilar(SMALL_BACKPACK)) + { + slots = 9; + } + else if (itemStack.isSimilar(LARGE_BACKPACK)) + { + slots = 18; + } + + if (slots == 0) + { + return; + } + + ItemStack[] items = player.getInventory().getContents(); + int removed = 0; + + for (int i = 0; i < items.length && removed < slots; i++) + { + ItemStack inventoryItem = items[i]; + + if (inventoryItem != null && inventoryItem.getType() == Material.STAINED_GLASS_PANE) + { + player.getInventory().setItem(i, null); + removed++; + } + } + + event.setCurrentItem(null); + player.playSound(player.getLocation(), Sound.LEVEL_UP, 1, 1); + player.sendMessage(F.main("Game", "You unlocked an additional " + F.elem(removed) + " slots in your inventory.")); + } + + @EventHandler + public void noHungerRegeneration(EntityRegainHealthEvent event) + { + if (event.getRegainReason() == RegainReason.SATIATED) + { + event.setCancelled(true); + } + } + + @EventHandler + public void inventoryOpen(InventoryOpenEvent event) + { + Inventory inventory = event.getInventory(); + + if (inventory instanceof EnchantingInventory || inventory instanceof AnvilInventory || inventory instanceof FurnaceInventory) + { + event.setCancelled(true); + } + } + @EventHandler public void damageToLevel(CustomDamageEvent event) { event.SetDamageToLevel(false); } + + @EventHandler + public void playerQuit(PlayerQuitEvent event) + { + _playerData.remove(event.getPlayer()); + } + + @EventHandler + public void removeEmptyPotions(UpdateEvent event) + { + if (event.getType() != UpdateType.FAST) + { + return; + } + + for (Player player : GetPlayers(true)) + { + player.getInventory().remove(Material.GLASS_BOTTLE); + } + } + + @EventHandler + public void playerDeath(PlayerDeathEvent event) + { + if (!IsLive()) + { + return; + } + + event.getDrops().removeIf(itemStack -> itemStack.getType() == Material.STAINED_GLASS_PANE); + + Player player = event.getEntity(); + awardTimeGems(player); + } + + protected void awardTimeGems(Player player) + { + long timeAlive = Math.min(System.currentTimeMillis() - GetStateTime(), TimeUnit.MINUTES.toMillis(30)); + + // i.e 1 gem per 10 seconds alive + AddGems(player, timeAlive / TimeUnit.SECONDS.toMillis(10), "Surviving " + UtilTime.MakeStr(timeAlive), false, false); + } + + @Override + public double GetKillsGems(Player killer, Player killed, boolean assist) + { + if (assist) + { + return 50; + } + + return _border.getSize() == MIN_CORD ? 200 : 100; + } } diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyalePlayer.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyalePlayer.java index fad6dedf9..c01c52658 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyalePlayer.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyalePlayer.java @@ -5,7 +5,6 @@ import mineplex.core.common.util.MapUtil; import mineplex.core.common.util.UtilColor; import mineplex.core.common.util.UtilEnt; import nautilus.game.arcade.ArcadeManager; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_8_R3.entity.CraftEnderDragon; @@ -27,8 +26,10 @@ class BattleRoyalePlayer private final Set _cageBlocks; private EnderDragon _dragon; private Chicken _chicken; + private int _kills; + private int _assists; - public BattleRoyalePlayer(ArcadeManager manager, Player player, Location location, Location goal) + BattleRoyalePlayer(ArcadeManager manager, Player player, Location location, Location goal) { _player = player; _location = location; @@ -129,4 +130,24 @@ class BattleRoyalePlayer { return _chicken; } + + public void incrementKills() + { + _kills++; + } + + public int getKills() + { + return _kills; + } + + public void incrementAssists() + { + _assists++; + } + + public int getAssists() + { + return _assists; + } } diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSolo.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSolo.java index b41d01379..678cae858 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSolo.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSolo.java @@ -1,6 +1,9 @@ package nautilus.game.arcade.game.games.battleroyale; import mineplex.core.common.util.C; +import mineplex.core.common.util.UtilMath; +import mineplex.core.common.util.UtilTime; +import mineplex.core.common.util.UtilWorld; import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; import nautilus.game.arcade.ArcadeManager; @@ -8,11 +11,14 @@ import nautilus.game.arcade.GameType; import nautilus.game.arcade.events.GameStateChangeEvent; import nautilus.game.arcade.game.GameTeam; import nautilus.game.arcade.game.games.moba.kit.KitPlayer; +import nautilus.game.arcade.game.modules.CustomScoreboardModule; import nautilus.game.arcade.kit.Kit; +import nautilus.game.arcade.scoreboard.GameScoreboard; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import java.util.ArrayList; import java.util.Collections; @@ -27,55 +33,157 @@ public class BattleRoyaleSolo extends BattleRoyale private GameTeam _players; + // Scoreboard data + private final String _playersAliveTitle = C.cYellowB + "Players"; + private String _playersAlive; + private final String _statsTitle = C.cYellowB + "Stats"; + private String _supplyDropTitle = C.cGoldB + "Supply Drop"; + private String _supplyDropLocation; + private String _supplyDropState; + private final String _borderTitle = C.cRedB + "World Border"; + private String _borderCenter; + private String _borderSize; + private boolean _showBorderCenter; + public BattleRoyaleSolo(ArcadeManager manager) { - super(manager, GameType.BattleRoyale, new Kit[] {new KitPlayer(manager)}, DESCRIPTION); + super(manager, GameType.BattleRoyale, new Kit[]{new KitPlayer(manager)}, DESCRIPTION); + + new CustomScoreboardModule() + .setSidebar((player, scoreboard) -> + { + switch (GetState()) + { + case Prepare: + writePrepare(player, scoreboard); + break; + case Live: + writeLive(player, scoreboard); + break; + } + }) + .setPrefix((perspective, subject) -> + { + if (!IsAlive(subject)) + { + return C.cGray; + } + + return perspective.equals(subject) ? C.cGreen : C.cRed; + }) + .register(this); } @EventHandler public void customTeamGeneration(GameStateChangeEvent event) { if (event.GetState() != GameState.Recruit) + { return; + } _players = GetTeamList().get(0); _players.SetColor(ChatColor.YELLOW); _players.SetName("Players"); } - @Override - @EventHandler - public void ScoreboardUpdate(UpdateEvent event) + // LOW so that this is run before the scoreboards are updated + @EventHandler(priority = EventPriority.LOW) + public void scoreboardDataUpdate(UpdateEvent event) { - if (event.getType() != UpdateType.FAST || !InProgress()) + if (event.getType() != UpdateType.FAST || _border == null) { return; } - Scoreboard.writeNewLine(); + // Due to many players being in this game and the fact that the scoreboard module scales O(n^2) + // we can optimise this by storing global variables that all players would see on their scoreboard + // regardless of their state. + _playersAlive = GetPlayers(true).size() + " Alive"; - Scoreboard.write(C.cYellow + C.Bold + "Players"); - if (_players.GetPlayers(true).size() > 10) + if (_supplyDrop != null) { - Scoreboard.write(String.valueOf( _players.GetPlayers(true).size())); + _supplyDropLocation = UtilWorld.locToStrClean(_supplyDrop.getDropLocation()); + _supplyDropState = _supplyDrop.getScoreboardString(); } else { - for (Player player : _players.GetPlayers(true)) - { - Scoreboard.write(player.getName()); - } + _supplyDropLocation = ""; + _supplyDropState = UtilTime.MakeStr(_lastSupplyDrop + BattleRoyale.SUPPLY_DROP_TIME - System.currentTimeMillis()); } - Scoreboard.writeNewLine(); - int size = (int) _border.getSize(); Location center = _border.getCenter(); - Scoreboard.write(C.cRedB + "World Border"); - Scoreboard.write("(" + center.getBlockX() + ", " + center.getBlockZ() + ")"); - Scoreboard.write(size + " Blocks Wide"); - Scoreboard.draw(); + if (size < 1000 && !_showBorderCenter) + { + _showBorderCenter = true; + Announce(C.cRedB + "The Center Of The Border Is Now Visible!"); + } + + if (_showBorderCenter) + { + _borderCenter = "(" + center.getBlockX() + ", " + center.getBlockZ() + ")"; + } + else + { + _borderCenter = "Center Unknown"; + } + + _borderSize = size + " Blocks Wide"; + } + + public void writePrepare(Player player, GameScoreboard scoreboard) + { + scoreboard.writeNewLine(); + + scoreboard.write(_playersAliveTitle); + scoreboard.write(_playersAlive); + + scoreboard.writeNewLine(); + } + + public void writeLive(Player player, GameScoreboard scoreboard) + { + BattleRoyalePlayer royalePlayer = _playerData.get(player); + + scoreboard.writeNewLine(); + + scoreboard.write(_playersAliveTitle); + scoreboard.write(_playersAlive); + + scoreboard.writeNewLine(); + + if (royalePlayer != null) + { + scoreboard.write(_statsTitle); + scoreboard.write("Kills: " + C.cGreen + royalePlayer.getKills()); + scoreboard.write("Assists: " + C.cGreen + royalePlayer.getAssists()); + + scoreboard.writeNewLine(); + } + + scoreboard.write(_supplyDropTitle); + if (_supplyDrop != null) + { + scoreboard.write(_supplyDropLocation); + } + if (_supplyDropState != null) + { + scoreboard.write(_supplyDropState); + } + else if (_supplyDrop != null && IsAlive(player)) + { + int dist = (int) UtilMath.offset2d(_supplyDrop.getDropLocation(), player.getLocation()); + + scoreboard.write(dist + " Blocks Away"); + } + + scoreboard.writeNewLine(); + + scoreboard.write(_borderTitle); + scoreboard.write(_borderCenter); + scoreboard.write(_borderSize); } @Override @@ -86,8 +194,12 @@ public class BattleRoyaleSolo extends BattleRoyale return; } - if (GetPlayers(true).size() <= 1) + List alive = GetPlayers(true); + + if (alive.size() <= 1) { + alive.forEach(this::awardTimeGems); + List places = _players.GetPlacements(true); AnnounceEnd(places); @@ -105,14 +217,6 @@ public class BattleRoyaleSolo extends BattleRoyale AddGems(places.get(2), 10, "3rd Place", false, false); } - for (Player player : GetPlayers(false)) - { - if (player.isOnline()) - { - AddGems(player, 10, "Participation", false, false); - } - } - _border.setSize(10000); SetState(GameState.End); } @@ -126,12 +230,18 @@ public class BattleRoyaleSolo extends BattleRoyale List places = _players.GetPlacements(true); if (places.isEmpty() || !places.get(0).isOnline()) + { return new ArrayList<>(0); + } else + { return Collections.singletonList(places.get(0)); + } } else + { return null; + } } @Override diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSupplyDrop.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSupplyDrop.java new file mode 100644 index 000000000..966ea593c --- /dev/null +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/battleroyale/BattleRoyaleSupplyDrop.java @@ -0,0 +1,185 @@ +package nautilus.game.arcade.game.games.battleroyale; + +import mineplex.core.common.Pair; +import mineplex.core.common.util.MapUtil; +import mineplex.core.common.util.UtilAlg; +import mineplex.core.common.util.UtilBlock; +import mineplex.core.common.util.UtilEnt; +import mineplex.core.common.util.UtilFirework; +import mineplex.core.common.util.UtilMath; +import mineplex.core.common.util.UtilServer; +import mineplex.core.common.util.UtilTime; +import mineplex.core.updater.UpdateType; +import mineplex.core.updater.event.UpdateEvent; +import nautilus.game.arcade.game.modules.chest.ChestLootModule; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Chicken; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +class BattleRoyaleSupplyDrop implements Listener +{ + + private static final int DRAGON_Y = 120; + private static long DROP_WAIT = TimeUnit.MINUTES.toMillis(2); + private static final ItemStack CHEST = new ItemStack(Material.CHEST); + private static final FireworkEffect FIREWORK_EFFECT = FireworkEffect.builder() + .with(Type.BALL_LARGE) + .withColor(Color.YELLOW) + .withFlicker() + .build(); + + private final BattleRoyale _host; + private final long _start; + private final Set _beaconBlocks; + + private Location _dropLocation; + + private ArmorStand _chest; + private Chicken _seat; + private final List _chute; + + private boolean _dropped; + private boolean _opened; + private boolean _landed; + private long _openedAt; + + BattleRoyaleSupplyDrop(BattleRoyale host, Location dropLocation) + { + _host = host; + _dropLocation = dropLocation; + _start = System.currentTimeMillis(); + _beaconBlocks = new HashSet<>(); + _chute = new ArrayList<>(); + + // Construct a beacon + for (Pair> pair : UtilBlock.getBeaconBlocks(_dropLocation, (byte) 0)) + { + // Look it's like a maze + _beaconBlocks.add(pair.getLeft().getBlock()); + host.getArcadeManager().GetBlockRestore().add(pair.getLeft().getBlock(), pair.getRight().getLeft().getId(), pair.getRight().getRight(), Long.MAX_VALUE); + } + + _dropLocation.setY(DRAGON_Y); + + UtilServer.RegisterEvents(this); + } + + @EventHandler + public void updateDrop(UpdateEvent event) + { + if (event.getType() != UpdateType.FAST) + { + return; + } + + if (UtilTime.elapsed(_start, DROP_WAIT) && !_dropped) + { + _dropped = true; + _host.CreatureAllowOverride = true; + + UtilFirework.playFirework(_dropLocation, FIREWORK_EFFECT); + _chest = _dropLocation.getWorld().spawn(_dropLocation, ArmorStand.class); + _chest.setGravity(false); + _chest.setVisible(false); + _chest.setHelmet(CHEST); + + _seat = _dropLocation.getWorld().spawn(_dropLocation, Chicken.class); + UtilEnt.vegetate(_seat); + UtilEnt.ghost(_seat, true, true); + UtilEnt.silence(_seat, true); + _seat.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false)); + _seat.setPassenger(_chest); + + for (int i = 0; i < 6; i++) + { + Chicken chicken = _dropLocation.getWorld().spawn(UtilAlg.getRandomLocation(_dropLocation, 2, 0.5, 2).add(0, 5, 0), Chicken.class); + UtilEnt.vegetate(chicken); + UtilEnt.ghost(chicken, true, false); + chicken.setLeashHolder(_seat); + _chute.add(chicken); + } + + _host.CreatureAllowOverride = false; + } + else if (_dropped && !_landed && UtilEnt.isGrounded(_seat)) + { + _landed = true; + + Location chest = _seat.getLocation(); + UtilFirework.playFirework(chest, FIREWORK_EFFECT); + MapUtil.QuickChangeBlockAt(chest, Material.CHEST); + _dropLocation = chest; + + ChestLootModule lootModule = _host.getModule(ChestLootModule.class); + lootModule.addChestLocation("Supply Drop", chest); + + _beaconBlocks.forEach(block -> _host.getArcadeManager().GetBlockRestore().restore(block)); + + _chest.remove(); + _seat.remove(); + _chute.forEach(Chicken::remove); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void playerInteract(PlayerInteractEvent event) + { + if (event.isCancelled()) + { + return; + } + + Block block = event.getClickedBlock(); + + if (block == null || block.getType() != Material.CHEST) + { + return; + } + + if (UtilMath.offsetSquared(block.getLocation(), _dropLocation) < 4) + { + _opened = true; + _openedAt = System.currentTimeMillis(); + cleanup(); + } + } + + public void cleanup() + { + _chute.clear(); + UtilServer.Unregister(this); + } + + public Location getDropLocation() + { + return _dropLocation; + } + + public boolean isOpened() + { + return _opened; + } + + public String getScoreboardString() + { + return _dropped ? null : UtilTime.MakeStr(_start + DROP_WAIT - System.currentTimeMillis()); + } +} diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/bridge/Bridge.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/bridge/Bridge.java index dfc12db46..99b9e3b6e 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/bridge/Bridge.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/games/bridge/Bridge.java @@ -832,14 +832,6 @@ public class Bridge extends TeamGame implements OreObsfucation if (!_bridgesDown) { _bridgesDown = true; - -// WorldBorderModule borderModule = getModule(WorldBorderModule.class); -// -// for (Player player : GetPlayers(true)) -// { -// borderModule.setSize(player, 10000); -// } - Manager.GetExplosion().SetLiquidDamage(true); Announce(C.cRedB + "ALERT: " + C.Reset + C.Bold + "THE BRIDGES ARE SPAWNING!"); UtilTextMiddle.display(C.cRedB + "ALERT", "The BRIDGES ARE SPAWNING!"); diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/CustomScoreboardModule.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/CustomScoreboardModule.java index efb25437b..a4ba2f3dc 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/CustomScoreboardModule.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/CustomScoreboardModule.java @@ -82,6 +82,7 @@ public class CustomScoreboardModule extends Module } setupScoreboard(player); + refreshAsSubject(player); } /** diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/WorldBorderModule.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/WorldBorderModule.java deleted file mode 100644 index 6a1f13bae..000000000 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/WorldBorderModule.java +++ /dev/null @@ -1,116 +0,0 @@ -package nautilus.game.arcade.game.modules; - -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.bukkit.Location; -import org.bukkit.craftbukkit.v1_8_R3.CraftWorld; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; - -import mineplex.core.common.Pair; -import mineplex.core.common.util.UtilPlayer; -import mineplex.core.packethandler.IPacketHandler; -import mineplex.core.packethandler.PacketHandler; -import mineplex.core.packethandler.PacketInfo; -import nautilus.game.arcade.events.GameStateChangeEvent; -import nautilus.game.arcade.game.Game.GameState; -import net.minecraft.server.v1_8_R3.PacketPlayOutWorldBorder; -import net.minecraft.server.v1_8_R3.PacketPlayOutWorldBorder.EnumWorldBorderAction; -import net.minecraft.server.v1_8_R3.WorldBorder; - -public class WorldBorderModule extends Module implements IPacketHandler -{ - - private PacketHandler _packetHandler; - private Map _sizeToSet; - private Map> _centerToSet; - - @Override - protected void setup() - { - _packetHandler = getGame().getArcadeManager().getPacketHandler(); - _sizeToSet = new HashMap<>(); - _centerToSet = new HashMap<>(); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void live(GameStateChangeEvent event) - { - if (event.GetState() != GameState.Prepare) - { - return; - } - - _packetHandler.addPacketHandler(this, PacketPlayOutWorldBorder.class); - } - - @Override - public void handle(PacketInfo packetInfo) - { - UUID player = packetInfo.getPlayer().getUniqueId(); - PacketPlayOutWorldBorder packet = (PacketPlayOutWorldBorder) packetInfo.getPacket(); - - try - { - Field actionField = packet.getClass().getDeclaredField("a"); - actionField.setAccessible(true); - EnumWorldBorderAction action = (EnumWorldBorderAction) actionField.get(packet); - - if (action == EnumWorldBorderAction.SET_SIZE) - { - Field sizeField = packet.getClass().getDeclaredField("e"); - sizeField.setAccessible(true); - double newSize = _sizeToSet.get(player); - - sizeField.set(packet, newSize); - } - else if (action == EnumWorldBorderAction.SET_CENTER) - { - Field xField = packet.getClass().getDeclaredField("c"); - Field zField = packet.getClass().getDeclaredField("d"); - - xField.setAccessible(true); - zField.setAccessible(true); - - Pair pair = _centerToSet.get(player); - - xField.set(packet, pair.getLeft()); - zField.set(packet, pair.getRight()); - } - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - @Override - public void cleanup() - { - _packetHandler.removePacketHandler(this); - } - - public void setSize(Player player, double size) - { - _sizeToSet.put(player.getUniqueId(), size); - - sendPacket(player, EnumWorldBorderAction.SET_SIZE); - } - - public void setCenter(Player player, Location location) - { - _centerToSet.put(player.getUniqueId(), Pair.create(location.getX(), location.getZ())); - - sendPacket(player, EnumWorldBorderAction.SET_CENTER); - } - - private void sendPacket(Player player, EnumWorldBorderAction action) - { - WorldBorder border = ((CraftWorld) player.getWorld()).getHandle().getWorldBorder(); - UtilPlayer.sendPacket(player, new PacketPlayOutWorldBorder(border, action)); - } -} diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/chest/ChestLootModule.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/chest/ChestLootModule.java index 4ebeecc89..030a2fcbc 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/chest/ChestLootModule.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/modules/chest/ChestLootModule.java @@ -1,5 +1,6 @@ package nautilus.game.arcade.game.modules.chest; +import mineplex.core.common.util.MapUtil; import mineplex.core.common.util.UtilAlg; import mineplex.core.common.util.UtilBlock; import mineplex.core.common.util.UtilEvent; @@ -21,12 +22,12 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerInteractEvent; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -40,7 +41,6 @@ public class ChestLootModule extends Module BlockFace.EAST }; - private final List _chestTypes; private final Map> _chests; private long _destroyAfterOpened; @@ -50,7 +50,6 @@ public class ChestLootModule extends Module public ChestLootModule() { - _chestTypes = new ArrayList<>(); _chests = new HashMap<>(); } @@ -61,7 +60,7 @@ public class ChestLootModule extends Module public ChestLootModule registerChestType(String name, List chestLocations, double spawnChance, ChestLootPool... pools) { - _chestTypes.add(new ChestType(name, chestLocations, spawnChance, pools)); + _chests.put(new ChestType(name, chestLocations, spawnChance, pools), new HashSet<>()); return this; } @@ -90,6 +89,38 @@ public class ChestLootModule extends Module return this; } + public void addChestLocation(String typeName, Location location) + { + for (Entry> entry : _chests.entrySet()) + { + if (!entry.getKey().Name.equals(typeName)) + { + continue; + } + + entry.getValue().add(new ChestMetadata(location.getBlock(), entry.getKey())); + return; + } + } + + public void refill() + { + _chests.forEach((type, metadataSet) -> metadataSet.forEach(metadata -> metadata.Opened = false)); + } + + public void refill(String typeName) + { + _chests.forEach((type, metadataSet) -> + { + if (!type.Name.equals(typeName)) + { + return; + } + + metadataSet.forEach(metadata -> metadata.Opened = false); + }); + } + @EventHandler(priority = EventPriority.HIGHEST) public void populateChests(GameStateChangeEvent event) { @@ -98,14 +129,16 @@ public class ChestLootModule extends Module return; } - for (ChestType chestType : _chestTypes) + for (Entry> entry : _chests.entrySet()) { + ChestType chestType = entry.getKey(); + if (chestType.ChestSpawns == null) { continue; } - Set metadataSet = new HashSet<>(); + Set metadataSet = entry.getValue(); for (Location location : chestType.ChestSpawns) { @@ -186,8 +219,9 @@ public class ChestLootModule extends Module { Block block = metadata.Chest; Location location = block.getLocation(); - location.getWorld().playEffect(location.add(0.5, 0.5, 0.5), Effect.STEP_SOUND, block.getType(), block.getData()); - block.setType(Material.AIR); + location.getWorld().playEffect(location.add(0.5, 0.5, 0.5), Effect.STEP_SOUND, block.getType()); + ((Chest) block.getState()).getBlockInventory().clear(); + MapUtil.QuickChangeBlockAt(location, Material.AIR); return true; } @@ -258,10 +292,10 @@ public class ChestLootModule extends Module private class ChestMetadata { - Block Chest; - ChestType Type; - long OpenedAt; - boolean Opened; + Block Chest; + ChestType Type; + long OpenedAt; + boolean Opened; ChestMetadata(Block chest, ChestType type) { @@ -271,6 +305,8 @@ public class ChestLootModule extends Module void populateChest(Chest chest) { + chest.getBlockInventory().clear(); + for (ChestLootPool pool : Type.Pools) { if (pool.getProbability() == 1 || Math.random() < pool.getProbability())