diff --git a/src/de/steamwar/lobby/LobbySystem.properties b/src/de/steamwar/lobby/LobbySystem.properties index 109eb0f..7fac647 100644 --- a/src/de/steamwar/lobby/LobbySystem.properties +++ b/src/de/steamwar/lobby/LobbySystem.properties @@ -145,6 +145,10 @@ GAMES_DRAW = §6Draw! GAMES_TURN = {0}\'s turn GAMES_LEFT = §7{0} left the game +BOAT_RACE_TIME = §7Finished in §e{0} +BOAT_RACE_NEW_BEST = §aNew best time! +BOAT_RACE_TITLE = §6Checkpoint {0}/6§7: {1} + # Easter Egg Hunt DIFFICULTY_EASY = §aEasy DIFFICULTY_MEDIUM = §eMedium diff --git a/src/de/steamwar/lobby/LobbySystem_de.properties b/src/de/steamwar/lobby/LobbySystem_de.properties index 76fb6ea..1258a30 100644 --- a/src/de/steamwar/lobby/LobbySystem_de.properties +++ b/src/de/steamwar/lobby/LobbySystem_de.properties @@ -136,6 +136,9 @@ GAMES_DRAW = §6Unentschieden! GAMES_TURN = {0} ist dran GAMES_LEFT = §7{0} hat das Spiel verlassen +BOAT_RACE_TIME = §7Abgeschlossen in {0} +BOAT_RACE_NEW_BEST = §aNeue Bestzeit! + # Easter Egg Hunt DIFFICULTY_EASY = §aLeicht DIFFICULTY_MEDIUM = §eMedium diff --git a/src/de/steamwar/lobby/boatrace/BoatRace.java b/src/de/steamwar/lobby/boatrace/BoatRace.java new file mode 100644 index 0000000..fadb539 --- /dev/null +++ b/src/de/steamwar/lobby/boatrace/BoatRace.java @@ -0,0 +1,159 @@ +package de.steamwar.lobby.boatrace; + +import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityServer; +import de.steamwar.entity.RPlayer; +import de.steamwar.lobby.LobbySystem; +import de.steamwar.sql.UserConfig; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.vehicle.VehicleExitEvent; +import org.bukkit.event.vehicle.VehicleMoveEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.*; + +public class BoatRace implements EventListener, Listener { + + private static final double MIN_HEIGHT = 4.3; + + public static final REntityServer boatNpcServer; + + private static boolean oneNotStarted = false; + + static { + boatNpcServer = new REntityServer(); + new REntity(boatNpcServer, EntityType.VILLAGER, BoatRacePositions.NPC); + boatNpcServer.setCallback((player, rEntity, entityAction) -> { + Bukkit.getWorlds().get(0).getEntities().stream().filter(entity -> entity.getType() == EntityType.ENDER_CRYSTAL).forEach(Entity::remove); + if (entityAction == REntityServer.EntityAction.INTERACT && !oneNotStarted) { + oneNotStarted = true; + new BoatRace(player); + } + }); + } + + private final Player player; + private Boat boat; + private int nextCheckpoint = 0; + private long startTime; + private final BukkitTask task; + private final BossBar bossBar; + private boolean hasBacked = false; + + @EventHandler + public void onBoatMove(VehicleMoveEvent event) { + if (event.getVehicle() != boat) return; + + if(nextCheckpoint == 0 && inRegion(player, BoatRacePositions.BACKWARDS[0], BoatRacePositions.BACKWARDS[1])) { + player.eject(); + player.teleport(BoatRacePositions.END); + oneNotStarted = false; + return; + } + + if(player.getLocation().getY() < MIN_HEIGHT) { + Location[] backTo = BoatRacePositions.CHECKPOINTS[nextCheckpoint - 1]; + Location avg = new Location(backTo[0].getWorld(), (backTo[0].getX() + backTo[1].getX()) / 2, Math.max(backTo[0].getY(), backTo[1].getY()), (backTo[0].getZ() + backTo[1].getZ()) / 2, backTo[0].getYaw(), backTo[0].getPitch()); + Boat nboat = Bukkit.getWorlds().get(0).spawn(avg, Boat.class); + nboat.setBoatType(boat.getBoatType()); + hasBacked = true; + player.eject(); + boat.remove(); + boat = nboat; + boat.addPassenger(player); + player.playSound(avg, Sound.ENTITY_ENDERMAN_TELEPORT, 1, 1); + return; + } + + Location[] checkpoint = BoatRacePositions.CHECKPOINTS[nextCheckpoint]; + if(inRegion(player, checkpoint[0], checkpoint[1])) { + if(nextCheckpoint == 0) { + oneNotStarted = false; + startTime = System.currentTimeMillis(); + bossBar.addPlayer(player); + bossBar.setVisible(true); + } + nextCheckpoint++; + if (nextCheckpoint == BoatRacePositions.CHECKPOINTS.length) { + long time = System.currentTimeMillis() - startTime; + boat.remove(); + player.eject(); + player.teleport(BoatRacePositions.END); + player.playSound(player.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1, 1); + bossBar.removeAll(); + HandlerList.unregisterAll(this); + task.cancel(); + LobbySystem.getMessage().send("BOAT_RACE_TIME", player, renderTime(time)); + String conf = UserConfig.getConfig(player.getUniqueId(), "lobby@boatrace"); + long best = Long.parseLong(conf == null ? String.valueOf(Long.MAX_VALUE) : conf); + if (time < best) { + LobbySystem.getMessage().send("BOAT_RACE_NEW_BEST", player); + UserConfig.updatePlayerConfig(player.getUniqueId(), "lobby@boatrace", String.valueOf(time)); + } + } else { + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1); + } + } + } + + @EventHandler + public void onVehicleExit(VehicleExitEvent event) { + if (event.getVehicle() != boat) return; + if (event.getExited() != player) return; + if (hasBacked) return; + HandlerList.unregisterAll(this); + task.cancel(); + bossBar.removeAll(); + boat.remove(); + player.teleport(BoatRacePositions.END); + oneNotStarted = false; + } + + public BoatRace(Player player) { + this.player = player; + boat = Bukkit.getWorlds().get(0).spawn(BoatRacePositions.START, Boat.class); + boat.setBoatType(Boat.Type.values()[new Random().nextInt(Boat.Type.values().length)]); + boat.addPassenger(player); + bossBar = Bukkit.createBossBar("", BarColor.BLUE, BarStyle.SOLID); + task = Bukkit.getScheduler().runTaskTimer(LobbySystem.getPlugin(), () -> { + hasBacked = false; + if (nextCheckpoint != 0) { + bossBar.setProgress((nextCheckpoint - 1d) / (BoatRacePositions.CHECKPOINTS.length - 1d)); + bossBar.setTitle(LobbySystem.getMessage().parse("BOAT_RACE_TITLE", player, nextCheckpoint, renderTime(System.currentTimeMillis() - startTime))); + } + }, 0, 1); + Bukkit.getPluginManager().registerEvents(this, LobbySystem.getPlugin()); + } + + private boolean inRegion(Player p, Location loc1, Location loc2) { + double x1 = Math.min(loc1.getX(), loc2.getX()); + double y1 = Math.min(loc1.getY(), loc2.getY()); + double z1 = Math.min(loc1.getZ(), loc2.getZ()); + + double x2 = Math.max(loc1.getX(), loc2.getX()); + double y2 = Math.max(loc1.getY(), loc2.getY()); + double z2 = Math.max(loc1.getZ(), loc2.getZ()); + + return p.getLocation().getX() >= x1 && p.getLocation().getY() >= y1 && p.getLocation().getZ() >= z1 && p.getLocation().getX() <= x2 && p.getLocation().getY() <= y2 && p.getLocation().getZ() <= z2; + } + + private String renderTime(long time) { + return String.format( + "%d:%02d.%03d", + time / 60000, + (time / 1000) % 60, + time % 1000); + } +} diff --git a/src/de/steamwar/lobby/boatrace/BoatRacePositions.java b/src/de/steamwar/lobby/boatrace/BoatRacePositions.java new file mode 100644 index 0000000..9086591 --- /dev/null +++ b/src/de/steamwar/lobby/boatrace/BoatRacePositions.java @@ -0,0 +1,55 @@ +package de.steamwar.lobby.boatrace; + +import org.bukkit.Bukkit; +import org.bukkit.Location; + +public class BoatRacePositions { + + public static final Location LEADERBOARD = loc(2414, 6, 1542.5); + + public static final Location NPC = loc(2424.5, 5, 1544.5, -180, 0); + + public static final Location START = loc(2410.5, 6, 1537.5, -90, 0); + + public static final Location[][] CHECKPOINTS = new Location[][] { + new Location[] { + loc(2414, 4, 1528, -90, 0), + loc(2415, 8, 1542), + }, + new Location[] { + loc(2484, 4, 1456, -180, 0), + loc(2494, 8, 1459), + }, + new Location[] { + loc(2310, 4, 1475, 0, 0), + loc(2321, 8, 1477), + }, + new Location[] { + loc(2428, 4, 1448, -90, 0), + loc(2430, 8, 1458), + }, + new Location[] { + loc(2392, 4, 1492, 90, 0), + loc(2394, 8, 1502), + }, + new Location[] { + loc(2414, 4, 1528, -90, 0), + loc(2416, 8, 1542), + }, + }; + + public static final Location[] BACKWARDS = new Location[] { + loc(2407, 3,1526), + loc(2408, 9,1544) + }; + + public static final Location END = loc(2449.5, 6, 1550, -180, 0); + + private static Location loc(double x, double y, double z) { + return new Location(Bukkit.getWorlds().get(0), x, y, z); + } + + private static Location loc(double x, double y, double z, float yaw, float pitch) { + return new Location(Bukkit.getWorlds().get(0), x, y, z, yaw, pitch); + } +} diff --git a/src/de/steamwar/lobby/listener/PlayerSpawn.java b/src/de/steamwar/lobby/listener/PlayerSpawn.java index d45b797..9a38417 100644 --- a/src/de/steamwar/lobby/listener/PlayerSpawn.java +++ b/src/de/steamwar/lobby/listener/PlayerSpawn.java @@ -20,6 +20,7 @@ package de.steamwar.lobby.listener; import de.steamwar.lobby.LobbySystem; +import de.steamwar.lobby.boatrace.BoatRace; import de.steamwar.lobby.util.ItemBuilder; import de.steamwar.network.NetworkSender; import de.steamwar.network.packets.client.ImALobbyPacket; @@ -59,6 +60,7 @@ public class PlayerSpawn extends BasicListener { giveItems(player); LobbySystem.getEntityServer(false).addPlayer(player); + BoatRace.boatNpcServer.addPlayer(player); Bukkit.getScheduler().runTaskLater(LobbySystem.getPlugin(), () -> NetworkSender.send(new ImALobbyPacket(), player), 20); }