diff --git a/configuration/skyblock/Minestom.regions.csv b/configuration/skyblock/Minestom.regions.csv index 67fd59ac2..25b634938 100644 --- a/configuration/skyblock/Minestom.regions.csv +++ b/configuration/skyblock/Minestom.regions.csv @@ -89,6 +89,8 @@ dark_thicket,DARK_THICKET,-333,148,-24,-415,59,-122,THE_PARK trial_of_fire,TRIALS_OF_FIRE,-369,120,-100,-357,101,-88,THE_PARK jungle,JUNGLE_ISLAND,-417,149,-130,-507,68,-18,THE_PARK savanna,SAVANNA_WOODLAND,-467,155,-20,-383,56,102,THE_PARK +viking,VIKING_LONGHOUSE,-349,50,53,-315,131,94,THE_PARK +melody,MELODY_PLATEAU,-434,100,63,-399,132,101,THE_PARK sherry,SHERRYS_SHOWROOM,21,98,86,7,76,108,JERRYS_WORKSHOP jerry,JERRYS_WORKSHOP,139,183,200,-163,30,-167,JERRYS_WORKSHOP terry,TERRYS_SHACK,-87,94,30,-95,76,21,JERRYS_WORKSHOP diff --git a/configuration/skyblock/items/vanilla/blocks/wood/spruce.yml b/configuration/skyblock/items/vanilla/blocks/wood/spruce.yml index 132b8a8a5..229a6b449 100644 --- a/configuration/skyblock/items/vanilla/blocks/wood/spruce.yml +++ b/configuration/skyblock/items/vanilla/blocks/wood/spruce.yml @@ -22,6 +22,12 @@ items: material: SPRUCE_WOOD rarity: COMMON components: + - id: CUSTOM_DROP + rules: + - drops: + - item: SPRUCE_LOG + chance: 1 + amount: 1 - id: SELLABLE value: 2.0 - id: PLACEABLE diff --git a/type.generic/src/main/java/net/swofty/type/generic/entity/npc/DialogueController.java b/type.generic/src/main/java/net/swofty/type/generic/entity/npc/DialogueController.java index 27d4720a8..d72d37909 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/entity/npc/DialogueController.java +++ b/type.generic/src/main/java/net/swofty/type/generic/entity/npc/DialogueController.java @@ -64,7 +64,11 @@ public void cancelDialogue(HypixelPlayer player) { } private void handleLineSendingLoop(HypixelPlayer player, HypixelNPC.DialogueSet dialogueSet) { - npc.sendNPCMessage(player, dialogueSet.lines()[0]); + if (dialogueSet.sound() != null) { + npc.sendNPCMessage(player, dialogueSet.lines()[0], dialogueSet.sound()); + } else { + npc.sendNPCMessage(player, dialogueSet.lines()[0]); + } String[] newLines = new String[dialogueSet.lines().length - 1]; System.arraycopy(dialogueSet.lines(), 1, newLines, 0, dialogueSet.lines().length - 1); @@ -85,6 +89,7 @@ private void handleLineSendingLoop(HypixelPlayer player, HypixelNPC.DialogueSet handleLineSendingLoop(player, HypixelNPC.DialogueSet.builder() .key(dialogueSet.key()) .lines(newLines) + .sound(dialogueSet.sound()) .build()); } }).delay(TaskSchedule.seconds(2)).schedule(); diff --git a/type.generic/src/main/java/net/swofty/type/generic/entity/npc/HypixelNPC.java b/type.generic/src/main/java/net/swofty/type/generic/entity/npc/HypixelNPC.java index 45ef9a671..3634f66ae 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/entity/npc/HypixelNPC.java +++ b/type.generic/src/main/java/net/swofty/type/generic/entity/npc/HypixelNPC.java @@ -231,10 +231,14 @@ public void unregister() { } public void sendNPCMessage(HypixelPlayer player, String message) { - player.sendMessage("§e[NPC] " + getName() + "§f: " + message); - player.playSound(Sound.sound().type(Key.key("entity.villager.celebrate")).volume(1.0f).pitch(0.8f + new Random().nextFloat() * 0.4f).build()); + sendNPCMessage(player, message, Sound.sound().type(Key.key("entity.villager.celebrate")).volume(1.0f).pitch(0.8f + new Random().nextFloat() * 0.4f).build()); } + public void sendNPCMessage(HypixelPlayer player, String message, Sound sound) { + player.sendMessage("§e[NPC] " + getName() + "§f: " + message); + player.playSound(sound); + } + protected DialogueController dialogue() { return dialogueController; } @@ -299,7 +303,7 @@ public Map.Entry get(HypixelNPC } @Builder - public record DialogueSet(String key, String[] lines) { + public record DialogueSet(String key, String[] lines, Sound sound) { public static final DialogueSet[] EMPTY = new DialogueSet[0]; } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/SkyBlockDataHandler.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/SkyBlockDataHandler.java index 3758ef7de..446329427 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/SkyBlockDataHandler.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/SkyBlockDataHandler.java @@ -362,6 +362,9 @@ DatapointPetData.class, new DatapointPetData("pet_data")), QUIVER("quiver", false, false, false, DatapointQuiver.class, new DatapointQuiver("quiver")), + RACE_BEST_TIME("race_best_time", false, false, false, DatapointMapStringLong.class, + new DatapointMapStringLong("race_best_time")), + ACCESSORY_BAG("accessory_bag", false, false, false, DatapointAccessoryBag.class, new DatapointAccessoryBag("accessory_bag")), diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/event/actions/race/RaceEvents.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/event/actions/race/RaceEvents.java new file mode 100644 index 000000000..3b76e3c57 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/event/actions/race/RaceEvents.java @@ -0,0 +1,88 @@ +package net.swofty.type.skyblockgeneric.event.actions.race; + +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.skyblockgeneric.race.RaceInstance; +import net.swofty.type.skyblockgeneric.race.RaceManager; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.HashMap; +import java.util.UUID; + +public class RaceEvents implements HypixelEventClass { + + private final HashMap lastClickedTimes = new HashMap<>(); + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) + public void onPlayerJoin(PlayerSpawnEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (!(HypixelConst.getTypeLoader() instanceof RaceInstance raceInstance)) { + return; + } + RaceManager race = raceInstance.getRace(); + if (race == null) return; + + race.updateForPlayer(HypixelConst.getInstanceContainer(), player); + } + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) + public void onPlayerDisconnect(PlayerDisconnectEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (!(HypixelConst.getTypeLoader() instanceof RaceInstance raceInstance)) { + return; + } + RaceManager race = raceInstance.getRace(); + if (race == null) return; + if (!race.getPerPlayerStartTime().containsKey(player.getUuid())) return; + race.cancelRace(player); + } + + @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = true) + public void onPlayerMove(PlayerMoveEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (!(HypixelConst.getTypeLoader() instanceof RaceInstance raceInstance)) { + return; + } + RaceManager race = raceInstance.getRace(); + + if (lastClickedTimes.containsKey(player.getUuid()) && lastClickedTimes.get(player.getUuid()) >= System.currentTimeMillis() - 500) return; + if (race == null) return; + + Pos playerPos = event.getPlayer().getPosition(); + + if (playerPos.samePoint(race.getRace().startPosition(), 0.5)) { + if (!race.getPerPlayerStartTime().containsKey(player.getUuid()) || race.getPerPlayerStartTime().get(player.getUuid()).lastCheckpointIndex() == -1) { + player.playSound(Sound.sound( + Key.key("block.note_block.pling"), Sound.Source.NEUTRAL, + .3f, 0.75f + )); + + race.startRace(player); + lastClickedTimes.put(player.getUuid(), System.currentTimeMillis()); + return; + } + } + + for (int i = 0; i < race.getRace().getCheckpoints().size(); i++) { + if (playerPos.samePoint(race.getRace().getCheckpoints().get(i), 0.5)) { + race.checkpointPlayer(player, i); + lastClickedTimes.put(player.getUuid(), System.currentTimeMillis()); + break; + } + } + + if (playerPos.samePoint(race.getRace().endPosition(), 0.5)) { + race.finishedRace(player); + lastClickedTimes.put(player.getUuid(), System.currentTimeMillis()); + } + } + +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/event/custom/ActionPlayerFinishRace.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/event/custom/ActionPlayerFinishRace.java new file mode 100644 index 000000000..a603255a4 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/event/custom/ActionPlayerFinishRace.java @@ -0,0 +1,17 @@ +package net.swofty.type.skyblockgeneric.event.custom; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.PlayerEvent; +import net.swofty.type.skyblockgeneric.race.Race; + +@Getter +@AllArgsConstructor +public class ActionPlayerFinishRace implements PlayerEvent { + + private final Player player; + private final Race race; + private final long time; + +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIClaimReward.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIClaimReward.java index 1fb4edd0f..dbf87edcd 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIClaimReward.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIClaimReward.java @@ -10,6 +10,7 @@ import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; import net.swofty.type.generic.user.HypixelPlayer; import net.swofty.type.skyblockgeneric.item.SkyBlockItem; +import net.swofty.type.skyblockgeneric.item.updater.NonPlayerItemUpdater; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.List; @@ -48,7 +49,7 @@ public void run(InventoryPreClickEvent e, HypixelPlayer p) { @Override public ItemStack.Builder getItem(HypixelPlayer player) { return ItemStackCreator.appendLore( - new SkyBlockItem(rewardItem).getDisplayItem(), + new NonPlayerItemUpdater(new SkyBlockItem(rewardItem)).getUpdatedItem(), List.of( "", "§eClick to claim!" diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/savanna/MissionGiveMelodyAcaciaLogs.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/savanna/MissionGiveMelodyAcaciaLogs.java index e7d977b83..a06fd8333 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/savanna/MissionGiveMelodyAcaciaLogs.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/savanna/MissionGiveMelodyAcaciaLogs.java @@ -1,15 +1,11 @@ package net.swofty.type.skyblockgeneric.mission.missions.thepark.savanna; import net.minestom.server.coordinate.Pos; -import net.swofty.commons.skyblock.item.ItemType; -import net.swofty.type.skyblockgeneric.gui.inventories.GUIClaimReward; import net.swofty.type.skyblockgeneric.levels.SkyBlockLevelCause; import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; import net.swofty.type.skyblockgeneric.mission.MissionData; import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; -import net.swofty.type.skyblockgeneric.mission.missions.thepark.birchpark.MissionClaimTheTrousers; import net.swofty.type.skyblockgeneric.mission.missions.thepark.jungle.MissionTalkToMolbert; -import net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.MissionTalkToGustave; import net.swofty.type.skyblockgeneric.region.RegionType; import net.swofty.type.skyblockgeneric.skill.SkillCategories; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; @@ -65,7 +61,6 @@ public void onEnd(SkyBlockPlayer player, Map customData, Mission player.getSkills().increase(player, SkillCategories.FORAGING, 1500D); player.getMissionData().startMission(MissionTalkToMolbert.class); - player.getMissionData().startMission(MissionTalkToGustave.class); } @Override diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionGiveKellySpruceLogs.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionGiveKellySpruceLogs.java index c66620d66..85e0c4e6d 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionGiveKellySpruceLogs.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionGiveKellySpruceLogs.java @@ -5,6 +5,7 @@ import net.swofty.type.skyblockgeneric.mission.MissionData; import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; import net.swofty.type.skyblockgeneric.mission.missions.thepark.darkthicket.MissionTravelToTheDarkThicket; +import net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race.MissionTalkToGustave; import net.swofty.type.skyblockgeneric.region.RegionType; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; @@ -36,6 +37,7 @@ public Map onStart(SkyBlockPlayer player, MissionData.ActiveMiss @Override public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { player.getMissionData().startMission(MissionTravelToTheDarkThicket.class); + player.getMissionData().startMission(MissionTalkToGustave.class); } @Override diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceFourth.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceFourth.java new file mode 100644 index 000000000..c46d0c51b --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceFourth.java @@ -0,0 +1,38 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionCompleteTheRaceFourth extends SkyBlockMission { + + @Override + public String getID() { + return "run_the_race_18_seconds"; + } + + @Override + public String getName() { + return "Run the race in 18s"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionTalkToGustaveFifth.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS, RegionType.DARK_THICKET, RegionType.SAVANNA_WOODLAND, RegionType.JUNGLE_ISLAND); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceOneMinute.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceOneMinute.java new file mode 100644 index 000000000..aa32ea7d5 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceOneMinute.java @@ -0,0 +1,40 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionCompleteTheRaceOneMinute extends SkyBlockMission { + + @Override + public String getID() { + return "complete_the_race_one_minute"; + } + + @Override + public String getName() { + return "Run the race in 1m"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionTalkToGustaveAgainAgain.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS, RegionType.DARK_THICKET, RegionType.SAVANNA_WOODLAND, RegionType.JUNGLE_ISLAND); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceThird.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceThird.java new file mode 100644 index 000000000..072093615 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceThird.java @@ -0,0 +1,38 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionCompleteTheRaceThird extends SkyBlockMission { + + @Override + public String getID() { + return "run_the_race_32_seconds"; + } + + @Override + public String getName() { + return "Run the race in 32s"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionTalkToGustaveFourth.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS, RegionType.DARK_THICKET, RegionType.SAVANNA_WOODLAND, RegionType.JUNGLE_ISLAND); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceTwoMinutes.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceTwoMinutes.java new file mode 100644 index 000000000..23e3b2acd --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionCompleteTheRaceTwoMinutes.java @@ -0,0 +1,40 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionCompleteTheRaceTwoMinutes extends SkyBlockMission { + + @Override + public String getID() { + return "complete_the_race_two_minutes"; + } + + @Override + public String getName() { + return "Complete The Woods Race in 2m"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionTalkToGustaveAgain.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS, RegionType.DARK_THICKET, RegionType.SAVANNA_WOODLAND, RegionType.JUNGLE_ISLAND); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionTalkToGustave.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustave.java similarity index 86% rename from type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionTalkToGustave.java rename to type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustave.java index 25af12467..39e6399e1 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/MissionTalkToGustave.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustave.java @@ -1,4 +1,4 @@ -package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce; +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; import net.minestom.server.coordinate.Pos; import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; @@ -24,7 +24,7 @@ public String getID() { @Override public String getName() { - return ""; + return "Talk to Gustave"; } @Override @@ -34,11 +34,11 @@ public Map onStart(SkyBlockPlayer player, MissionData.ActiveMiss @Override public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { - + player.getMissionData().startMission(MissionCompleteTheRaceTwoMinutes.class); } @Override public Set getValidRegions() { - return Set.of(); + return Set.of(RegionType.SPRUCE_WOODS); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveAgain.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveAgain.java new file mode 100644 index 000000000..fc0559ebe --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveAgain.java @@ -0,0 +1,45 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionTalkToGustaveAgain extends SkyBlockMission implements LocationAssociatedMission { + + @Override + public Pos getLocation() { + return new Pos(-363.5, 89, 44.5); + } + + @Override + public String getID() { + return "talk_to_gustave_again"; + } + + @Override + public String getName() { + return "Talk to Gustave"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionCompleteTheRaceOneMinute.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveAgainAgain.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveAgainAgain.java new file mode 100644 index 000000000..56be85827 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveAgainAgain.java @@ -0,0 +1,45 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionTalkToGustaveAgainAgain extends SkyBlockMission implements LocationAssociatedMission { + + @Override + public Pos getLocation() { + return new Pos(-363.5, 89, 44.5); + } + + @Override + public String getID() { + return "talk_to_gustave_again_again"; + } + + @Override + public String getName() { + return "Talk to Gustave"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionCompleteTheRaceThird.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveFifth.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveFifth.java new file mode 100644 index 000000000..496f56ecf --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveFifth.java @@ -0,0 +1,45 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionTalkToGustaveFifth extends SkyBlockMission implements LocationAssociatedMission { + + @Override + public Pos getLocation() { + return new Pos(-363.5, 89, 44.5); + } + + @Override + public String getID() { + return "talk_to_gustave_fourth"; + } + + @Override + public String getName() { + return "Talk to Gustave"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + // final, give rewards here. + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveFourth.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveFourth.java new file mode 100644 index 000000000..c3311de16 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/mission/missions/thepark/spruce/race/MissionTalkToGustaveFourth.java @@ -0,0 +1,45 @@ +package net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.skyblockgeneric.mission.LocationAssociatedMission; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; +import net.swofty.type.skyblockgeneric.region.RegionType; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.Map; +import java.util.Set; + +public class MissionTalkToGustaveFourth extends SkyBlockMission implements LocationAssociatedMission { + + @Override + public Pos getLocation() { + return new Pos(-363.5, 89, 44.5); + } + + @Override + public String getID() { + return "talk_to_gustave_third"; + } + + @Override + public String getName() { + return "Talk to Gustave"; + } + + @Override + public Map onStart(SkyBlockPlayer player, MissionData.ActiveMission mission) { + mission.getNewObjectiveText().forEach(player::sendMessage); + return Map.of(); + } + + @Override + public void onEnd(SkyBlockPlayer player, Map customData, MissionData.ActiveMission mission) { + player.getMissionData().startMission(MissionCompleteTheRaceThird.class); + } + + @Override + public Set getValidRegions() { + return Set.of(RegionType.SPRUCE_WOODS); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/Race.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/Race.java new file mode 100644 index 000000000..3885b8d1e --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/Race.java @@ -0,0 +1,20 @@ +package net.swofty.type.skyblockgeneric.race; + +import net.minestom.server.coordinate.Point; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.List; + +public interface Race { + String getId(); + String getTitle(); + Point startPosition(); + Point endPosition(); + List getCheckpoints(); + void onFinish(SkyBlockPlayer player, long time, boolean isBest); + void onCheckpoint(SkyBlockPlayer player, int checkpointIndex, long time); + int timeLimit(); + default boolean canStart(SkyBlockPlayer player) { + return true; + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/RaceInstance.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/RaceInstance.java new file mode 100644 index 000000000..88a3d5cfb --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/RaceInstance.java @@ -0,0 +1,5 @@ +package net.swofty.type.skyblockgeneric.race; + +public interface RaceInstance { + RaceManager getRace(); +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/RaceManager.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/RaceManager.java new file mode 100644 index 000000000..dcf5358be --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/race/RaceManager.java @@ -0,0 +1,237 @@ +package net.swofty.type.skyblockgeneric.race; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.MinecraftServer; +import net.minestom.server.coordinate.Point; +import net.minestom.server.instance.Instance; +import net.minestom.server.timer.ExecutionType; +import net.minestom.server.timer.TaskSchedule; +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.data.datapoints.DatapointMapStringLong; +import net.swofty.type.generic.entity.hologram.PlayerHolograms; +import net.swofty.type.generic.event.HypixelEventHandler; +import net.swofty.type.skyblockgeneric.data.SkyBlockDataHandler; +import net.swofty.type.skyblockgeneric.event.custom.ActionPlayerFinishRace; +import net.swofty.type.skyblockgeneric.user.SkyBlockActionBar; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@AllArgsConstructor +public class RaceManager { + + @Getter + private final Race race; + + @Getter + private final Map perPlayerStartTime = new ConcurrentHashMap<>(); + + @Getter + private static final Map perPlayerHolo = new HashMap<>(); + + public void updateForPlayer(Instance instance, SkyBlockPlayer player) { + List checkpoints = race.getCheckpoints(); + if (checkpoints.isEmpty()) return; + + Point start = race.startPosition(); + Point end = race.endPosition(); + + boolean isSame = start.sameBlock(end); + + Thread.startVirtualThread(() -> { + boolean playerHasHolograms = perPlayerHolo.containsKey(player.getUuid()) && + !perPlayerHolo.get(player.getUuid()).getHolograms().isEmpty(); + + if (!playerHasHolograms) { + HologramCache cache = new HologramCache(); + String[] startHolograms = new String[]{ + getRace().getTitle(), + "§aStart" + }; + + if (perPlayerStartTime.containsKey(player.getUuid())) { + RunData runData = perPlayerStartTime.get(player.getUuid()); + if (runData.lastCheckpointIndex() == -1) { + startHolograms = new String[]{ + getRace().getTitle(), + "§aReset Timer", + }; + } else if (isSame) { + startHolograms = new String[]{ + getRace().getTitle(), + "§aFinish" + }; + } + } + + 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 (Point checkpointPos : checkpoints) { + String[] checkpointHolograms = new String[]{ + getRace().getTitle(), + "§aCheckpoint" + }; + 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 (!isSame) { + String[] endHolograms = new String[]{ + getRace().getTitle(), + "§aFinish" + }; + 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 startRace(SkyBlockPlayer player) { + if (perPlayerStartTime.containsKey(player.getUuid())) { + perPlayerStartTime.remove(player.getUuid()); + perPlayerStartTime.put(player.getUuid(), new RunData(System.currentTimeMillis(), -1)); + player.sendMessage(race.getTitle() + " §eTimer reset to §a0:00§e!"); + return; + } + + if (!getRace().canStart(player)) return; + + perPlayerStartTime.put(player.getUuid(), new RunData(System.currentTimeMillis(), -1)); + player.sendMessage(race.getTitle() + " §eRace started! Good luck!"); + updateForPlayer(HypixelConst.getInstanceContainer(), player); + + MinecraftServer.getSchedulerManager().submitTask(() -> { + if (!perPlayerStartTime.containsKey(player.getUuid())) { + return TaskSchedule.stop(); + } + + // Check for time limit + if (race.timeLimit() > 0) { + long timeElapsed = System.currentTimeMillis() - perPlayerStartTime.get(player.getUuid()).startTime(); + if (timeElapsed >= race.timeLimit() * 1000L) { + perPlayerStartTime.remove(player.getUuid()); + player.sendMessage(race.getTitle() + " §cRace cancelled! Time limit reached!"); + player.playSound(Sound.sound( + Key.key("block.note_block.pling"), Sound.Source.NEUTRAL, + .3f, 0.75f + )); + updateForPlayer(HypixelConst.getInstanceContainer(), player); + return TaskSchedule.stop(); + } + } + + long timeElapsed = System.currentTimeMillis() - perPlayerStartTime.get(player.getUuid()).startTime(); + String timeString = String.format("%02d:%05.2f", timeElapsed / 60000, (timeElapsed % 60000) / 1000.0); + SkyBlockActionBar.getFor(player).addReplacement( + SkyBlockActionBar.BarSection.HEALTH, + new SkyBlockActionBar.DisplayReplacement( + race.getTitle() + " §e" + timeString, + 25, + 5 + ) + ); + return TaskSchedule.millis(200); + }, ExecutionType.TICK_END); + } + + public void checkpointPlayer(SkyBlockPlayer player, int checkpointIndex) { + RunData runData = perPlayerStartTime.get(player.getUuid()); + if (runData == null) { + player.sendMessage(race.getTitle() + " §cThis is the end of a race, usually you start at the beginning!"); + return; + } + if (checkpointIndex <= runData.lastCheckpointIndex()) { + return; + } + perPlayerStartTime.put(player.getUuid(), new RunData(runData.startTime(), checkpointIndex)); + race.onCheckpoint(player, checkpointIndex, System.currentTimeMillis() - runData.startTime()); + updateForPlayer(HypixelConst.getInstanceContainer(), player); + } + + public void finishedRace(SkyBlockPlayer player) { + RunData runData = perPlayerStartTime.get(player.getUuid()); + if (runData == null) { + player.sendMessage(race.getTitle() + " §cThis is the end of a race, usually you start at the beginning!"); + return; + } + + if (runData.lastCheckpointIndex() < race.getCheckpoints().size() - 1) { + player.sendMessage(race.getTitle() + " §cYou haven't passed all the checkpoints yet!"); + return; + } + + perPlayerStartTime.remove(player.getUuid()); + long totalTime = System.currentTimeMillis() - runData.startTime(); + + SkyBlockDataHandler dataHandler = player.getSkyblockDataHandler(); + DatapointMapStringLong data = dataHandler.get(SkyBlockDataHandler.Data.RACE_BEST_TIME, DatapointMapStringLong.class); + + boolean isRecord = false; + if (data.getValue().isEmpty()) { + data.getValue().put(race.getId(), totalTime); + isRecord = true; + } else if (!data.getValue().containsKey(race.getId()) || totalTime < data.getValue().get(race.getId())) { + data.getValue().put(race.getId(), totalTime); + isRecord = true; + } + + race.onFinish(player, totalTime, isRecord); + updateForPlayer(HypixelConst.getInstanceContainer(), player); + + HypixelEventHandler.callCustomEvent(new ActionPlayerFinishRace( + player, + race, + totalTime + )); + } + + public void cancelRace(SkyBlockPlayer player) { + if (!perPlayerStartTime.containsKey(player.getUuid())) return; + perPlayerStartTime.remove(player.getUuid()); + updateForPlayer(HypixelConst.getInstanceContainer(), player); + } + + 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.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/region/RegionType.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/region/RegionType.java index 155a8923d..8bf4e3f22 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/region/RegionType.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/region/RegionType.java @@ -39,10 +39,11 @@ public enum RegionType { BIRCH_PARK("Birch Park", "§a", BirchParkConfiguration.class, BirchParkBiome.class), HOWLING_CAVE("Howling Cave", null, BirchParkBiome.class), SPRUCE_WOODS("Spruce Woods", "§a", SpruceWoodsConfiguration.class, SpruceWoodsBiome.class), + VIKING_LONGHOUSE("Viking Longhouse", "§b", SpruceWoodsConfiguration.class, SpruceWoodsBiome.class), DARK_THICKET("Dark Thicket", "§a", DarkOakConfiguration.class, DarkThicketBiome.class), TRIALS_OF_FIRE("Trials of Fire", "§c", null, DarkThicketBiome.class), SAVANNA_WOODLAND("Savanna Woodland", "§a", SavannaWoodlandConfiguration.class), - MELODY_PLATEAU("Melody's Plateau", "§5", SavannaWoodlandConfiguration.class), + MELODY_PLATEAU("Melody's Plateau", "§d", SavannaWoodlandConfiguration.class), JUNGLE_ISLAND("Jungle Island", "§a", JungleIslandConfiguration.class), JERRYS_WORKSHOP("Jerry's Workshop", "§c"), diff --git a/type.thepark/src/main/java/net/swofty/type/thepark/TypeTheParkLoader.java b/type.thepark/src/main/java/net/swofty/type/thepark/TypeTheParkLoader.java index a2ca54045..7c677fffd 100644 --- a/type.thepark/src/main/java/net/swofty/type/thepark/TypeTheParkLoader.java +++ b/type.thepark/src/main/java/net/swofty/type/thepark/TypeTheParkLoader.java @@ -31,6 +31,8 @@ import net.swofty.type.skyblockgeneric.mission.MissionData; import net.swofty.type.skyblockgeneric.mission.SkyBlockMission; import net.swofty.type.skyblockgeneric.mission.missions.thepark.jungle.*; +import net.swofty.type.skyblockgeneric.race.RaceInstance; +import net.swofty.type.skyblockgeneric.race.RaceManager; import net.swofty.type.skyblockgeneric.tabmodules.AccountInformationModule; import net.swofty.type.skyblockgeneric.tabmodules.SkyBlockPlayersOnlineModule; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; @@ -44,9 +46,10 @@ import java.util.Map; import java.util.stream.Collectors; -public class TypeTheParkLoader implements SkyBlockTypeLoader { +public class TypeTheParkLoader implements SkyBlockTypeLoader, RaceInstance { public static List entities = new ArrayList<>(); + private static final RaceManager raceManager = new RaceManager(new WoodsRacing()); @Override public ServerType getType() { @@ -206,6 +209,11 @@ private static float[] rotation(float xDeg, float yDeg, float zDeg) { }; } + @Override + public RaceManager getRace() { + return raceManager; + } + record TrapdoorSide(float x, float y, float z, float rotY, float tiltX, float tiltZ) { } diff --git a/type.thepark/src/main/java/net/swofty/type/thepark/WoodsRacing.java b/type.thepark/src/main/java/net/swofty/type/thepark/WoodsRacing.java new file mode 100644 index 000000000..ff4e4d673 --- /dev/null +++ b/type.thepark/src/main/java/net/swofty/type/thepark/WoodsRacing.java @@ -0,0 +1,86 @@ +package net.swofty.type.thepark; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.data.datapoints.DatapointMapStringLong; +import net.swofty.type.skyblockgeneric.data.SkyBlockDataHandler; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race.*; +import net.swofty.type.skyblockgeneric.race.Race; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.List; + +public class WoodsRacing implements Race { + + private final Pos start = new Pos(-362, 89, 42); + + @Override + public String getId() { + return "woods_racing"; + } + + @Override + public String getTitle() { + return "§a§lWOODS RACING"; + } + + @Override + public Point startPosition() { + return start; + } + + @Override + public Point endPosition() { + return start; + } + + @Override + public List getCheckpoints() { + return List.of(new Pos(-417, 128, -111)); + } + + @Override + public void onFinish(SkyBlockPlayer player, long time, boolean isRecord) { + player.sendMessage(getTitle() + " §eRace finished in " + String.format("%02d:%05.2f", time / 60000, (time % 60000) / 1000.0) + "!" + (isRecord ? " §dNew record!" : "")); + + // this behavior could be moved to the missions with the ActionPlayerFinishRace event + int sec = (int) (time / 1000); + MissionData data = player.getMissionData(); + if (data.isCurrentlyActive(MissionCompleteTheRaceTwoMinutes.class)) { + if (sec <= 120) { + data.endMission(MissionCompleteTheRaceTwoMinutes.class); + } + } + if (data.isCurrentlyActive(MissionCompleteTheRaceOneMinute.class)) { + if (sec <= 60) { + data.endMission(MissionCompleteTheRaceOneMinute.class); + } + } + if (data.isCurrentlyActive(MissionCompleteTheRaceThird.class)) { + if (sec <= 32) { + data.endMission(MissionCompleteTheRaceThird.class); + } + } + if (data.isCurrentlyActive(MissionCompleteTheRaceFourth.class)) { + if (sec <= 18) { + data.endMission(MissionCompleteTheRaceFourth.class); + } + } + } + + @Override + public void onCheckpoint(SkyBlockPlayer player, int checkpointIndex, long time) { + player.sendMessage(getTitle() + " §aYou reached the other end! Now go back to the start!"); + } + + @Override + public int timeLimit() { + return 180; + } + + @Override + public boolean canStart(SkyBlockPlayer player) { + return player.getMissionData().hasCompleted(MissionTalkToGustave.class); + } +} diff --git a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCGustave.java b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCGustave.java index d1f487ded..ce13492f0 100644 --- a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCGustave.java +++ b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCGustave.java @@ -1,10 +1,19 @@ package net.swofty.type.thepark.npcs; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minestom.server.coordinate.Pos; +import net.swofty.commons.skyblock.item.ItemType; import net.swofty.type.generic.entity.npc.HypixelNPC; import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration; import net.swofty.type.generic.event.custom.NPCInteractEvent; import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.gui.inventories.GUIClaimReward; +import net.swofty.type.skyblockgeneric.mission.MissionData; +import net.swofty.type.skyblockgeneric.mission.missions.thepark.spruce.race.*; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import org.jspecify.annotations.NonNull; + +import java.util.List; public class NPCGustave extends HypixelNPC { @@ -34,11 +43,116 @@ public Pos position(HypixelPlayer player) { public boolean looking(HypixelPlayer player) { return true; } + + @Override + public @NonNull String chatName() { + return "§aGustave"; + } }); } @Override public void onClick(NPCInteractEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (isInDialogue(player)) return; + MissionData missionData = player.getMissionData(); + if (missionData.isCurrentlyActive(MissionTalkToGustave.class)) { + setDialogue(player, "first-interaction").thenRun(() -> { + missionData.endMission(MissionTalkToGustave.class); + }); + return; + } + if (missionData.isCurrentlyActive(MissionCompleteTheRaceTwoMinutes.class)) { + sendNPCMessage(player, "Run over the pressure plate to start racing! Run up to the §eopposite §fside of the island AND back to complete the race. Can you finish it in §e2 minutes§f?"); + return; + } + if (missionData.isCurrentlyActive(MissionTalkToGustaveAgain.class)) { + setDialogue(player, "completed-1").thenRun(() -> { + // TODO: give polished pebble + missionData.endMission(MissionTalkToGustaveAgain.class); + }); + return; + } + if (missionData.isCurrentlyActive(MissionCompleteTheRaceOneMinute.class)) { + sendNPCMessage(player, "Keep it up! Can you finish the race in 1 minute or less?"); + return; + } + if (missionData.isCurrentlyActive(MissionTalkToGustaveAgainAgain.class)) { + setDialogue(player, "completed-2").thenRun(() -> { + new GUIClaimReward(ItemType.HUNTER_KNIFE, () -> { + missionData.endMission(MissionTalkToGustaveAgainAgain.class); + }).open(player); + }); + return; + } + if (missionData.isCurrentlyActive(MissionCompleteTheRaceThird.class)) { + sendNPCMessage(player, "Keep at it! Can you finish the race in 32 seconds or less?"); + return; + } + if (missionData.isCurrentlyActive(MissionTalkToGustaveFourth.class)) { + setDialogue(player, "completed-3").thenRun(() -> { + // TODO: give trinket (wolf paw) + missionData.endMission(MissionTalkToGustaveFourth.class); + }); + return; + } + if (missionData.isCurrentlyActive(MissionCompleteTheRaceFourth.class)) { + sendNPCMessage(player, "Real challenge! Can you finish the race in 18 seconds or less and beat my personal record?"); + return; + } + if (missionData.isCurrentlyActive(MissionTalkToGustaveFifth.class)) { + setDialogue(player, "completed-4").thenRun(() -> { + // TODO: give silky lichen + missionData.endMission(MissionTalkToGustaveFifth.class); + }); + return; + } + setDialogue(player, "idle-" + (1 + (int) (Math.random() * 2))); + } + + @Override + protected DialogueSet[] dialogues(HypixelPlayer player) { + return List.of( + DialogueSet.builder().key("first-interaction").lines(new String[]{ + "There's nothing like island racing!", + "I've traveled many islands and this is my favorite to race on. Want to try?", + "To complete the race, you'll need to reach the §eopposite side of the island §fAND come all the way back.", + "Start the race by walking over the pressure plate. If you finish in under 2 minutes, I'll reward you for it! Good luck!", + "The other end of the race can be found at -407 128 -119 and is also marked by a beacon.", + "There are also crit-particle trails to follow." + }).build(), + DialogueSet.builder().key("not-too-fast").lines(new String[]{ + "Hey! Come talk to me over here! I'll tell you all about racing!" + }).build(), + DialogueSet.builder().key("completed-1").lines(new String[]{ + "Not bad, " + LegacyComponentSerializer.legacySection().serialize(player.getColouredName()) + "§f!", + "I hope you can get better than this!", + "Here's a §aPolished Pebble§f. I found it laying around...", + "Try again, but this time come back in 1 minute or less!" + }).build(), + DialogueSet.builder().key("completed-2").lines(new String[]{ + "You're getting much faster!", + "Use my §aHunter Knife§f. Holding it somehow makes me go faster.", + "See if you can beat 32 seconds!" + }).build(), + DialogueSet.builder().key("completed-3").lines(new String[]{ + "Now we're cooking! You're getting close to my record!", + "Here's an trinket I've found in the caverns behind the waterfall.", + "Finish the race in under 18 seconds and come talk to me!" + }).build(), + DialogueSet.builder().key("completed-4").lines(new String[]{ + "You did it! Congratulations, " + LegacyComponentSerializer.legacySection().serialize(player.getColouredName()) + "§f. You are now faster than §aGustave§f!", + "I've been racing this island so fast that some bark from the trees snatched right off!", + "Here's a rare piece of §9Silky Lichen§f. Maybe you can do something with it.", + "I have this lichen could improve some weapons!" + }).build(), + DialogueSet.builder().key("idle-1").lines(new String[]{ + "You're always welcome to try the race again. Just run over the pressure plate to begin!" + }).build(), + DialogueSet.builder().key("idle-2").lines(new String[]{ + "It's a great day to go for a run!" + }).build() + ).toArray(DialogueSet[]::new); } } diff --git a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCJuliette.java b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCJuliette.java index 1b6a9f368..b3a5f456f 100644 --- a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCJuliette.java +++ b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCJuliette.java @@ -5,6 +5,9 @@ import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration; import net.swofty.type.generic.event.custom.NPCInteractEvent; import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.List; public class NPCJuliette extends HypixelNPC { @@ -12,7 +15,7 @@ public NPCJuliette() { super(new HumanConfiguration() { @Override public String[] holograms(HypixelPlayer player) { - return new String[]{"Juliette"}; + return new String[]{"Juliette", "§e§lCLICK"}; } @Override @@ -39,6 +42,169 @@ public boolean looking(HypixelPlayer player) { @Override public void onClick(NPCInteractEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (isInDialogue(player)) return; + setDialogue(player, "q1-start"); } + + @Override + protected DialogueSet[] dialogues(HypixelPlayer player) { + return List.of( + + DialogueSet.builder().key("completing-step-not-holding").lines( + new String[]{ + "You found §eRomero§f?", + "Did he have anything for me?" + } + ).build(), + + // Quest Step 1 – Savanna Woodland + DialogueSet.builder().key("q1-start").lines(new String[]{ + "Nice to meet you!", + "Could you find my dear §eRomero§f?", + "He's in a cave somewhere under the §aSavanna Woodland§f.", + "...", + "Oh!", + "He wears glasses and blends well in a crowd!" + }).build(), + + DialogueSet.builder().key("q1-complete").lines(new String[]{ + "You found Romero? That's weird, I just saw him!", + "He wanted me to have this? So sweet!", + "...", + "A yellow rock? What's the point of that?", + "Here, you keep it!" + }).build(), + + // Quest Step 2 – Flower House + DialogueSet.builder().key("q2-start").lines(new String[]{ + "Since you're here, can you find Romero again?", + "He said he was getting groceries at the downtown markets." + }).build(), + + DialogueSet.builder().key("q2-complete").lines(new String[]{ + "Romero? He just came over with the groceries.", + "These flowers are for me?", + "That's so kind of you!", + "You know I'm with Romero, right?" + }).build(), + + // Quest Step 3 – Graveyard + DialogueSet.builder().key("q3-start").lines(new String[]{ + "Dear, sorry for the inconvenience...", + "Can you please find Romero again?", + "He's at some sort of emerald altar..." + }).build(), + + DialogueSet.builder().key("q3-complete").lines(new String[]{ + "Oof! Why did you bring this all the way here?", + "How is that a gift?", + "Just keep it! I don't want this!" + }).build(), + + // Quest Step 4 – Crimson Isle + DialogueSet.builder().key("q4-start").lines(new String[]{ + "What I want is Romero!", + "For some reason, he's now looking for...", + "The largest, most evil patch of fungus ever!" + }).build(), + + DialogueSet.builder().key("q4-complete").lines(new String[]{ + "Stew? I love stew!", + "...", + "!!!", + "Yuck!!!", + "Yuck!!!", + "Yuck!!!", + "Yuck!!!", + "Yuck!!!", + "DISGUSTING!", + "Is that a stew from hell?", + "Maybe Romero should give you cooking lessons!" + }).build(), + + // Quest Step 5 – Mountain + DialogueSet.builder().key("q5-start").lines(new String[]{ + "Oh! I love Romero! He has so many skills.", + "Did you know he likes photography?", + "Just now, he went to take pictures of the old castle.", + "Go find him!" + }).build(), + + DialogueSet.builder().key("q5-complete").lines(new String[]{ + "You really believe that this is from the Moon?", + "How? Someone flew up there?", + "Thanks, I guess.", + "Actually, I heard Romero flew close to the Sun.", + "Sounds more like a metaphor, like..." + }).build(), + + // Quest Step 6 – Gold Mine + DialogueSet.builder().key("q6-start").lines(new String[]{ + "He went to where gold is smelted.", + "Please find him, wherever that is!" + }).build(), + + DialogueSet.builder().key("q6-complete").lines(new String[]{ + "This is a wonderful gift!", + "I will keep this one for myself.", + "Sometimes, some things have to remain a mystery." + }).build(), + + // Quest Step 7 – Wilderness + DialogueSet.builder().key("q7-start").lines(new String[]{ + "About something not so secret, can you get in touch with Romero?", + "He told me he's in his home." + }).build(), + + DialogueSet.builder().key("q7-complete").lines(new String[]{ + "Uh? Romero solved his cube?", + "That's unbelievable.", + "No, really, I don't believe it at all." + }).build(), + + // Quest Step 8 – Colosseum + DialogueSet.builder().key("q8-start").lines(new String[]{ + "I don't know why Romero is trying to be so unlike himself lately.", + "I like him just the way he is!", + "He says he's getting equipment for a duel.", + "Can you find Romero before he hurts himself?" + }).build(), + + DialogueSet.builder().key("q8-complete").lines(new String[]{ + "This is a wonderful poem.", + "But, does he really think there's someone else?", + "I can't possibly imagine why he'd think that." + }).build(), + + // Quest Step 9 – Mushroom Desert + DialogueSet.builder().key("q9-start").lines(new String[]{ + "Romero went to meditate at his retreat.", + "It's the smallest house in SkyBlock.", + "Please find him, I'm worried!" + }).build(), + + DialogueSet.builder().key("q9-complete").lines(new String[]{ + "Another gift from Romero?", + "Thank you for delivering it.", + "I don't know how Romero has such hard a time understanding that I LOVE HIM.", + "I like him just the way he is!", + "I don't need tons of gifts to know that." + }).build(), + + // Quest Step 10 – Jungle Island + DialogueSet.builder().key("q10-before-suit").lines(new String[]{ + "It's decided, this is the day!", + "We're making it official!" + }).build(), + + DialogueSet.builder().key("q10-after-suit").lines(new String[]{ + "We can't thank you enough for bringing us together!", + "You're the best, !" + }).build() + + ).toArray(DialogueSet[]::new); + } + } diff --git a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCMelancholicViking.java b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCMelancholicViking.java index 10135398b..dc0673be0 100644 --- a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCMelancholicViking.java +++ b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCMelancholicViking.java @@ -1,10 +1,20 @@ package net.swofty.type.thepark.npcs; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; import net.minestom.server.coordinate.Pos; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; import net.swofty.type.generic.entity.npc.HypixelNPC; import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration; import net.swofty.type.generic.event.custom.NPCInteractEvent; import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import org.jspecify.annotations.NonNull; + +import java.time.LocalDate; +import java.time.Period; +import java.util.List; public class NPCMelancholicViking extends HypixelNPC { @@ -12,7 +22,7 @@ public NPCMelancholicViking() { super(new HumanConfiguration() { @Override public String[] holograms(HypixelPlayer player) { - return new String[]{"§aMelancholic Viking", "§e§lCLICK"}; + return new String[]{"§bMelancholic Viking", "§e§lCLICK"}; } @Override @@ -34,11 +44,86 @@ public Pos position(HypixelPlayer player) { public boolean looking(HypixelPlayer player) { return true; } + + @Override + public @NonNull String chatName() { + return "§bViking"; + } }); } @Override public void onClick(NPCInteractEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (isInDialogue(player)) return; + + ItemStack heldItemStack = player.getItemInMainHand(); + Material heldItem = heldItemStack.material(); + if (heldItem.name().endsWith("_boat")) { + setDialogue(player, "holding-boat"); + return; + } + if (heldItem == Material.ICE || heldItem == Material.PACKED_ICE) { + setDialogue(player, "holding-ice"); + return; + } + if (heldItem == Material.COD || heldItem == Material.SALMON) { + setDialogue(player, "holding-raw-fish"); + return; + } + if (heldItem == Material.FISHING_ROD) { + setDialogue(player, "holding-fishing-rod"); + return; + } + // TODO: finish this monstrosity + } + + @Override + protected DialogueSet[] dialogues(HypixelPlayer player) { + LocalDate pastDate = LocalDate.of(2009, 9, 1); + LocalDate now = LocalDate.now(); + Period period = Period.between(pastDate, now); + int years = period.getYears(); + int months = period.getMonths(); + return List.of( + DialogueSet.builder().key("intro").lines(new String[]{ + "I last saw the sea §b" + years + " years, " + months + " months §fago.", + "I wish I could remember what it felt like!", + "Sadly, my memory is now my worst enemy.", + "Please, help me remember the §bsea§f." + }).sound(Sound.sound().type(Key.key("entity.villager.hurt")).pitch(0.5f).volume(0.9f).build()).build(), + DialogueSet.builder().key("holding-boat").lines(new String[]{ + "Wow!", + "A boat!", + "Throw them at people you hate" + }).build(), + DialogueSet.builder().key("starting-to-splash").lines(new String[]{ + "§aYES! §fThis totally reminds me of the sea!", + "Although... there were §amore §ffishes back then." + }).build(), + DialogueSet.builder().key("enough-splashes").lines(new String[]{ + "§a§lWOW! §fThis §bfeels §fjust like on my Drakkar!", + "I suddenly feel so great!", + "Thanks for bringing §ejoy §ejoy §fto an old viking!", + "Take a look at my wares!" + }).build(), + DialogueSet.builder().key("splashing-no-requirements").lines(new String[]{ + "§eWow! Nice move! §fThere just isn't the ambience to fully appreciate it." + }).build(), + DialogueSet.builder().key("holding-ice").lines(new String[]{ + "Don't you have some liquid water?" + }).build(), + DialogueSet.builder().key("holding-raw-fish").lines(new String[]{ + "I prefer when the fishes are lively and go splish-splash in the water!" + }).build(), + DialogueSet.builder().key("holding-fishing-rod").lines(new String[]{ + "It's a nice thought, but I don't feel like fishing right now." + }).build(), + DialogueSet.builder().key("holding-magical-water-bucket").lines(new String[]{ + "There's as much water here as an ocean.", + "If only you could pour it somewhere!" + }).build() + ).toArray(DialogueSet[]::new); } } diff --git a/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCRomero.java b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCRomero.java new file mode 100644 index 000000000..7e7842d78 --- /dev/null +++ b/type.thepark/src/main/java/net/swofty/type/thepark/npcs/NPCRomero.java @@ -0,0 +1,340 @@ +package net.swofty.type.thepark.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.entity.npc.HypixelNPC; +import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration; +import net.swofty.type.generic.event.custom.NPCInteractEvent; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import org.jspecify.annotations.NonNull; + +import java.util.List; + +public class NPCRomero extends HypixelNPC { + + public NPCRomero() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{}; + } + + @Override + public String signature(HypixelPlayer player) { + return "mJ2X8Afy14c9pFiAvCNGCKuZPLt3cinpBJKloxz5mwcN00BEon6o3XqEPWWX8ZqamCVSG7lJanxFD2xU7b9zgpZpubk/dnKyilFTmXLrjHEBIfWBJQZO/Ae9XyghfIJYeEF0eLLyNN3e0dddBxv4HmkiwrcHVg1n9Ped5t1Wt55W3YaTKQhkG/S+f4FqM5c1Wkmcwgz2291j9+j6Put7as18+JFHhD+bdOj7Tjqg5Mf4YN6jkjsLQtHMwsIUr0WZ35fovtkjb0s3FkJob1Zy4dR3+tv6gTne9WINO9p1M3U5oSdT1I3/3m7q1qBmaDIQgtD2NgANHCLOtKo3886Ay0ya4YkQ701MD9mHYoAQwWEpGDwCNxtqQNxbCl0ThLOY0xg//XBFSOPCxAJVrQ07IVyqzYOmHKDq1XvAqgLxTN7GsTCiPjVd6hpT9qY2d7MigX2drDFppSZzKbibtQoovK68U1oCAJiNQZJKdJxcWM1ryllYwAx6yNrXSf744rU5mAkXr/tPO0XxuouaZpQPLBGj5ZP8yzG8TRA3GOYcvhoFxhE4Toxp3OeQ4u0RKAx821LqN+LzbXlNz7Vsl4djA637cuNdJZyxAWGdKCxC+CNOd+Xb4V98wEgpRRNUh3XE3YJDXhbhcOdMIdwx9j6iZz5IyJD46k50qCYHrZyfxV8="; + } + + @Override + public String texture(HypixelPlayer player) { + return "eyJ0aW1lc3RhbXAiOjE1NzI4OTE3NzU3OTcsInByb2ZpbGVJZCI6IjQxZDNhYmMyZDc0OTQwMGM5MDkwZDU0MzRkMDM4MzFiIiwicHJvZmlsZU5hbWUiOiJNZWdha2xvb24iLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdlZGJkZTcxOGQ3NGJjNmU4MGMxN2JmMjdiZmZhNmQ4YjI3OTViZjIwNTY0ZmI5YjBkNTgzNGNhNTkyZTNjZTYifX19"; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(-456.500, 100.000, 30.500, 65, 0); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public @NonNull String chatName() { + return "Romero"; + } + }); + } + + @Override + public void onClick(NPCInteractEvent event) { + SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer(); + if (isInDialogue(player)) return; + + setDialogue(player, "idle"); + } + + @Override + protected DialogueSet[] dialogues(HypixelPlayer player) { + return List.of( + DialogueSet.builder().key("romero-bring-item").lines(new String[]{ + "Bring the item I gave you to Juliette!" + }).build(), + + DialogueSet.builder().key("romero-bring-item-missing").lines(new String[]{ + "Bring the item I gave you to Juliette!", + "If you lost the item, we can fix that!", + "Although, you will have to cover my expenses, considering the sentimental value..." + }).build(), + + // Quest Step 1 – Savanna Woodland + DialogueSet.builder().key("r-q1-start").lines(new String[]{ + "I'm... trying to find gold...", + "but I can only find sand and stone here!", + "I heard of a gold mine in the Savanna Woodland.", + "I'd like to craft a gift for my beloved Juliette.", + "Could you find some gold for me, please?" + }).build(), + + DialogueSet.builder().key("r-q1-complete").lines(new String[]{ + "Thank you so much!", + "Here, could you do me a favor and bring this gift back to Juliette?" + }).build(), + + // Quest Step 2 – Village + DialogueSet.builder().key("r-q2-start").lines(new String[]{ + "Groceries?", + "Haha!", + "I'm here to get a flower bouquet for Juliette.", + "Could you help out and get some flowers?" + }).build(), + + DialogueSet.builder().key("r-q2-wrong-material").lines(new String[]{ + "I was thinking of a bouquet of roses." + }).build(), + + DialogueSet.builder().key("r-q2-1-poppy").lines(new String[]{ + "Just one? That's not much of a bouquet!" + }).build(), + + DialogueSet.builder().key("r-q2-2-7-poppies").lines(new String[]{ + "Going to need a few more than that!" + }).build(), + + DialogueSet.builder().key("r-q2-8-11-poppies").lines(new String[]{ + "I love roses! Can you get more?" + }).build(), + + DialogueSet.builder().key("r-q2-12-14-poppies").lines(new String[]{ + "That's a lot of flowers! Maybe a few more..." + }).build(), + + DialogueSet.builder().key("r-q2-complete").lines(new String[]{ + "Wow, they're beautiful!", + "Please bring the bouquet to Juliette!" + }).build(), + + // Quest Step 3 – Graveyard + DialogueSet.builder().key("r-q3-start").lines(new String[]{ + "I love emeralds!", + "There's emerald on everything I like!", + "This one can't be dislodged!", + "Could you bring me some emerald?" + }).build(), + + DialogueSet.builder().key("r-q3-insufficient").lines(new String[]{ + "I'm going to need a full stack of those!" + }).build(), + + DialogueSet.builder().key("r-q3-enchanted-emerald").lines(new String[]{ + "Very cool, but regular emeralds will do the trick." + }).build(), + + DialogueSet.builder().key("r-q3-enchanted-block").lines(new String[]{ + "Although I adore this pristine piece of emerald, I don't need it yet." + }).build(), + + DialogueSet.builder().key("r-q3-block").lines(new String[]{ + "I don't need a whole block!" + }).build(), + + DialogueSet.builder().key("r-q3-complete").lines(new String[]{ + "Am", + "-az-", + "-ing!", + "Now bring this gift to Juliette!" + }).build(), + + // Quest Step 4 – Crimson Isle + DialogueSet.builder().key("r-q4-start").lines(new String[]{ + "Hey, you again!", + "I wanted mushrooms to concoct a stew,", + "but these big red ones won't cut it.", + "I need some decent mushrooms." + }).build(), + + DialogueSet.builder().key("r-q4-red-mushroom").lines(new String[]{ + "I can't cook a gourmet stew with tiny mushrooms!", + "I need bigger ones!" + }).build(), + + DialogueSet.builder().key("r-q4-brown-mushroom").lines(new String[]{ + "That's not the color of mushroom I'm looking for." + }).build(), + + DialogueSet.builder().key("r-q4-red-block").lines(new String[]{ + "Sweet! Just what I need!", + "EXCEPT... Could you enchant it?", + "I need the perfect stew for my beloved Juliette." + }).build(), + + DialogueSet.builder().key("r-q4-brown-block").lines(new String[]{ + "That's a big mushroom that I don't need.", + "It's well known that good stews aren't made from brown mushrooms." + }).build(), + + DialogueSet.builder().key("r-q4-enchanted-brown-block").lines(new String[]{ + "I need red mushrooms." + }).build(), + + DialogueSet.builder().key("r-q4-complete").lines(new String[]{ + "Fantastic!", + "There's some stew for Juliette!", + "Mmh! Tastes great!" + }).build(), + + // Quest Step 5 – Mountain + DialogueSet.builder().key("r-q5-start").lines(new String[]{ + "So cold! I wish I had coffee right now...", + "I want to reach the moon for Juliette.", + "Literally!", + "I wish I could jump like a Rabbit and grab a chunk of it." + }).build(), + + DialogueSet.builder().key("r-q5-wrong-potion").lines(new String[]{ + "A potion might do the trick, but not quite that one." + }).build(), + + DialogueSet.builder().key("r-q5-rabbit-not-6").lines(new String[]{ + "Rabbit potion? Sounds great!", + "Although... could you bring a more potent one?", + "I don't want to take any chances!" + }).build(), + + DialogueSet.builder().key("r-q5-no-coffee").lines(new String[]{ + "Awesome potion!", + "Any chance you could have it taste like coffee?", + "Come back with a caffeinated beverage, thanks!" + }).build(), + + DialogueSet.builder().key("r-q5-complete").lines(new String[]{ + "Thanks!", + "See ya later!", + "Bring the chunk to Juliette!" + }).build(), + + // Quest Step 6 – Gold Mine + DialogueSet.builder().key("r-q6-start").lines(new String[]{ + "Hi friend!", + "I want to make a secret gift for Juliette.", + "I collected tons of gold...", + "Now I just need enough heat to melt it!" + }).build(), + + DialogueSet.builder().key("r-q6-flint").lines(new String[]{ + "That's not even close to hot!" + }).build(), + + DialogueSet.builder().key("r-q6-lava").lines(new String[]{ + "*tastes it*", + "This is the kind of lava I need...", + "Except I need hotter." + }).build(), + + DialogueSet.builder().key("r-q6-magical-lava").lines(new String[]{ + "This is too magical for my taste." + }).build(), + + DialogueSet.builder().key("r-q6-complete").lines(new String[]{ + "Perfect!", + "Thank you so much for helping with all my gift ideas.", + "Can you bring this one to Juliette?" + }).build(), + + // Quest Step 7 – Wilderness + DialogueSet.builder().key("r-q7-start").lines(new String[]{ + "I'm trying to solve this Rubix Prism.", + "Going to need someone wicked smart to solve it." + }).build(), + + DialogueSet.builder().key("r-q7-low-int").lines(new String[]{ + "You're pretty smart.", + "I bet you wish you were smarter, though!" + }).build(), + + DialogueSet.builder().key("r-q7-mid-int").lines(new String[]{ + "While very astute,", + "you don't have quite what it takes for the Prism!" + }).build(), + + DialogueSet.builder().key("r-q7-high-int").lines(new String[]{ + "Can you somehow get even more intelligence?" + }).build(), + + DialogueSet.builder().key("r-q7-almost-genius").lines(new String[]{ + "I can definitely see it, your brain barely fits your head.", + "Just not quite genius enough to solve the Prism!" + }).build(), + + DialogueSet.builder().key("r-q7-threshold").lines(new String[]{ + "If I was to quantify it, I'd say you need 1,291 Intelligence." + }).build(), + + DialogueSet.builder().key("r-q7-complete").lines(new String[]{ + "Incredible!", + "You solved the Prism!", + "Please, bring it to Juliette and show her how smart I am." + }).build(), + + // Quest Step 8 – Colosseum + DialogueSet.builder().key("r-q8-start").lines(new String[]{ + "Juliette keeps talking to me about this other person.", + "I don't know what's going on, they meet over and over!", + "Our love is stronger than theirs!", + "I'm waiting for this other person to show up.", + "I'll need my favorite weapon, can you fetch it for me?" + }).build(), + + DialogueSet.builder().key("r-q8-jerry").lines(new String[]{ + "Go away." + }).build(), + + DialogueSet.builder().key("r-q8-emerald").lines(new String[]{ + "That's not even a weapon!", + "I do like it though!" + }).build(), + + DialogueSet.builder().key("r-q8-weak-sword").lines(new String[]{ + "That sword is weak!", + "Besides, would you really give it to me?" + }).build(), + + DialogueSet.builder().key("r-q8-complete").lines(new String[]{ + "This is exactly what I need!", + "Now I just have to wait for the pretendent.", + "Please bring this poem to Juliette.", + "I hope she'll see the reason." + }).build(), + + // Quest Step 9 – Mushroom Desert + DialogueSet.builder().key("r-q9-start").lines(new String[]{ + "It's you, !", + "I need your help to win Juliette back.", + "My plan is to assemble the greatest bouquet ever!", + "I need a ridiculous amount of flowers." + }).build(), + + DialogueSet.builder().key("r-q9-complete").lines(new String[]{ + "That's exquisite!!", + "With this, I can get enough flowers to win Juliette back!" + }).build(), + + // Quest Step 10 – Jungle Island + DialogueSet.builder().key("r-q10-before-suit").lines(new String[]{ + "! You're the best person to turn the day into something memorable!", + "But first, you should wear something nice!" + }).build(), + + DialogueSet.builder().key("r-q10-after-suit").lines(new String[]{ + "Looking slick!", + "You really look stunning!", + "Thank you so much for reuniting Juliette and I.", + "Please, one last time, give this to Juliette." + }).build(), + DialogueSet.builder().key("idle").lines(new String[]{ + "I love Juliette!" + }).build() + ).toArray(DialogueSet[]::new); + } + +}