diff --git a/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/TypeBedWarsLobbyLoader.java b/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/TypeBedWarsLobbyLoader.java index d9d782666..4ee9b4eab 100644 --- a/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/TypeBedWarsLobbyLoader.java +++ b/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/TypeBedWarsLobbyLoader.java @@ -27,6 +27,7 @@ import net.swofty.type.generic.tab.TablistModule; import net.swofty.type.bedwarslobby.tab.BedWarsPlayersOnlineModule; import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.events.LobbyBlockBreak; import net.swofty.type.lobby.events.LobbyItemEvents; import net.swofty.type.lobby.events.LobbyLaunchPadEvents; import net.swofty.type.lobby.events.LobbyPlayerJoinEvents; @@ -38,6 +39,7 @@ import net.swofty.type.lobby.item.impl.ProfileItem; import net.swofty.type.lobby.launchpad.LaunchPad; import net.swofty.type.lobby.launchpad.LaunchPadHandler; +import net.swofty.type.lobby.parkour.LobbyParkourManager; import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; @@ -88,9 +90,7 @@ public void afterInitialize(MinecraftServer server) { BedWarsLeaderboardAggregator.initialize(); // Schedule hologram updates every 2 seconds - MinecraftServer.getSchedulerManager().buildTask(() -> { - PlayerHolograms.updateExternalHolograms(); - }).delay(TaskSchedule.seconds(5)) + MinecraftServer.getSchedulerManager().buildTask(PlayerHolograms::updateExternalHolograms).delay(TaskSchedule.seconds(5)) .repeat(TaskSchedule.seconds(2)) .schedule(); } @@ -112,6 +112,11 @@ public Map getHotbarItems() { ); } + @Override + public LobbyParkourManager getParkourManager() { + return null; + } + @Override public List getRequiredServices() { return List.of(ServiceType.ORCHESTRATOR); @@ -150,6 +155,7 @@ public List getTraditionalEvents() { events.add(new LobbyItemEvents()); events.add(new LobbyLaunchPadEvents()); events.add(new LobbyPlayerJoinEvents()); + events.add(new LobbyBlockBreak()); return events; } diff --git a/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/events/ActionPlayerBreak.java b/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/events/ActionPlayerBreak.java deleted file mode 100644 index d7e81bddf..000000000 --- a/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/events/ActionPlayerBreak.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.swofty.type.bedwarslobby.events; - -import lombok.SneakyThrows; -import net.minestom.server.event.player.PlayerBlockBreakEvent; -import net.swofty.type.generic.event.EventNodes; -import net.swofty.type.generic.event.HypixelEvent; -import net.swofty.type.generic.event.HypixelEventClass; - -public class ActionPlayerBreak implements HypixelEventClass { - - @SneakyThrows - @HypixelEvent(node = EventNodes.PLAYER , requireDataLoaded = false) - public void run(PlayerBlockBreakEvent event) { - event.setCancelled(true); - } -} - diff --git a/type.generic/src/main/java/net/swofty/type/generic/data/HypixelDataHandler.java b/type.generic/src/main/java/net/swofty/type/generic/data/HypixelDataHandler.java index 6a2b28013..ae949f847 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/data/HypixelDataHandler.java +++ b/type.generic/src/main/java/net/swofty/type/generic/data/HypixelDataHandler.java @@ -220,6 +220,8 @@ DatapointAchievementData.class, new DatapointAchievementData("achievement_data") QUEST_DATA("quest_data", DatapointQuestData.class, new DatapointQuestData("quest_data")), + + PARKOUR_DATA("parkour_data", DatapointParkourData.class, new DatapointParkourData("parkour_data")) ; @Getter private final String key; diff --git a/type.generic/src/main/java/net/swofty/type/generic/data/datapoints/DatapointParkourData.java b/type.generic/src/main/java/net/swofty/type/generic/data/datapoints/DatapointParkourData.java new file mode 100644 index 000000000..d0fbea66e --- /dev/null +++ b/type.generic/src/main/java/net/swofty/type/generic/data/datapoints/DatapointParkourData.java @@ -0,0 +1,21 @@ +package net.swofty.type.generic.data.datapoints; + +import lombok.Getter; +import net.swofty.commons.protocol.JacksonSerializer; +import net.swofty.type.generic.data.Datapoint; + +import java.util.HashMap; +import java.util.Map; + +public class DatapointParkourData extends Datapoint> { + private static final JacksonSerializer> serializer = new JacksonSerializer<>((Class) Map.class); + + public DatapointParkourData(String key) { + super(key, new HashMap<>(), serializer); + } + + @Getter + public enum ParkourType { + PROTOTYPE_LOBBY + } +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/LobbyTypeLoader.java b/type.lobby/src/main/java/net/swofty/type/lobby/LobbyTypeLoader.java index 94a058f44..5d1cb5683 100644 --- a/type.lobby/src/main/java/net/swofty/type/lobby/LobbyTypeLoader.java +++ b/type.lobby/src/main/java/net/swofty/type/lobby/LobbyTypeLoader.java @@ -4,6 +4,9 @@ import net.swofty.type.lobby.item.LobbyItem; import net.swofty.type.lobby.item.LobbyItemHandler; import net.swofty.type.lobby.launchpad.LaunchPad; +import net.swofty.type.lobby.parkour.LobbyParkourManager; +import net.swofty.type.lobby.parkour.Parkour; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; @@ -27,4 +30,10 @@ public interface LobbyTypeLoader extends HypixelTypeLoader { * Returns Map - only items in map are given to players. */ Map getHotbarItems(); + + default Parkour getParkour() { + return null; + } + + @Nullable LobbyParkourManager getParkourManager(); } diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/events/LobbyBlockBreak.java b/type.lobby/src/main/java/net/swofty/type/lobby/events/LobbyBlockBreak.java new file mode 100644 index 000000000..da1b913ed --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/events/LobbyBlockBreak.java @@ -0,0 +1,14 @@ +package net.swofty.type.lobby.events; + +import net.minestom.server.event.player.PlayerBlockBreakEvent; +import net.swofty.type.generic.event.EventNodes; +import net.swofty.type.generic.event.HypixelEvent; +import net.swofty.type.generic.event.HypixelEventClass; + +public class LobbyBlockBreak implements HypixelEventClass { + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) + public void onBlockBreak(PlayerBlockBreakEvent event) { + event.setCancelled(true); + } +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/events/LobbyParkourEvents.java b/type.lobby/src/main/java/net/swofty/type/lobby/events/LobbyParkourEvents.java new file mode 100644 index 000000000..ac4dd3701 --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/events/LobbyParkourEvents.java @@ -0,0 +1,109 @@ +package net.swofty.type.lobby.events; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.event.player.PlayerDisconnectEvent; +import net.minestom.server.event.player.PlayerMoveEvent; +import net.minestom.server.event.player.PlayerSpawnEvent; +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.event.EventNodes; +import net.swofty.type.generic.event.HypixelEvent; +import net.swofty.type.generic.event.HypixelEventClass; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.generic.utility.MathUtility; +import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.parkour.LobbyParkourManager; + +import java.util.HashMap; +import java.util.UUID; + +public class LobbyParkourEvents implements HypixelEventClass { + + private final HashMap lastClickedTimes = new HashMap<>(); + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) + public void onPlayerJoin(PlayerSpawnEvent event) { + HypixelPlayer player = (HypixelPlayer) event.getPlayer(); + LobbyTypeLoader loader = (LobbyTypeLoader) HypixelConst.getTypeLoader(); + + LobbyParkourManager parkourManager = loader.getParkourManager(); + if (parkourManager == null) return; + + parkourManager.updateForPlayer(HypixelConst.getInstanceContainer(), player); + } + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) + public void onPlayerDisconnect(PlayerDisconnectEvent event) { + HypixelPlayer player = (HypixelPlayer) event.getPlayer(); + lastClickedTimes.remove(player.getUuid()); + + LobbyTypeLoader loader = (LobbyTypeLoader) HypixelConst.getTypeLoader(); + LobbyParkourManager parkourManager = loader.getParkourManager(); + if (parkourManager == null) return; + if (!parkourManager.getPerPlayerStartTime().containsKey(player.getUuid())) return; + parkourManager.cancelParkour(player); + } + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = true) + public void run(PlayerMoveEvent event) { + LobbyTypeLoader loader = (LobbyTypeLoader) HypixelConst.getTypeLoader(); + final HypixelPlayer player = (HypixelPlayer) event.getPlayer(); + + if (lastClickedTimes.containsKey(player.getUuid()) && lastClickedTimes.get(player.getUuid()) >= System.currentTimeMillis() - 5000) return; + + LobbyParkourManager parkourManager = loader.getParkourManager(); + if (parkourManager == null) return; + + Pos playerPos = event.getPlayer().getPosition(); + + if (playerPos.samePoint(parkourManager.getParkour().getCheckpoints().getFirst(), 0.5)) { + player.playSound(Sound.sound( + Key.key("block.metal_pressure_plate.click_on"), Sound.Source.NEUTRAL, + .3f, 0.75f + )); + + MathUtility.delay(() -> player.playSound(Sound.sound( + Key.key("block.metal_pressure_plate.click_off"), Sound.Source.NEUTRAL, + .3f, 0.65f + )), 30); + + parkourManager.startParkour(player); + lastClickedTimes.put(player.getUuid(), System.currentTimeMillis()); + } + + for (int i = 1; i < parkourManager.getParkour().getCheckpoints().size() - 1; i++) { + if (playerPos.samePoint(parkourManager.getParkour().getCheckpoints().get(i), 0.5)) { + player.playSound(Sound.sound( + Key.key("block.metal_pressure_plate.click_on"), Sound.Source.NEUTRAL, + .3f, 0.75f + )); + + MathUtility.delay(() -> player.playSound(Sound.sound( + Key.key("block.metal_pressure_plate.click_off"), Sound.Source.NEUTRAL, + .3f, 0.65f + )), 30); + + parkourManager.checkpointPlayer(player, i); + lastClickedTimes.put(player.getUuid(), System.currentTimeMillis()); + break; + } + } + + if (playerPos.samePoint(parkourManager.getParkour().getCheckpoints().getLast(), 0.5)) { + player.playSound(Sound.sound( + Key.key("block.metal_pressure_plate.click_on"), Sound.Source.NEUTRAL, + .3f, 0.75f + )); + + MathUtility.delay(() -> player.playSound(Sound.sound( + Key.key("block.metal_pressure_plate.click_off"), Sound.Source.NEUTRAL, + .3f, 0.65f + )), 30); + + parkourManager.finishedParkour(player); + lastClickedTimes.put(player.getUuid(), System.currentTimeMillis()); + } + } + +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/item/LobbyItemHandler.java b/type.lobby/src/main/java/net/swofty/type/lobby/item/LobbyItemHandler.java index 7e323c97a..ebcb0bdbe 100644 --- a/type.lobby/src/main/java/net/swofty/type/lobby/item/LobbyItemHandler.java +++ b/type.lobby/src/main/java/net/swofty/type/lobby/item/LobbyItemHandler.java @@ -9,13 +9,22 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.component.CustomData; import net.minestom.server.tag.Tag; +import net.swofty.type.lobby.item.impl.CancelParkour; +import net.swofty.type.lobby.item.impl.LastCheckpoint; +import net.swofty.type.lobby.item.impl.ResetParkour; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class LobbyItemHandler { - private final List items = new ArrayList<>(); + private final List items = new ArrayList<>( + List.of( + new LastCheckpoint(), + new CancelParkour(), + new ResetParkour() + ) + ); public void add(LobbyItem item) { items.add(item); @@ -70,6 +79,7 @@ public void onBlockPlace(PlayerBlockPlaceEvent event) { ItemStack itemStack = event.getPlayer().getItemInMainHand(); if (isItem(item, itemStack)) { item.onBlockPlace(event); + item.onItemInteract(event); } } } diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/CancelParkour.java b/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/CancelParkour.java new file mode 100644 index 000000000..0887b0634 --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/CancelParkour.java @@ -0,0 +1,34 @@ +package net.swofty.type.lobby.item.impl; + +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerInstanceEvent; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.item.LobbyItem; +import net.swofty.type.lobby.parkour.LobbyParkourManager; + +public class CancelParkour extends LobbyItem { + + public CancelParkour() { + super("cancel_parkour"); + } + + @Override + public ItemStack getBlandItem() { + return ItemStackCreator.createNamedItemStack(Material.RED_BED, "§cCancel").build(); + } + + @Override + public void onItemInteract(PlayerInstanceEvent event) { + ((CancellableEvent) event).setCancelled(true); + if (HypixelConst.getTypeLoader() instanceof LobbyTypeLoader lobbyLoader) { + LobbyParkourManager parkourManager = lobbyLoader.getParkourManager(); + if (parkourManager == null) return; + parkourManager.cancelParkour((HypixelPlayer) event.getPlayer()); + } + } +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/LastCheckpoint.java b/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/LastCheckpoint.java new file mode 100644 index 000000000..5e852c14d --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/LastCheckpoint.java @@ -0,0 +1,36 @@ +package net.swofty.type.lobby.item.impl; + +import net.minestom.server.coordinate.CoordConversion; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerInstanceEvent; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.item.LobbyItem; +import net.swofty.type.lobby.parkour.LobbyParkourManager; + +public class LastCheckpoint extends LobbyItem { + + public LastCheckpoint() { + super("last_checkpoint"); + } + + @Override + public ItemStack getBlandItem() { + return ItemStackCreator.createNamedItemStack(Material.HEAVY_WEIGHTED_PRESSURE_PLATE, "§aTeleport to Last Checkpoint").build(); + } + + @Override + public void onItemInteract(PlayerInstanceEvent event) { + ((CancellableEvent) event).setCancelled(true); + if (HypixelConst.getTypeLoader() instanceof LobbyTypeLoader lobbyLoader) { + LobbyParkourManager parkourManager = lobbyLoader.getParkourManager(); + if (parkourManager == null) return; + event.getPlayer().teleport( + parkourManager.getParkour().getCheckpoints().get(parkourManager.getPerPlayerStartTime().get(event.getPlayer().getUuid()).lastCheckpointIndex()).asPos().add(0.5, 0, 0.5) + ); + } + } +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/ResetParkour.java b/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/ResetParkour.java new file mode 100644 index 000000000..6a764c972 --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/item/impl/ResetParkour.java @@ -0,0 +1,33 @@ +package net.swofty.type.lobby.item.impl; + +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerInstanceEvent; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.item.LobbyItem; +import net.swofty.type.lobby.parkour.LobbyParkourManager; + +public class ResetParkour extends LobbyItem { + + public ResetParkour() { + super("reset_parkour"); + } + + @Override + public ItemStack getBlandItem() { + return ItemStackCreator.createNamedItemStack(Material.OAK_DOOR, "§cReset").build(); + } + + @Override + public void onItemInteract(PlayerInstanceEvent event) { + ((CancellableEvent) event).setCancelled(true); + if (HypixelConst.getTypeLoader() instanceof LobbyTypeLoader lobbyLoader) { + LobbyParkourManager parkourManager = lobbyLoader.getParkourManager(); + if (parkourManager == null) return; + event.getPlayer().teleport(parkourManager.getParkour().getStartLocation()); + } + } +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/parkour/LobbyParkourManager.java b/type.lobby/src/main/java/net/swofty/type/lobby/parkour/LobbyParkourManager.java new file mode 100644 index 000000000..76402c9bf --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/parkour/LobbyParkourManager.java @@ -0,0 +1,229 @@ +package net.swofty.type.lobby.parkour; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.instance.Instance; +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.data.HypixelDataHandler; +import net.swofty.type.generic.data.datapoints.DatapointParkourData; +import net.swofty.type.generic.entity.hologram.PlayerHolograms; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.item.LobbyItem; +import net.swofty.type.lobby.item.impl.CancelParkour; +import net.swofty.type.lobby.item.impl.LastCheckpoint; +import net.swofty.type.lobby.item.impl.ResetParkour; +import org.tinylog.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Getter +@AllArgsConstructor +public class LobbyParkourManager { + + private final Parkour parkour; + private final Map perPlayerStartTime = new ConcurrentHashMap<>(); + private static final Map perPlayerHolo = new HashMap<>(); + + public void updateForPlayer(Instance instance, HypixelPlayer player) { + List checkpoints = parkour.getCheckpoints(); + if (checkpoints.isEmpty()) return; + + Point start = checkpoints.getFirst(); + Point end = checkpoints.getLast(); + + Thread.startVirtualThread(() -> { + boolean playerHasHolograms = perPlayerHolo.containsKey(player.getUuid()) && + !perPlayerHolo.get(player.getUuid()).getHolograms().isEmpty(); + + if (!playerHasHolograms) { + HologramCache cache = new HologramCache(); + + Map time = player.getDataHandler().get(HypixelDataHandler.Data.PARKOUR_DATA, DatapointParkourData.class).getValue(); + Long bestTime = time.getOrDefault(parkour.getId(), null); + Logger.info("Best time for player " + player.getUsername() + " on parkour " + parkour.getId() + " is " + bestTime); + String bestTimeString = "00:00.000"; + if (bestTime != null) { + bestTimeString = String.format("%02d:%02d.%03d", + (bestTime / 60000), + (bestTime % 60000) / 1000, + (bestTime % 1000)); + } + + String[] startHolograms = new String[]{ + "§e§lParkour Challenge", + "§6§lYour best time: §e§l" + bestTimeString, + "§a§lStart" + }; + PlayerHolograms.ExternalPlayerHologram startHolo = PlayerHolograms.ExternalPlayerHologram.builder() + .pos(start.asPos().add(0.5, 0, 0.5)) + .text(startHolograms) + .player(player) + .instance(instance) + .build(); + PlayerHolograms.addExternalPlayerHologram(startHolo); + cache.add(startHolo); + + for (int i = 1; i < checkpoints.size() - 1; i++) { + Point checkpointPos = checkpoints.get(i); + String[] checkpointHolograms = new String[]{ + "§e§lCheckpoint", + "§b§l#" + i + }; + PlayerHolograms.ExternalPlayerHologram checkpointHolo = PlayerHolograms.ExternalPlayerHologram.builder() + .pos(checkpointPos.asPos().add(0.5, 0, 0.5)) + .text(checkpointHolograms) + .player(player) + .instance(instance) + .build(); + PlayerHolograms.addExternalPlayerHologram(checkpointHolo); + cache.add(checkpointHolo); + } + + if (checkpoints.size() > 1) { + String[] endHolograms = new String[]{ + "§e§lParkour Challenge", + "§c§lEnd" + }; + PlayerHolograms.ExternalPlayerHologram endHolo = PlayerHolograms.ExternalPlayerHologram.builder() + .pos(end.asPos().add(0.5, 0, 0.5)) + .text(endHolograms) + .player(player) + .instance(instance) + .build(); + PlayerHolograms.addExternalPlayerHologram(endHolo); + cache.add(endHolo); + } + + perPlayerHolo.put(player.getUuid(), cache); + return; + } + HologramCache cache = perPlayerHolo.get(player.getUuid()); + cache.getHolograms().forEach(PlayerHolograms::removeExternalPlayerHologram); + perPlayerHolo.remove(player.getUuid()); + updateForPlayer(instance, player); + }); + } + + public void startParkour(HypixelPlayer player) { + if (perPlayerStartTime.containsKey(player.getUuid())) { + perPlayerStartTime.remove(player.getUuid()); + perPlayerStartTime.put(player.getUuid(), new RunData(System.currentTimeMillis(), 0)); + player.sendMessage("§a§lReset your timer to 00:00! Get to the finish line!"); + return; + } + perPlayerStartTime.put(player.getUuid(), new RunData(System.currentTimeMillis(), 0)); + player.sendMessage("§a§lParkour challenge started!"); + player.sendMessage("§aUse §e/parkour checkpoint §ato teleport to the last checkpoint or §e/parkour cancel §ato cancel!"); + + player.getInventory().setItemStack(3, new LastCheckpoint().getItemStack(player)); + player.getInventory().setItemStack(4, new ResetParkour().getItemStack(player)); + player.getInventory().setItemStack(5, new CancelParkour().getItemStack(player)); + } + + public void finishedParkour(HypixelPlayer player) { + Long startTime = perPlayerStartTime.get(player.getUuid()).startTime(); + if (startTime == null) { + player.sendMessage("§cYou haven't started the parkour challenge yet! Use §e/parkour start §cto start!"); + return; + } + + int lastCheckpointIndex = perPlayerStartTime.get(player.getUuid()).lastCheckpointIndex(); + if (lastCheckpointIndex != parkour.getCheckpoints().size() - 2) { + player.sendMessage("§cYou must go through all checkpoints before finishing the parkour challenge!"); + return; + } + + long timeTaken = System.currentTimeMillis() - startTime; + DatapointParkourData datapoint = player.getDataHandler().get(HypixelDataHandler.Data.PARKOUR_DATA, DatapointParkourData.class); + Map data = datapoint.getValue(); + if (data.containsKey(parkour.getId())) { + data.replace(parkour.getId(), Math.min(data.getOrDefault(parkour.getId(), Long.MAX_VALUE), timeTaken)); + } else { + data.put(parkour.getId(), timeTaken); + } + datapoint.setValue(data); + String timeString = String.format("%02d:%02d.%03d", + (timeTaken / 60000), + (timeTaken % 60000) / 1000, + (timeTaken % 1000)); + player.sendMessage("§a§lCongratulations! You finished the parkour challenge in §e§l" + timeString + "§a§l!"); + + if (HypixelConst.getTypeLoader() instanceof LobbyTypeLoader lobbyLoader) { + player.getInventory().clear(); + Map hotbarItems = lobbyLoader.getHotbarItems(); + for (Map.Entry entry : hotbarItems.entrySet()) { + player.getInventory().setItemStack( + entry.getKey(), + entry.getValue().getItemStack(player) + ); + } + } + + updateForPlayer(player.getInstance(), player); + } + + public void checkpointPlayer(HypixelPlayer player, int checkpointIndex) { + RunData runData = perPlayerStartTime.get(player.getUuid()); + if (runData == null) { + player.sendMessage("§cYou haven't started the parkour challenge yet! Use §e/parkour start §cto start!"); + return; + } + if (checkpointIndex <= runData.lastCheckpointIndex()) { + return; + } + perPlayerStartTime.put(player.getUuid(), new RunData(runData.startTime(), checkpointIndex)); + player.sendMessage("§a§lCheckpoint §e§l#" + checkpointIndex + " §areached!"); + } + + public void cancelParkour(HypixelPlayer player) { + if (perPlayerStartTime.containsKey(player.getUuid())) { + perPlayerStartTime.remove(player.getUuid()); + + if (!player.isOnline()) return; + player.sendMessage("§c§lParkour challenge cancelled!"); + + if (HypixelConst.getTypeLoader() instanceof LobbyTypeLoader lobbyLoader) { + player.getInventory().clear(); + Map hotbarItems = lobbyLoader.getHotbarItems(); + for (Map.Entry entry : hotbarItems.entrySet()) { + player.getInventory().setItemStack( + entry.getKey(), + entry.getValue().getItemStack(player) + ); + } + } + } else { + if (!player.isOnline()) return; + player.sendMessage("§cYou haven't started the parkour challenge yet! Use §e/parkour start §cto start!"); + } + } + + public static void removePlayerHolograms(HypixelPlayer player) { + HologramCache cache = perPlayerHolo.remove(player.getUuid()); + if (cache != null) { + for (PlayerHolograms.ExternalPlayerHologram holo : cache.getHolograms()) { + PlayerHolograms.removeExternalPlayerHologram(holo); + } + } + } + + public record RunData(Long startTime, int lastCheckpointIndex) { + } + + @Getter + public static class HologramCache { + private final List holograms = new ArrayList<>(); + + public void add(PlayerHolograms.ExternalPlayerHologram hologram) { + holograms.add(hologram); + } + } +} diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/parkour/Parkour.java b/type.lobby/src/main/java/net/swofty/type/lobby/parkour/Parkour.java new file mode 100644 index 000000000..3789b4ceb --- /dev/null +++ b/type.lobby/src/main/java/net/swofty/type/lobby/parkour/Parkour.java @@ -0,0 +1,13 @@ +package net.swofty.type.lobby.parkour; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.data.datapoints.DatapointParkourData; + +import java.util.List; + +public interface Parkour { + DatapointParkourData.ParkourType getId(); + Pos getStartLocation(); + List getCheckpoints(); +} diff --git a/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/TypePrototypeLobbyLoader.java b/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/TypePrototypeLobbyLoader.java index 696e78647..1cdce822d 100644 --- a/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/TypePrototypeLobbyLoader.java +++ b/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/TypePrototypeLobbyLoader.java @@ -1,6 +1,5 @@ package net.swofty.type.prototypelobby; -import lombok.Getter; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; import net.swofty.commons.CustomWorlds; @@ -18,7 +17,9 @@ import net.swofty.type.generic.tab.TablistManager; import net.swofty.type.generic.tab.TablistModule; import net.swofty.type.lobby.LobbyTypeLoader; +import net.swofty.type.lobby.events.LobbyBlockBreak; import net.swofty.type.lobby.events.LobbyItemEvents; +import net.swofty.type.lobby.events.LobbyParkourEvents; import net.swofty.type.lobby.events.LobbyPlayerJoinEvents; import net.swofty.type.lobby.item.LobbyItem; import net.swofty.type.lobby.item.LobbyItemHandler; @@ -27,6 +28,9 @@ import net.swofty.type.lobby.item.impl.PlayCompass; import net.swofty.type.lobby.item.impl.ProfileItem; import net.swofty.type.lobby.launchpad.LaunchPad; +import net.swofty.type.lobby.parkour.LobbyParkourManager; +import net.swofty.type.lobby.parkour.Parkour; +import net.swofty.type.prototypelobby.parkour.PrototypeLobbyParkour; import net.swofty.type.prototypelobby.util.PrototypeLobbyMap; import org.jetbrains.annotations.Nullable; @@ -36,6 +40,7 @@ public class TypePrototypeLobbyLoader implements LobbyTypeLoader { private static final LobbyItemHandler itemHandler = new LobbyItemHandler(); + public static LobbyParkourManager parkourManager; @Override public ServerType getType() { @@ -55,6 +60,18 @@ public void afterInitialize(MinecraftServer server) { // Register all hotbar items getHotbarItems().values().forEach(itemHandler::add); + + parkourManager = new LobbyParkourManager(getParkour()); + } + + @Override + public Parkour getParkour() { + return new PrototypeLobbyParkour(); + } + + @Override + public @Nullable LobbyParkourManager getParkourManager() { + return parkourManager; } @Override @@ -114,6 +131,8 @@ public List getTraditionalEvents() { // Add lobby base events events.add(new LobbyItemEvents()); events.add(new LobbyPlayerJoinEvents()); + events.add(new LobbyParkourEvents()); + events.add(new LobbyBlockBreak()); return events; } diff --git a/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/events/ActionPlayerBlockBreak.java b/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/events/ActionPlayerBlockBreak.java deleted file mode 100644 index b13079bc3..000000000 --- a/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/events/ActionPlayerBlockBreak.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.swofty.type.prototypelobby.events; - -import net.minestom.server.entity.GameMode; -import net.minestom.server.event.player.PlayerBlockBreakEvent; -import net.swofty.commons.ServerType; -import net.swofty.type.generic.HypixelConst; -import net.swofty.type.generic.event.EventNodes; -import net.swofty.type.generic.event.HypixelEvent; -import net.swofty.type.generic.event.HypixelEventClass; -import net.swofty.type.generic.user.HypixelPlayer; - -public class ActionPlayerBlockBreak implements HypixelEventClass { - - @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) - public void onBlockBreak(PlayerBlockBreakEvent event) { - if (HypixelConst.getTypeLoader().getType() != ServerType.PROTOTYPE_LOBBY) return; - - HypixelPlayer player = (HypixelPlayer) event.getPlayer(); - - if (player.getGameMode() == GameMode.CREATIVE) return; - - event.setCancelled(true); - } -} diff --git a/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/parkour/PrototypeLobbyParkour.java b/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/parkour/PrototypeLobbyParkour.java new file mode 100644 index 000000000..87e4076e3 --- /dev/null +++ b/type.prototypelobby/src/main/java/net/swofty/type/prototypelobby/parkour/PrototypeLobbyParkour.java @@ -0,0 +1,32 @@ +package net.swofty.type.prototypelobby.parkour; + +import net.minestom.server.coordinate.BlockVec; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.data.datapoints.DatapointParkourData; +import net.swofty.type.lobby.parkour.Parkour; + +import java.util.List; + +public class PrototypeLobbyParkour implements Parkour { + + @Override + public DatapointParkourData.ParkourType getId() { + return DatapointParkourData.ParkourType.PROTOTYPE_LOBBY; + } + + @Override + public Pos getStartLocation() { + return new Pos(2.5, 77, 42.5, -135, 0); + } + + @Override + public List getCheckpoints() { + return List.of( + new BlockVec(3, 77, 41), + new BlockVec(40, 91, -10), + new BlockVec(-38, 94, -25), + new BlockVec(-7, 81, 38) + ); + } +}