From 1f420052b426fac70a4d58a58277a7ab5aa3aebc Mon Sep 17 00:00:00 2001 From: Vicente Date: Wed, 4 Feb 2026 19:28:02 -0300 Subject: [PATCH 01/13] NPCs --- .idea/.gitignore | 10 + .../type/hub/npcs/NPCCoachJackrabbit.java | 57 +++ .../swofty/type/hub/npcs/NPCRabbitBro.java | 112 ++++++ .../swofty/type/hub/npcs/NPCRabbitCousin.java | 97 +++++ .../swofty/type/hub/npcs/NPCRabbitDaddy.java | 124 +++++++ .../swofty/type/hub/npcs/NPCRabbitDog.java | 188 ++++++++++ .../swofty/type/hub/npcs/NPCRabbitGranny.java | 132 +++++++ .../swofty/type/hub/npcs/NPCRabbitSis.java | 154 ++++++++ .../swofty/type/hub/npcs/NPCRabbitUncle.java | 185 ++++++++++ .../datapoints/DatapointChocolateFactory.java | 335 ++++++++++++++++++ 10 files changed, 1394 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..ab1f4164e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java new file mode 100644 index 000000000..b6babbefd --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java @@ -0,0 +1,57 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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; + +public class NPCCoachJackrabbit extends HypixelNPC { + + public NPCCoachJackrabbit() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{"§dCoach Jackrabbit", "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(-7.5, 69, 1.5, -74.5f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return "§dCoach Jackrabbit"; + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + // TODO: Add interaction logic + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return DialogueSet.EMPTY; + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java new file mode 100644 index 000000000..0ea08c0bb --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java @@ -0,0 +1,112 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitBro extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Bro"; + + public NPCRabbitBro() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(8.5, 71, 19.5, 0f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 12 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "You should hire me! I can help you boost your §6Chocolate Factory §fproduction §dtenfold §fby next quarter!" + }).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + "Hire me, bro!" + }).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "Get me a job at your §6Chocolate Factory§f!" + }).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "§aHoppity §fthinks he's all that with his chocolate empire.", + "But ask him this...who can hop the highest?" + }).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "Rise and grind, as I say. The early rabbit gets the cocoa beans." + }).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "§aHoppity §fmay run the factory, but who do you think inspires the work ethic?" + }).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "My morning routine? A quick hop around the fields, a bit of carrot juice, and then straight to work on the next big chocolate innovation." + }).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "Sleep is for the weak!" + }).build(), + DialogueSet.builder() + .key("dialogue-9").lines(new String[]{ + "Working out before dawn has its perks; you get to see §aHoppity§f's 'inspirational' morning pep talks.", + "It's like a shot of espresso, but with more hopping and less coffee." + }).build(), + DialogueSet.builder() + .key("dialogue-10").lines(new String[]{ + "§aHoppity's §fliving the sweet life now, but who's the one who taught him to dodge those garden gnomes? Bro knows best." + }).build(), + DialogueSet.builder() + .key("dialogue-11").lines(new String[]{ + "It's like a shot of espresso, but with more hopping and less coffee." + }).build(), + DialogueSet.builder() + .key("dialogue-12").lines(new String[]{ + "§aHoppity's §fliving the sweet life now, but who's the one who taught him to dodge those garden gnomes? Bro knows best." + }).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java new file mode 100644 index 000000000..9ef872907 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java @@ -0,0 +1,97 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitCousin extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Cousin"; + + public NPCRabbitCousin() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(18.5, 69, 17.5, 71.7f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 8 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "My parents have been hounding me to get a job! Please hire me!" + }).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + "I need a job soon, or my parents will kick me out of the warren." + }).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "I tell ya, family gatherings got a lot more interesting when §aHoppity§f started bringing those experimental chocolates.", + "Remember the carrot crunch debacle?" + }).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "Working hard or hardly working? With chocolate, it's both. I might drift in past noon, but when I'm on, I'm on fire.", + "They say genius often looks like laziness. Guess I'm living proof." + }).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "When I finally get to it, even §aGranny's§f impressed with the flavors I whip up." + }).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "I suggested chocolate-covered carrots to §aHoppity§f once. He laughed until he tried it. Now, who's laughing?", + "Still him, because it was a terrible idea." + }).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "My parents have been hounding me to get a job! Please hire me!" + }).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "I need a job soon, or my parents will kick me out of the warren." + }).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java new file mode 100644 index 000000000..a36e4e00f --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java @@ -0,0 +1,124 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitDaddy extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Daddy"; + + public NPCRabbitDaddy() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(14.5, 69, 23.5, -25.3f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 12 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "In life there are §csharks§f and there are §asheep§f.", + "I'm a §cshark§f.", + "Well, I'm a rabbit. But you get the idea." + }).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + "I am the heir to the §6chocolate throne§f.", + "The only thing in my way? You. Please hire me, boss." + }).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "Heir to the §6chocolate throne§f, they say.", + "More like heir to a bunch of headaches, courtesy of my own daughter's protests." + }).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "Every day, it's a new challenge. If it's not the market, it's Sis with her picket signs.", + "Still, we're making the world sweeter, one chocolate bar at a time." + }).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "Every family has its ups and downs, but ours?", + "We've got a whole soap opera thanks to Sis." + }).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "Between Bro's grindset, Cousin's relaxed approach, and Granny's wisdom, we've got all the ingredients for success.", + "Now, if only Sis would see that." + }).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "Bro's dedication is what this company needs more of. And Cousin, well, he brings...§dcreativity§f.", + "Granny? She's the glue holding us all together." + }).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "§aHoppity §fwants us to 'think outside the box.' Last time I did that, we ended up with chocolate-covered grass.", + "Sold out in a week. Shows what I know." + }).build(), + DialogueSet.builder() + .key("dialogue-9").lines(new String[]{ + "In life there are §csharks§f and there are §asheep§f.", + "I'm a §cshark§f.", + "Well, I'm a rabbit. But you get the idea." + }).build(), + DialogueSet.builder() + .key("dialogue-10").lines(new String[]{ + "I am the heir to the §6chocolate throne§f.", + "The only thing in my way? You. Please hire me, boss." + }).build(), + DialogueSet.builder() + .key("dialogue-11").lines(new String[]{ + "Heir to the §6chocolate throne§f, they say.", + "More like heir to a bunch of headaches, courtesy of my own daughter's protests." + }).build(), + DialogueSet.builder() + .key("dialogue-12").lines(new String[]{ + "Every day, it's a new challenge. If it's not the market, it's Sis with her picket signs.", + "Still, we're making the world sweeter, one chocolate bar at a time." + }).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java new file mode 100644 index 000000000..6a9f20906 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java @@ -0,0 +1,188 @@ +package net.swofty.type.hub.npcs; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitDog extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Dog"; + + public NPCRabbitDog() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(15.5, 69.25, 13.5, 16.9f, -21.1f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 26 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + Sound barkSound = Sound.sound(Key.key("entity.wolf.ambient"), Sound.Source.NEUTRAL, 1.0f, 1.0f); + + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "In the pursuit of excellence, one must remember that the journey is as significant as the destination.", + "True success lies not in the accolades, but in the lessons learned along the way." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + "Consider the possibility that limitations are merely figments of our imagination, crafted by our fears and insecurities.", + "To break free, one must first believe in the boundlessness of their own potential." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "Empathy is the silent language of the soul, spoken through actions rather than words.", + "To truly understand another, one must listen not only with ears but with the heart." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "The greatest paradox of life is that in seeking control, we often surrender it.", + "True power comes not from dominion over others, but from mastery over oneself." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "Wisdom often comes to us in whispers, through the leaves of the trees or the ripple of the waters.", + "It teaches us that every voice, no matter how small, has something of value to say." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "In every moment of decision, we stand at the crossroads of countless possibilities.", + "The paths we choose not only define our destiny but also reflect the essence of our being." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "Solitude is not the absence of company, but the moment when our soul is free to speak to us, unencumbered by the chaos of the world." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "Life, like a vast symphony, plays on the strings of our experiences, emotions, and encounters.", + "Each note, while fleeting, contributes to the eternal melody of our existence." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-9").lines(new String[]{ + "Ah, Rabbit Bro, tirelessly toiling from the break of dawn, clinging to his routines with the desperation of a shipwreck survivor to a life raft.", + "One wonders if he runs from failure or merely jogs alongside it." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-10").lines(new String[]{ + "And there strides Rabbit Sis, torchbearer of tumult, whose fervent protests might one day change the world, if they don't first incite her to single-handedly dismantle it, piece by bureaucratic piece." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-11").lines(new String[]{ + "Ever observant Rabbit Daddy, guardian of the gold, manages finances with a paranoia that would make a conspiracy theorist blush.", + "His ledger is tighter than a drum, his brow perpetually furrowed in fiscal fear." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-12").lines(new String[]{ + "Granny, with anecdotes as endless as eternity, dispenses wisdom like a vending machine stuck on dispense, whether you requested it or not.", + "Each story a subtle reminder that history repeats itself, especially at family gatherings." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-13").lines(new String[]{ + "Observe the tranquil Rabbit Cuz, philosopher of sloth, whose profound punctuality issues suggest a man deeply at peace with life's ephemeral nature, or perhaps just deeply asleep." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-14").lines(new String[]{ + "Bark!" + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-15").lines(new String[]{ + "*pants*" + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-16").lines(new String[]{ + "Woof!" + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-17").lines(new String[]{ + "In the pursuit of excellence, one must remember that the journey is as significant as the destination.", + "True success lies not in the accolades, but in the lessons learned along the way." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-18").lines(new String[]{ + "Consider the possibility that limitations are merely figments of our imagination, crafted by our fears and insecurities.", + "To break free, one must first believe in the boundlessness of their own potential." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-19").lines(new String[]{ + "Empathy is the silent language of the soul, spoken through actions rather than words.", + "To truly understand another, one must listen not only with ears but with the heart." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-20").lines(new String[]{ + "The greatest paradox of life is that in seeking control, we often surrender it.", + "True power comes not from dominion over others, but from mastery over oneself." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-21").lines(new String[]{ + "Wisdom often comes to us in whispers, through the leaves of the trees or the ripple of the waters.", + "It teaches us that every voice, no matter how small, has something of value to say." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-22").lines(new String[]{ + "In every moment of decision, we stand at the crossroads of countless possibilities.", + "The paths we choose not only define our destiny but also reflect the essence of our being." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-23").lines(new String[]{ + "Solitude is not the absence of company, but the moment when our soul is free to speak to us, unencumbered by the chaos of the world." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-24").lines(new String[]{ + "Life, like a vast symphony, plays on the strings of our experiences, emotions, and encounters.", + "Each note, while fleeting, contributes to the eternal melody of our existence." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-25").lines(new String[]{ + "Ah, Rabbit Bro, tirelessly toiling from the break of dawn, clinging to his routines with the desperation of a shipwreck survivor to a life raft.", + "One wonders if he runs from failure or merely jogs alongside it." + }).sound(barkSound).build(), + DialogueSet.builder() + .key("dialogue-26").lines(new String[]{ + "And there strides Rabbit Sis, torchbearer of tumult, whose fervent protests might one day change the world, if they don't first incite her to single-handedly dismantle it, piece by bureaucratic piece." + }).sound(barkSound).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java new file mode 100644 index 000000000..219e644f3 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java @@ -0,0 +1,132 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitGranny extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Granny"; + + public NPCRabbitGranny() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(9.5, 69, 23.5, -85.8f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 14 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "I'm looking to come out of retirement for §done last job§f.", + "Plus, I decided I don't think I could ever give up chocolate." + }).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + "The §6Chocolate Factory §fused to be different before §aHoppity§f arrived.", + "You never know that you're in the glory days until it's too late." + }).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "In my day, we made sweets from whatever was in the garden. §aHoppity§f just added cocoa.", + "Kids these days think they've invented sugar." + }).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "Every time §aHoppity§f talks about 'expanding the business', I remind him: 'Don't forget to expand your heart too.'", + "He's a good boy, he listens." + }).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "§aHoppity§f wants to automate the chocolate wrapping. I told him, 'Nothing beats the personal touch.'", + "He gave me a computerized knitting machine." + }).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "I've watched this family business grow from a single carrot patch to rows of cocoa trees.", + "Each bar we produce carries that legacy. Makes an old bunny proud." + }).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "They say you can't choose your family. But if I could, I'd choose this chocolate-crazed bunch every time.", + "From Dust Bowl to chocolate empire, we've come a long way." + }).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "In my days, we settled disputes over a hot cup of cocoa.", + "Maybe that's what Sis and my son need - a reminder of what binds us." + }).build(), + DialogueSet.builder() + .key("dialogue-9").lines(new String[]{ + "I'm looking to come out of retirement for §done last job§f.", + "Plus, I decided I don't think I could ever give up chocolate." + }).build(), + DialogueSet.builder() + .key("dialogue-10").lines(new String[]{ + "The §6Chocolate Factory §fused to be different before §aHoppity§f arrived.", + "You never know that you're in the glory days until it's too late." + }).build(), + DialogueSet.builder() + .key("dialogue-11").lines(new String[]{ + "In my day, we made sweets from whatever was in the garden. §aHoppity§f just added cocoa.", + "Kids these days think they've invented sugar." + }).build(), + DialogueSet.builder() + .key("dialogue-12").lines(new String[]{ + "Every time §aHoppity§f talks about 'expanding the business', I remind him: 'Don't forget to expand your heart too.'", + "He's a good boy, he listens." + }).build(), + DialogueSet.builder() + .key("dialogue-13").lines(new String[]{ + "§aHoppity§f wants to automate the chocolate wrapping. I told him, 'Nothing beats the personal touch.'", + "He gave me a computerized knitting machine." + }).build(), + DialogueSet.builder() + .key("dialogue-14").lines(new String[]{ + "I've watched this family business grow from a single carrot patch to rows of cocoa trees.", + "Each bar we produce carries that legacy. Makes an old bunny proud." + }).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java new file mode 100644 index 000000000..13c17ca7d --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java @@ -0,0 +1,154 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitSis extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Sis"; + + public NPCRabbitSis() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(18.5, 69, 24.5, 127f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 19 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "Hire me, " + player.getUsername() + "!", + "Together we can abolish the patriarchy, and ensure clean chocolate for all rabbits!" + }).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + "Down with Big Chocolate!" + }).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "Regulate! Regulate! Regulate!" + }).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "Chocolate is love, chocolate is life. But at what cost?", + "It's time this family faces the music and listens to the cocoa beans' side of the story!" + }).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "They call me a troublemaker, a rebel. I say, I'm the only one talking sense!", + "Wake up and smell the exploitation, family!" + }).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "A protest a day keeps the unethical practices away.", + "Dad might not see it now, but I'm doing this for the future of all chocolate bunnies." + }).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "They say I'm disrupting the peace, but I'm just trying to sprinkle a little truth on our chocolate-covered lies.", + "The factory needs a new recipe...for justice." + }).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "Bro's always talking about his 'sigma grindset'. Tried to get me to read a book on it.", + "I told him I'd start my own movement: the 'chocolate mindfulness mindset'." + }).build(), + DialogueSet.builder() + .key("dialogue-9").lines(new String[]{ + "Cousin might be the laziest bunny I know, but he's onto something.", + "Why protest when you can just 'be the change'? By napping, apparently." + }).build(), + DialogueSet.builder() + .key("dialogue-10").lines(new String[]{ + "I told §aHoppity§f we should have a line of eco-friendly chocolates.", + "He asked if green food coloring counted.", + "We're...working on it." + }).build(), + DialogueSet.builder() + .key("dialogue-11").lines(new String[]{ + "§aHoppity§f actually listens to my protests.", + "Well, more like he can't avoid them since I do it in the lobby. But it's a start, right?" + }).build(), + DialogueSet.builder() + .key("dialogue-12").lines(new String[]{ + "Hire me, " + player.getUsername() + "!", + "Together we can abolish the patriarchy, and ensure clean chocolate for all rabbits!" + }).build(), + DialogueSet.builder() + .key("dialogue-13").lines(new String[]{ + "Down with Big Chocolate!" + }).build(), + DialogueSet.builder() + .key("dialogue-14").lines(new String[]{ + "Regulate! Regulate! Regulate!" + }).build(), + DialogueSet.builder() + .key("dialogue-15").lines(new String[]{ + "Chocolate is love, chocolate is life. But at what cost?", + "It's time this family faces the music and listens to the cocoa beans' side of the story!" + }).build(), + DialogueSet.builder() + .key("dialogue-16").lines(new String[]{ + "They call me a troublemaker, a rebel. I say, I'm the only one talking sense!", + "Wake up and smell the exploitation, family!" + }).build(), + DialogueSet.builder() + .key("dialogue-17").lines(new String[]{ + "A protest a day keeps the unethical practices away.", + "Dad might not see it now, but I'm doing this for the future of all chocolate bunnies." + }).build(), + DialogueSet.builder() + .key("dialogue-18").lines(new String[]{ + "They say I'm disrupting the peace, but I'm just trying to sprinkle a little truth on our chocolate-covered lies.", + "The factory needs a new recipe...for justice." + }).build(), + DialogueSet.builder() + .key("dialogue-19").lines(new String[]{ + "Bro's always talking about his 'sigma grindset'. Tried to get me to read a book on it.", + "I told him I'd start my own movement: the 'chocolate mindfulness mindset'." + }).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java new file mode 100644 index 000000000..9826e56a0 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java @@ -0,0 +1,185 @@ +package net.swofty.type.hub.npcs; + +import net.minestom.server.coordinate.Pos; +import net.swofty.type.generic.user.HypixelPlayer; +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 java.util.stream.Stream; + +public class NPCRabbitUncle extends HypixelNPC { + private static final ChocolateFactoryRank RANK = ChocolateFactoryRank.UNEMPLOYED; + private static final String NPC_NAME = "Rabbit Uncle"; + + public NPCRabbitUncle() { + super(new HumanConfiguration() { + @Override + public String[] holograms(HypixelPlayer player) { + return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; + } + + @Override + public String signature(HypixelPlayer player) { + // TODO: Add skin signature + return ""; + } + + @Override + public String texture(HypixelPlayer player) { + // TODO: Add skin texture + return ""; + } + + @Override + public Pos position(HypixelPlayer player) { + return new Pos(-18.5, 71, 16.5, -105.5f, 0f); + } + + @Override + public boolean looking(HypixelPlayer player) { + return true; + } + + @Override + public String chatName() { + return RANK.getChatName(NPC_NAME); + } + }); + } + + @Override + public void onClick(NPCInteractEvent e) { + if (isInDialogue(e.player())) return; + setDialogue(e.player(), "dialogue-" + (int) (Math.random() * 25 + 1)); + } + + @Override + public DialogueSet[] dialogues(HypixelPlayer player) { + return Stream.of( + DialogueSet.builder() + .key("dialogue-1").lines(new String[]{ + "Grandma always said I could be somebody if I put my mind to it.", + "She was wrong." + }).build(), + DialogueSet.builder() + .key("dialogue-2").lines(new String[]{ + player.getUsername() + "...you have to hire me. Please." + }).build(), + DialogueSet.builder() + .key("dialogue-3").lines(new String[]{ + "I should be in the Hall of Fame by now.", + "Instead, I am here. Please, hire me." + }).build(), + DialogueSet.builder() + .key("dialogue-4").lines(new String[]{ + "Back in high school, I was just one play away from that state championship.", + "If only I had zigged instead of zagged..." + }).build(), + DialogueSet.builder() + .key("dialogue-5").lines(new String[]{ + "I used to chuck that ball like nobody's business. Those were the days, eh?", + "Still got some of those moves, you know." + }).build(), + DialogueSet.builder() + .key("dialogue-6").lines(new String[]{ + "Every time I see a football, I can't help but wonder 'What if?'", + "Was so close to grabbing that championship ring." + }).build(), + DialogueSet.builder() + .key("dialogue-7").lines(new String[]{ + "Sometimes, late at night, I replay that final drive in my head.", + "I could have been a legend, you know?" + }).build(), + DialogueSet.builder() + .key("dialogue-8").lines(new String[]{ + "Man." + }).build(), + DialogueSet.builder() + .key("dialogue-9").lines(new String[]{ + "I've got the entire high school trophy case memorized.", + "Sometimes, I give tours. You know, just to keep the legacy going." + }).build(), + DialogueSet.builder() + .key("dialogue-10").lines(new String[]{ + "Cuz might not have the typical athlete's discipline, but he's clutch.", + "Just like my old teammate who didn't show up to every practice, but could score tuddies when it mattered most." + }).build(), + DialogueSet.builder() + .key("dialogue-11").lines(new String[]{ + "§dHoppity§f's like the star quarterback of this chocolate game. Always looking for that next big play.", + "He's got that championship mindset, just like I had." + }).build(), + DialogueSet.builder() + .key("dialogue-12").lines(new String[]{ + "Sis has that fire, like the team captains back in my day.", + "She doesn't just play - she changes the game.", + "Always rallying the troops for her cause, much like a good quarterback does in the fourth quarter." + }).build(), + DialogueSet.builder() + .key("dialogue-13").lines(new String[]{ + "Sure, Rabbit Bro's up at dawn doing push-ups and planning his day.", + "I used to be like him, then I tore my ACL.", + "My knee has never been the same since, man." + }).build(), + DialogueSet.builder() + .key("dialogue-14").lines(new String[]{ + "Cuz showing up to work is like a trick play.", + "You never see it coming, and when it happens you can't help but wonder if it was a fluke." + }).build(), + DialogueSet.builder() + .key("dialogue-15").lines(new String[]{ + "Grandma always said I could be somebody if I put my mind to it.", + "She was wrong." + }).build(), + DialogueSet.builder() + .key("dialogue-16").lines(new String[]{ + player.getUsername() + "...you have to hire me. Please." + }).build(), + DialogueSet.builder() + .key("dialogue-17").lines(new String[]{ + "I should be in the Hall of Fame by now.", + "Instead, I am here. Please, hire me." + }).build(), + DialogueSet.builder() + .key("dialogue-18").lines(new String[]{ + "Back in high school, I was just one play away from that state championship.", + "If only I had zigged instead of zagged..." + }).build(), + DialogueSet.builder() + .key("dialogue-19").lines(new String[]{ + "I used to chuck that ball like nobody's business. Those were the days, eh?", + "Still got some of those moves, you know." + }).build(), + DialogueSet.builder() + .key("dialogue-20").lines(new String[]{ + "Every time I see a football, I can't help but wonder 'What if?'", + "Was so close to grabbing that championship ring." + }).build(), + DialogueSet.builder() + .key("dialogue-21").lines(new String[]{ + "Sometimes, late at night, I replay that final drive in my head.", + "I could have been a legend, you know?" + }).build(), + DialogueSet.builder() + .key("dialogue-22").lines(new String[]{ + "Man." + }).build(), + DialogueSet.builder() + .key("dialogue-23").lines(new String[]{ + "I've got the entire high school trophy case memorized.", + "Sometimes, I give tours. You know, just to keep the legacy going." + }).build(), + DialogueSet.builder() + .key("dialogue-24").lines(new String[]{ + "Cuz might not have the typical athlete's discipline, but he's clutch.", + "Just like my old teammate who didn't show up to every practice, but could score tuddies when it mattered most." + }).build(), + DialogueSet.builder() + .key("dialogue-25").lines(new String[]{ + "§dHoppity§f's like the star quarterback of this chocolate game. Always looking for that next big play.", + "He's got that championship mindset, just like I had." + }).build() + ).toArray(DialogueSet[]::new); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java new file mode 100644 index 000000000..c75da991d --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java @@ -0,0 +1,335 @@ +package net.swofty.type.skyblockgeneric.data.datapoints; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import net.swofty.commons.protocol.Serializer; +import net.swofty.type.skyblockgeneric.data.SkyBlockDatapoint; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +public class DatapointChocolateFactory extends SkyBlockDatapoint { + private static final Serializer serializer = new Serializer<>() { + @Override + public String serialize(ChocolateFactoryData value) { + JSONObject json = new JSONObject(); + + json.put("chocolate", value.chocolate); + json.put("chocolateAllTime", value.chocolateAllTime); + json.put("lastUpdated", value.lastUpdated); + + // Upgrades + json.put("timeTowerLevel", value.timeTowerLevel); + json.put("timeTowerCharges", value.timeTowerCharges); + json.put("timeTowerLastUsed", value.timeTowerLastUsed); + json.put("timeTowerActiveUntil", value.timeTowerActiveUntil); + json.put("rabbitBarnLevel", value.rabbitBarnLevel); + json.put("handBakedChocolateLevel", value.handBakedChocolateLevel); + json.put("rabbitShrineLevel", value.rabbitShrineLevel); + json.put("coachJackrabbitLevel", value.coachJackrabbitLevel); + + // Employees - store as JSON object + JSONObject employeesJson = new JSONObject(); + for (Map.Entry entry : value.employees.entrySet()) { + JSONObject employeeJson = new JSONObject(); + employeeJson.put("level", entry.getValue().level); + employeeJson.put("baseProduction", entry.getValue().baseProduction); + employeesJson.put(entry.getKey(), employeeJson); + } + json.put("employees", employeesJson); + + // Production stats + json.put("totalClicks", value.totalClicks); + json.put("totalTimeTowerUsages", value.totalTimeTowerUsages); + + return json.toString(); + } + + @Override + public ChocolateFactoryData deserialize(String json) { + JSONObject jsonObject = new JSONObject(json); + + Map employees = new HashMap<>(); + if (jsonObject.has("employees")) { + JSONObject employeesJson = jsonObject.getJSONObject("employees"); + for (String key : employeesJson.keySet()) { + JSONObject employeeJson = employeesJson.getJSONObject(key); + employees.put(key, new EmployeeData( + employeeJson.getInt("level"), + employeeJson.getDouble("baseProduction") + )); + } + } + + return new ChocolateFactoryData( + jsonObject.optLong("chocolate", 0L), + jsonObject.optLong("chocolateAllTime", 0L), + jsonObject.optLong("lastUpdated", System.currentTimeMillis()), + jsonObject.optInt("timeTowerLevel", 0), + jsonObject.optInt("timeTowerCharges", 0), + jsonObject.optLong("timeTowerLastUsed", 0L), + jsonObject.optLong("timeTowerActiveUntil", 0L), + jsonObject.optInt("rabbitBarnLevel", 0), + jsonObject.optInt("handBakedChocolateLevel", 0), + jsonObject.optInt("rabbitShrineLevel", 0), + jsonObject.optInt("coachJackrabbitLevel", 0), + employees, + jsonObject.optLong("totalClicks", 0L), + jsonObject.optInt("totalTimeTowerUsages", 0) + ); + } + + @Override + public ChocolateFactoryData clone(ChocolateFactoryData value) { + Map clonedEmployees = new HashMap<>(); + for (Map.Entry entry : value.employees.entrySet()) { + clonedEmployees.put(entry.getKey(), new EmployeeData( + entry.getValue().level, + entry.getValue().baseProduction + )); + } + + return new ChocolateFactoryData( + value.chocolate, + value.chocolateAllTime, + value.lastUpdated, + value.timeTowerLevel, + value.timeTowerCharges, + value.timeTowerLastUsed, + value.timeTowerActiveUntil, + value.rabbitBarnLevel, + value.handBakedChocolateLevel, + value.rabbitShrineLevel, + value.coachJackrabbitLevel, + clonedEmployees, + value.totalClicks, + value.totalTimeTowerUsages + ); + } + }; + + public DatapointChocolateFactory(String key, ChocolateFactoryData value, Serializer serializer) { + super(key, value, serializer); + } + + public DatapointChocolateFactory(String key, ChocolateFactoryData value) { + super(key, value, serializer); + } + + public DatapointChocolateFactory(String key) { + super(key, new ChocolateFactoryData(), serializer); + } + + @AllArgsConstructor + @Getter + @Setter + public static class ChocolateFactoryData { + private long chocolate; + private long chocolateAllTime; + private long lastUpdated; + + // Upgrades + private int timeTowerLevel; + private int timeTowerCharges; + private long timeTowerLastUsed; + private long timeTowerActiveUntil; + private int rabbitBarnLevel; + private int handBakedChocolateLevel; + private int rabbitShrineLevel; + private int coachJackrabbitLevel; + + // Employees (rabbit name -> employee data) + private Map employees; + + // Statistics + private long totalClicks; + private int totalTimeTowerUsages; + + public ChocolateFactoryData() { + this.chocolate = 0L; + this.chocolateAllTime = 0L; + this.lastUpdated = System.currentTimeMillis(); + this.timeTowerLevel = 0; + this.timeTowerCharges = 0; + this.timeTowerLastUsed = 0L; + this.timeTowerActiveUntil = 0L; + this.rabbitBarnLevel = 0; + this.handBakedChocolateLevel = 0; + this.rabbitShrineLevel = 0; + this.coachJackrabbitLevel = 0; + this.employees = new HashMap<>(); + this.totalClicks = 0L; + this.totalTimeTowerUsages = 0; + } + + /** + * Adds chocolate and updates all-time total + */ + public void addChocolate(long amount) { + this.chocolate += amount; + if (amount > 0) { + this.chocolateAllTime += amount; + } + } + + /** + * Removes chocolate (for purchases) + */ + public boolean removeChocolate(long amount) { + if (this.chocolate >= amount) { + this.chocolate -= amount; + return true; + } + return false; + } + + /** + * Gets the click power (chocolate per click) + * Base is 1, increases with Hand-Baked Chocolate upgrade + */ + public int getClickPower() { + return 1 + handBakedChocolateLevel; + } + + /** + * Gets the maximum number of rabbit slots based on Rabbit Barn level + */ + public int getMaxRabbitSlots() { + return 3 + rabbitBarnLevel; + } + + /** + * Gets the production multiplier from Rabbit Shrine + * Base is 1.0, increases by 0.1 per level + */ + public double getShrineMultiplier() { + return 1.0 + (rabbitShrineLevel * 0.1); + } + + /** + * Gets the Time Tower multiplier when active + * Base is 1.0, increases by 0.1 per level + */ + public double getTimeTowerMultiplier() { + if (System.currentTimeMillis() < timeTowerActiveUntil) { + return 1.0 + (timeTowerLevel * 0.1); + } + return 1.0; + } + + /** + * Checks if Time Tower is currently active + */ + public boolean isTimeTowerActive() { + return System.currentTimeMillis() < timeTowerActiveUntil; + } + + /** + * Activates the Time Tower if charges are available + */ + public boolean activateTimeTower() { + if (timeTowerCharges > 0 && !isTimeTowerActive()) { + timeTowerCharges--; + timeTowerLastUsed = System.currentTimeMillis(); + timeTowerActiveUntil = System.currentTimeMillis() + (60 * 60 * 1000); // 1 hour + totalTimeTowerUsages++; + return true; + } + return false; + } + + /** + * Gets the Coach Jackrabbit multiplier + * Base is 1.0, increases by 0.01 per level + */ + public double getCoachMultiplier() { + return 1.0 + (coachJackrabbitLevel * 0.01); + } + + /** + * Calculates total chocolate production per second from all sources + */ + public double getChocolatePerSecond() { + double baseProduction = 0; + + // Sum production from all employees + for (EmployeeData employee : employees.values()) { + baseProduction += employee.getProductionPerSecond(); + } + + // Apply multipliers + baseProduction *= getShrineMultiplier(); + baseProduction *= getTimeTowerMultiplier(); + baseProduction *= getCoachMultiplier(); + + return baseProduction; + } + + /** + * Updates chocolate based on time passed since last update + */ + public void updateChocolateFromProduction() { + long now = System.currentTimeMillis(); + double secondsPassed = (now - lastUpdated) / 1000.0; + + if (secondsPassed > 0) { + long produced = (long) (getChocolatePerSecond() * secondsPassed); + addChocolate(produced); + lastUpdated = now; + } + } + + /** + * Registers a click and adds chocolate + */ + public void click() { + addChocolate(getClickPower()); + totalClicks++; + } + + /** + * Hires or upgrades an employee + */ + public void setEmployee(String name, int level, double baseProduction) { + employees.put(name, new EmployeeData(level, baseProduction)); + } + + /** + * Gets employee count + */ + public int getEmployeeCount() { + return employees.size(); + } + + /** + * Gets prestige level based on all-time chocolate + */ + public int getPrestigeLevel() { + if (chocolateAllTime >= 1_000_000_000_000L) return 6; // 1T + if (chocolateAllTime >= 10_000_000_000L) return 5; // 10B + if (chocolateAllTime >= 1_000_000_000L) return 4; // 1B + if (chocolateAllTime >= 100_000_000L) return 3; // 100M + if (chocolateAllTime >= 10_000_000L) return 2; // 10M + if (chocolateAllTime >= 1_000_000L) return 1; // 1M + return 0; + } + } + + @AllArgsConstructor + @Getter + @Setter + public static class EmployeeData { + private int level; + private double baseProduction; + + /** + * Gets production per second for this employee + * Production scales with level + */ + public double getProductionPerSecond() { + return baseProduction * (1 + (level - 1) * 0.5); + } + } +} From f37e8fe74f6c3ca3c987ab980c07461454d94058 Mon Sep 17 00:00:00 2001 From: Vicente Date: Wed, 4 Feb 2026 21:23:12 -0300 Subject: [PATCH 02/13] Factory Menu --- build.gradle.kts | 4 + service.darkauction/build.gradle.kts | 2 +- service.orchestrator/build.gradle.kts | 2 +- .../command/commands/AdminMeCommand.java | 3 +- .../type/generic/entity/npc/HypixelNPC.java | 16 +- .../type/hub/npcs/ChocolateFactoryRank.java | 68 ++ .../type/hub/npcs/NPCCoachJackrabbit.java | 8 +- .../swofty/type/hub/npcs/NPCRabbitBro.java | 21 +- .../swofty/type/hub/npcs/NPCRabbitCousin.java | 24 +- .../swofty/type/hub/npcs/NPCRabbitDaddy.java | 24 +- .../swofty/type/hub/npcs/NPCRabbitDog.java | 24 +- .../swofty/type/hub/npcs/NPCRabbitGranny.java | 24 +- .../swofty/type/hub/npcs/NPCRabbitSis.java | 24 +- .../swofty/type/hub/npcs/NPCRabbitUncle.java | 24 +- .../SkyBlockGenericLoader.java | 2 + .../ChocolateFactoryHelper.java | 354 ++++++++ .../ChocolateFactoryProductionLoop.java | 30 + .../commands/ChocolateFactoryCommand.java | 24 + .../data/SkyBlockDataHandler.java | 3 + .../datapoints/DatapointChocolateFactory.java | 21 +- .../gui/inventories/GUIChocolateFactory.java | 789 ++++++++++++++++++ 21 files changed, 1457 insertions(+), 34 deletions(-) create mode 100644 type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java diff --git a/build.gradle.kts b/build.gradle.kts index b202e2921..3ee2091f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,10 @@ plugins { group = "net.swofty" version = "1.0" +repositories { + mavenCentral() +} + subprojects { apply(plugin = "java") apply(plugin = "java-library") diff --git a/service.darkauction/build.gradle.kts b/service.darkauction/build.gradle.kts index fe68e8dec..2345b4d5e 100644 --- a/service.darkauction/build.gradle.kts +++ b/service.darkauction/build.gradle.kts @@ -3,7 +3,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { java application - id("io.github.goooler.shadow") version "8.1.7" + id("com.gradleup.shadow") version "9.3.1" } group = "net.swofty" diff --git a/service.orchestrator/build.gradle.kts b/service.orchestrator/build.gradle.kts index f6c404e17..00b3e220c 100644 --- a/service.orchestrator/build.gradle.kts +++ b/service.orchestrator/build.gradle.kts @@ -3,7 +3,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { java application - id("io.github.goooler.shadow") version "8.1.7" + id("com.gradleup.shadow") version "9.3.1" } group = "net.swofty" diff --git a/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java b/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java index 618110742..d3974d8fe 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java +++ b/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java @@ -25,7 +25,8 @@ public class AdminMeCommand extends HypixelCommand { "Swofty", "Foodzz", "Hamza_dev", - "ItzKatze" + "ItzKatze", + "Vicente_1313" ); @Override 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 df3fda2b4..9b5d18592 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 @@ -85,9 +85,22 @@ public static void updateForPlayer(HypixelPlayer player) { HypixelNPC.getRegisteredNPCs().forEach((npc) -> { // If the main username can't be used (over 16 chars), use a blank space instead and use holograms for all lines boolean playerHasNPC = cache.getEntityImpls().containsKey(npc); + NPCConfiguration config = npc.getParameters(); + + // Check visibility - if not visible, skip creation or remove existing + if (!config.visible(player)) { + if (playerHasNPC) { + // Remove NPC that is no longer visible + Entity entity = cache.get(npc).getValue(); + PlayerHolograms.ExternalPlayerHologram holo = cache.get(npc).getKey(); + entity.remove(); + PlayerHolograms.removeExternalPlayerHologram(holo); + cache.remove(npc); + } + return; + } if (!playerHasNPC) { - NPCConfiguration config = npc.getParameters(); String[] holograms = config.holograms(player); Pos position = config.position(player); @@ -141,7 +154,6 @@ public static void updateForPlayer(HypixelPlayer player) { Entity entity = cache.get(npc).getValue(); PlayerHolograms.ExternalPlayerHologram holo = cache.get(npc).getKey(); - NPCConfiguration config = npc.getParameters(); Pos npcPosition = config.position(player); String[] npcHolograms = config.holograms(player); diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java new file mode 100644 index 000000000..7b2c97244 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java @@ -0,0 +1,68 @@ +package net.swofty.type.hub.npcs; + +public enum ChocolateFactoryRank { + UNEMPLOYED(0, "Unemployed", "§c"), + INTERN(1, "Intern", "§7"), + EMPLOYEE(20, "Employee", "§a"), + ASSISTANT(120, "Assistant", "§9"), + MANAGER(140, "Manager", "§5"), + DIRECTOR(180, "Director", "§6"), + EXECUTIVE(200, "Executive", "§d"), + BOARD_MEMBER(220, "Board Member", "§b"); + + private final int level; + private final String name; + private final String color; + + ChocolateFactoryRank(int level, String name, String color) { + this.level = level; + this.name = name; + this.color = color; + } + + public int getLevel() { + return level; + } + + public String getName() { + return name; + } + + public String getColor() { + return color; + } + + /** + * Gets the formatted hologram line for this rank. + * Format: §7[Lvl X] §{color}RankName + * For UNEMPLOYED: §cUnemployed (no level) + */ + public String getHologramLine() { + if (this == UNEMPLOYED) { + return color + name; + } + return "§7[Lvl " + level + "] " + color + name; + } + + /** + * Gets the formatted chat name for this rank. + * Format: §{color}RankName + */ + public String getChatName(String npcName) { + return color + npcName; + } + + /** + * Gets the rank for a given level. + * Returns the highest rank the level qualifies for. + */ + public static ChocolateFactoryRank fromLevel(int level) { + ChocolateFactoryRank result = UNEMPLOYED; + for (ChocolateFactoryRank rank : values()) { + if (level >= rank.level) { + result = rank; + } + } + return result; + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java index b6babbefd..5eabebc76 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java @@ -5,6 +5,8 @@ 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.skyblockgeneric.gui.inventories.GUIChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; public class NPCCoachJackrabbit extends HypixelNPC { @@ -17,14 +19,12 @@ public String[] holograms(HypixelPlayer player) { @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxMzAyMjkyOTYwNCwKICAicHJvZmlsZUlkIiA6ICI2NGY0MGFiNzFmM2E0NGZiYjg0N2I5ZWFhOWZjNDRlNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJvZGF2aWRjZXNhciIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9iYzBjYzY3ZTc5YzIyOGU1NDFlNjhhZWIxZDgxZWQ3YWY1MTE2NjYyMmFkNGRiOTQxN2Q3YTI5ZDFiODlhZjk1IgogICAgfQogIH0KfQ=="; } @Override @@ -47,7 +47,7 @@ public String chatName() { @Override public void onClick(NPCInteractEvent e) { if (isInDialogue(e.player())) return; - // TODO: Add interaction logic + new GUIChocolateFactory().open((SkyBlockPlayer) e.player()); } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java index 0ea08c0bb..016d90ad6 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java @@ -5,6 +5,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -14,21 +17,33 @@ public class NPCRabbitBro extends HypixelNPC { public NPCRabbitBro() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + // Rabbit Bro is always visible (unlocked by default) + return true; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxMjU5NDI0NjM2MywKICAicHJvZmlsZUlkIiA6ICJjZjc4YzFkZjE3ZTI0Y2Q5YTIxYmU4NWQ0NDk5ZWE4ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNYXR0c0FybW9yU3RhbmRzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzI4NzkzNGJkZDlkZjI3MDViMjUxYmI5OTdlMDI5YjE4YzFlOTRkZjEyOTkyYjgxMDdlNzQ0OTdiMjA1Y2E3ZTgiCiAgICB9CiAgfQp9"; } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java index 9ef872907..7d96f632e 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java @@ -5,6 +5,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -14,21 +17,36 @@ public class NPCRabbitCousin extends HypixelNPC { public NPCRabbitCousin() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + return data.getEmployees().containsKey(NPC_NAME); + } + return false; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxMjU5NDI2ODkxNCwKICAicHJvZmlsZUlkIiA6ICJlMjc5NjliODYyNWY0NDg1YjkyNmM5NTBhMDljMWMwMSIsCiAgInByb2ZpbGVOYW1lIiA6ICJLRVZJTktFTE9LRSIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9hOTgyODI1YzAxYjY1OGYzNDhhMDk5YjQ1NzkwMjlhMTgwZDJlNDE1MTgzOTUxYjJlNmU1ZTI3MjU3ZGY0MjU0IgogICAgfQogIH0KfQ=="; } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java index a36e4e00f..c5e1dd710 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java @@ -5,6 +5,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -14,21 +17,36 @@ public class NPCRabbitDaddy extends HypixelNPC { public NPCRabbitDaddy() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + return data.getEmployees().containsKey(NPC_NAME); + } + return false; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxMjg0NzA0NzAwNSwKICAicHJvZmlsZUlkIiA6ICIzOThiZGM3NWVhYzQ0ZjMzYWEyMDBiMTYyNTRmMDhlOSIsCiAgInByb2ZpbGVOYW1lIiA6ICJJa2h3YW4wNTEwIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzU3Y2FiMGMzNGQ3ZGRjZjcyZGI1NmZmMzZmMjg4M2Y1NTRjZmY3NmViNWQzYjNlMDU2MjMzODAzNmM5NzYwNDMiCiAgICB9CiAgfQp9"; } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java index 6a9f20906..4630c1c50 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java @@ -7,6 +7,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -16,21 +19,36 @@ public class NPCRabbitDog extends HypixelNPC { public NPCRabbitDog() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + return data.getEmployees().containsKey(NPC_NAME); + } + return false; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxNDk1OTAyNzAyNCwKICAicHJvZmlsZUlkIiA6ICJiZDNhNWRmY2ZkZjg0NDczOTViZDJiZmUwNGY0YzAzMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJwcmVja3Jhc25vIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzM1Y2E5OGJlZGUzODY1ZGQxMjA1ZTRkMDkxMDM2Y2Q5ZGMzNjc5MWI4M2VhNGUwZmY0YTk5YWQ2MWI3MWU4OTgiCiAgICB9CiAgfQp9"; } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java index 219e644f3..f7a9097e3 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java @@ -5,6 +5,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -14,21 +17,36 @@ public class NPCRabbitGranny extends HypixelNPC { public NPCRabbitGranny() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + return data.getEmployees().containsKey(NPC_NAME); + } + return false; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxMjU5NDIyNDA2NCwKICAicHJvZmlsZUlkIiA6ICI2OGVmMmM5NTc5NjM0MjE4YjYwNTM5YWVlOTU3NWJiNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJUaGVNdWx0aUFjb3VudCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9kNmViMmQ4NWVlOGUzYWYxYzJlYzkzNGJlYjcwYTM5YzVlNzY2YjIzYmRhYjYzMjEwYmQyYWFjZDczY2JiZmM4IgogICAgfQogIH0KfQ=="; } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java index 13c17ca7d..837a83b08 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java @@ -5,6 +5,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -14,21 +17,36 @@ public class NPCRabbitSis extends HypixelNPC { public NPCRabbitSis() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + return data.getEmployees().containsKey(NPC_NAME); + } + return false; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxMjg0NzA5MzAxMSwKICAicHJvZmlsZUlkIiA6ICIyMWNjMzkxZmNkMjc0NzY5OTg5Y2M3M2VjYWRiNTE3YiIsCiAgInByb2ZpbGVOYW1lIiA6ICJHT1NUTFk5NyIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9mZDA3NmUwZTNkNDA3MmQwZmZmZWUwYTg3YTVkNzI2ZmMzNGIyYmNlYzM4YzI2NGZiOWI2Nzg3MWE4ZWFkNjMzIgogICAgfQogIH0KfQ=="; } @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java index 9826e56a0..a7a5a4884 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java @@ -5,6 +5,9 @@ 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.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import java.util.stream.Stream; @@ -14,21 +17,36 @@ public class NPCRabbitUncle extends HypixelNPC { public NPCRabbitUncle() { super(new HumanConfiguration() { + @Override + public boolean visible(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + return data.getEmployees().containsKey(NPC_NAME); + } + return false; + } + @Override public String[] holograms(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + } + } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; } @Override public String signature(HypixelPlayer player) { - // TODO: Add skin signature return ""; } @Override public String texture(HypixelPlayer player) { - // TODO: Add skin texture - return ""; + return "ewogICJ0aW1lc3RhbXAiIDogMTcxNjkwOTAxMDg2NywKICAicHJvZmlsZUlkIiA6ICJmYjZkM2E5Zjk3MWY0ZTdlYmQ0MjE2Yjk0MjE5NDA3NCIsCiAgInByb2ZpbGVOYW1lIiA6ICJtYXJjaXhkZCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9hODY1MTc2NzIzYTBiOWVlMjkxNjE4MGE1NWEwNGNjY2I3NzA0YWQxZjMxZmRmM2U5ZDg5Yzc5OGY2ODAyZTZiIgogICAgfQogIH0KfQ=="; } @Override diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java index 48ec42f11..86d444114 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java @@ -80,6 +80,7 @@ import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import net.swofty.type.skyblockgeneric.user.SkyBlockScoreboard; import net.swofty.type.skyblockgeneric.user.StashReminder; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryProductionLoop; import net.swofty.type.generic.user.categories.CustomGroups; import net.swofty.type.skyblockgeneric.user.fairysouls.FairySoul; import net.swofty.type.skyblockgeneric.user.fairysouls.FairySoulZone; @@ -326,6 +327,7 @@ public void initialize(MinecraftServer server) { // Start repeaters SkyBlockScoreboard.start(); StashReminder.start(MinecraftServer.getSchedulerManager()); + ChocolateFactoryProductionLoop.start(); PlayerHolograms.updateAll(MinecraftServer.getSchedulerManager()); /** diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java new file mode 100644 index 000000000..16dc50fa9 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -0,0 +1,354 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import net.swofty.type.skyblockgeneric.data.SkyBlockDataHandler; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +/** + * Helper class for Chocolate Factory operations. + * Provides utility methods for production calculation, formatting, and player interactions. + */ +public class ChocolateFactoryHelper { + private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance(Locale.US); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.0"); + + /** + * Gets the chocolate factory data for a player + */ + public static DatapointChocolateFactory.ChocolateFactoryData getData(SkyBlockPlayer player) { + return player.getSkyblockDataHandler() + .get(SkyBlockDataHandler.Data.CHOCOLATE_FACTORY, DatapointChocolateFactory.class) + .getValue(); + } + + /** + * Gets the chocolate factory datapoint for a player + */ + public static DatapointChocolateFactory getDatapoint(SkyBlockPlayer player) { + return player.getSkyblockDataHandler() + .get(SkyBlockDataHandler.Data.CHOCOLATE_FACTORY, DatapointChocolateFactory.class); + } + + /** + * Updates chocolate production for the player based on time elapsed + */ + public static void updateProduction(SkyBlockPlayer player) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + data.updateChocolateFromProduction(); + datapoint.setValue(data); + } + + /** + * Handles a click on the chocolate cookie + */ + public static void handleClick(SkyBlockPlayer player) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + data.click(); + datapoint.setValue(data); + } + + /** + * Formats chocolate amount for display with commas (e.g., 1,000 not 1k) + */ + public static String formatChocolate(long amount) { + return NUMBER_FORMAT.format(amount); + } + + /** + * Formats production per second for display + */ + public static String formatProductionPerSecond(double production) { + return DECIMAL_FORMAT.format(production) + "/s"; + } + + /** + * Formats production per hour for display + */ + public static String formatProductionPerHour(double productionPerSecond) { + double perHour = productionPerSecond * 3600; + return formatChocolate((long) perHour) + "/h"; + } + + /** + * Gets the upgrade cost for Hand-Baked Chocolate + * Level 1→2: 500, Level 2→3: 1000, etc. + * Internal level 0 = display level 1, so cost = 500 * (internalLevel + 1) + * Max internal level is 9 (display level 10) + */ + public static long getHandBakedChocolateCost(int currentLevel) { + if (currentLevel >= 9) return 0; // Already maxed at level 10 + return 500L * (currentLevel + 1); + } + + /** + * Gets the upgrade cost for Rabbit Barn + */ + public static long getRabbitBarnCost(int currentLevel) { + return (long) (500 * Math.pow(2.5, currentLevel)); + } + + /** + * Gets the upgrade cost for Rabbit Shrine + */ + public static long getRabbitShrineCost(int currentLevel) { + return (long) (1000 * Math.pow(3, currentLevel)); + } + + /** + * Gets the upgrade cost for Time Tower + */ + public static long getTimeTowerCost(int currentLevel) { + return (long) (2500 * Math.pow(2, currentLevel)); + } + + /** + * Gets the upgrade cost for Coach Jackrabbit + */ + public static long getCoachJackrabbitCost(int currentLevel) { + return (long) (5000 * Math.pow(1.5, currentLevel)); + } + + /** + * Gets the employee index (1-7) for cost calculation + */ + public static int getEmployeeIndex(String employeeName) { + return switch (employeeName) { + case "Rabbit Bro" -> 1; + case "Rabbit Cousin" -> 2; + case "Rabbit Sis" -> 3; + case "Rabbit Daddy" -> 4; + case "Rabbit Granny" -> 5; + case "Rabbit Uncle" -> 6; + case "Rabbit Dog" -> 7; + default -> 1; + }; + } + + /** + * Gets the cost to hire/upgrade an employee at a specific level + * For Rabbit Bro levels 1-10: (30 + 20 × CF) × multiplier + * For all other cases: base_cost × 1.05^L where base_cost = (216 + 144 × CF) × i² + */ + public static long getEmployeeCost(String employeeName, int targetLevel, int chocolateFactoryLevel) { + int employeeIndex = getEmployeeIndex(employeeName); + int cf = chocolateFactoryLevel; // CF level 1-6 + + // Rabbit Bro's first 10 levels use special formula + if (employeeName.equals("Rabbit Bro") && targetLevel <= 10) { + double multiplier = getRabbitBroMultiplier(targetLevel); + return (long) ((30 + 20 * cf) * multiplier); + } + + // All other cases: base_cost × 1.05^L + double baseCost = (216 + 144.0 * cf) * (employeeIndex * employeeIndex); + return (long) (baseCost * Math.pow(1.05, targetLevel)); + } + + /** + * Gets the cost to hire/upgrade an employee (uses player's CF level) + */ + public static long getEmployeeCost(SkyBlockPlayer player, String employeeName, int targetLevel) { + int cfLevel = getData(player).getPrestigeLevel() + 1; // Prestige 0 = CF1, etc. + return getEmployeeCost(employeeName, targetLevel, cfLevel); + } + + /** + * Legacy method - assumes CF level 1 + */ + public static long getEmployeeCost(String employeeName, int targetLevel) { + return getEmployeeCost(employeeName, targetLevel, 1); + } + + /** + * Gets the multiplier for Rabbit Bro's first 10 levels + */ + private static double getRabbitBroMultiplier(int level) { + return switch (level) { + case 1 -> 1; + case 2 -> 2; + case 3 -> 4; + case 4 -> 6; + case 5 -> 8; + case 6 -> 9; + case 7 -> 9.5; + case 8 -> 10; + case 9 -> 10.5; + case 10 -> 11; + default -> 11; + }; + } + + /** + * Gets the base production per level for an employee type + * This is the chocolate per second gained per employee level + */ + public static double getEmployeeBaseProduction(String employeeName) { + return switch (employeeName) { + case "Rabbit Bro" -> 1.0; // +1/level + case "Rabbit Cousin" -> 2.0; // +2/level + case "Rabbit Sis" -> 3.0; // +3/level + case "Rabbit Daddy" -> 4.0; // +4/level + case "Rabbit Granny" -> 5.0; // +5/level + case "Rabbit Uncle" -> 6.0; // +6/level + case "Rabbit Dog" -> 7.0; // +7/level + default -> 1.0; + }; + } + + /** + * Gets the employee that must be at level 20 to unlock the given employee + * Returns null if no prerequisite (Rabbit Bro) + */ + public static String getEmployeePrerequisite(String employeeName) { + return switch (employeeName) { + case "Rabbit Bro" -> null; // No prerequisite + case "Rabbit Cousin" -> "Rabbit Bro"; + case "Rabbit Sis" -> "Rabbit Cousin"; + case "Rabbit Daddy" -> "Rabbit Sis"; + case "Rabbit Granny" -> "Rabbit Daddy"; + case "Rabbit Uncle" -> "Rabbit Granny"; + case "Rabbit Dog" -> "Rabbit Uncle"; + default -> null; + }; + } + + /** + * Checks if an employee is unlocked for a player + */ + public static boolean isEmployeeUnlocked(SkyBlockPlayer player, String employeeName) { + String prerequisite = getEmployeePrerequisite(employeeName); + if (prerequisite == null) return true; // No prerequisite + + DatapointChocolateFactory.ChocolateFactoryData data = getData(player); + DatapointChocolateFactory.EmployeeData prereqEmployee = data.getEmployees().get(prerequisite); + return prereqEmployee != null && prereqEmployee.getLevel() >= 20; + } + + /** + * Tries to purchase an upgrade + * @return true if purchase was successful + */ + public static boolean purchaseUpgrade(SkyBlockPlayer player, UpgradeType type) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + + long cost = switch (type) { + case HAND_BAKED_CHOCOLATE -> getHandBakedChocolateCost(data.getHandBakedChocolateLevel()); + case RABBIT_BARN -> getRabbitBarnCost(data.getRabbitBarnLevel()); + case RABBIT_SHRINE -> getRabbitShrineCost(data.getRabbitShrineLevel()); + case TIME_TOWER -> getTimeTowerCost(data.getTimeTowerLevel()); + case COACH_JACKRABBIT -> getCoachJackrabbitCost(data.getCoachJackrabbitLevel()); + }; + + if (data.removeChocolate(cost)) { + switch (type) { + case HAND_BAKED_CHOCOLATE -> data.setHandBakedChocolateLevel(data.getHandBakedChocolateLevel() + 1); + case RABBIT_BARN -> data.setRabbitBarnLevel(data.getRabbitBarnLevel() + 1); + case RABBIT_SHRINE -> data.setRabbitShrineLevel(data.getRabbitShrineLevel() + 1); + case TIME_TOWER -> data.setTimeTowerLevel(data.getTimeTowerLevel() + 1); + case COACH_JACKRABBIT -> data.setCoachJackrabbitLevel(data.getCoachJackrabbitLevel() + 1); + } + datapoint.setValue(data); + return true; + } + return false; + } + + /** + * Tries to hire or upgrade an employee + * @return true if purchase was successful + */ + public static boolean hireOrUpgradeEmployee(SkyBlockPlayer player, String employeeName) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + + DatapointChocolateFactory.EmployeeData existing = data.getEmployees().get(employeeName); + int targetLevel = existing != null ? existing.getLevel() + 1 : 1; + + // Check if employee is unlocked (prerequisite employee at level 20) + if (!isEmployeeUnlocked(player, employeeName)) { + return false; + } + + long cost = getEmployeeCost(player, employeeName, targetLevel); + + if (data.removeChocolate(cost)) { + double baseProduction = getEmployeeBaseProduction(employeeName); + data.setEmployee(employeeName, targetLevel, baseProduction); + datapoint.setValue(data); + return true; + } + return false; + } + + /** + * Activates the Time Tower for the player + * @return true if activation was successful + */ + public static boolean activateTimeTower(SkyBlockPlayer player) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + + if (data.activateTimeTower()) { + datapoint.setValue(data); + return true; + } + return false; + } + + /** + * Adds a Time Tower charge to the player + */ + public static void addTimeTowerCharge(SkyBlockPlayer player) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + data.setTimeTowerCharges(data.getTimeTowerCharges() + 1); + datapoint.setValue(data); + } + + /** + * Gets the prestige rank name based on level + */ + public static String getPrestigeRankName(int level) { + return switch (level) { + case 0 -> "Newcomer"; + case 1 -> "Apprentice"; + case 2 -> "Worker"; + case 3 -> "Journeyman"; + case 4 -> "Expert"; + case 5 -> "Master"; + case 6 -> "Grandmaster"; + default -> "Unknown"; + }; + } + + /** + * Gets the prestige rank color based on level + */ + public static String getPrestigeRankColor(int level) { + return switch (level) { + case 0 -> "§7"; + case 1 -> "§a"; + case 2 -> "§9"; + case 3 -> "§5"; + case 4 -> "§6"; + case 5 -> "§d"; + case 6 -> "§b"; + default -> "§f"; + }; + } + + public enum UpgradeType { + HAND_BAKED_CHOCOLATE, + RABBIT_BARN, + RABBIT_SHRINE, + TIME_TOWER, + COACH_JACKRABBIT + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java new file mode 100644 index 000000000..e96bde5c0 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java @@ -0,0 +1,30 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.timer.TaskSchedule; +import net.swofty.type.skyblockgeneric.SkyBlockGenericLoader; + +/** + * Handles periodic chocolate production updates for all online players. + * This runs every second to update chocolate amounts based on production rates. + */ +public class ChocolateFactoryProductionLoop { + + /** + * Starts the chocolate factory production loop. + * Should be called during server initialization. + */ + public static void start() { + MinecraftServer.getSchedulerManager().submitTask(() -> { + // Update chocolate production for all online players + SkyBlockGenericLoader.getLoadedPlayers().forEach(player -> { + try { + ChocolateFactoryHelper.updateProduction(player); + } catch (Exception e) { + // Silently ignore errors to prevent loop from stopping + } + }); + return TaskSchedule.seconds(1); + }); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java new file mode 100644 index 000000000..49e16f5e2 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java @@ -0,0 +1,24 @@ +package net.swofty.type.skyblockgeneric.commands; + +import net.swofty.type.generic.command.CommandParameters; +import net.swofty.type.generic.command.HypixelCommand; +import net.swofty.type.generic.user.categories.Rank; +import net.swofty.type.skyblockgeneric.gui.inventories.GUIChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +@CommandParameters(aliases = "cf factory", + description = "Opens the Chocolate Factory menu", + usage = "/chocolatefactory", + permission = Rank.DEFAULT, + allowsConsole = false) +public class ChocolateFactoryCommand extends HypixelCommand { + @Override + public void registerUsage(MinestomCommand command) { + command.addSyntax((sender, context) -> { + if (!permissionCheck(sender)) return; + + SkyBlockPlayer player = (SkyBlockPlayer) sender; + new GUIChocolateFactory().open(player); + }); + } +} 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 4b01c28b0..cf53969a5 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 @@ -457,6 +457,9 @@ DatapointStash.class, new DatapointStash("stash")), COLLECTED_MOB_TYPE_REWARDS("collected_mob_type_rewards", false, false, false, DatapointCollectedMobTypeRewards.class, new DatapointCollectedMobTypeRewards("collected_mob_type_rewards")), + + CHOCOLATE_FACTORY("chocolate_factory", false, false, false, + DatapointChocolateFactory.class, new DatapointChocolateFactory("chocolate_factory")), ; @Getter private final String key; diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java index c75da991d..9d9a458ac 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java @@ -19,6 +19,7 @@ public String serialize(ChocolateFactoryData value) { json.put("chocolate", value.chocolate); json.put("chocolateAllTime", value.chocolateAllTime); json.put("lastUpdated", value.lastUpdated); + json.put("partialChocolate", value.partialChocolate); // Upgrades json.put("timeTowerLevel", value.timeTowerLevel); @@ -67,6 +68,7 @@ public ChocolateFactoryData deserialize(String json) { jsonObject.optLong("chocolate", 0L), jsonObject.optLong("chocolateAllTime", 0L), jsonObject.optLong("lastUpdated", System.currentTimeMillis()), + jsonObject.optDouble("partialChocolate", 0.0), jsonObject.optInt("timeTowerLevel", 0), jsonObject.optInt("timeTowerCharges", 0), jsonObject.optLong("timeTowerLastUsed", 0L), @@ -95,6 +97,7 @@ public ChocolateFactoryData clone(ChocolateFactoryData value) { value.chocolate, value.chocolateAllTime, value.lastUpdated, + value.partialChocolate, value.timeTowerLevel, value.timeTowerCharges, value.timeTowerLastUsed, @@ -129,6 +132,7 @@ public static class ChocolateFactoryData { private long chocolate; private long chocolateAllTime; private long lastUpdated; + private double partialChocolate; // Accumulates fractional chocolate production // Upgrades private int timeTowerLevel; @@ -151,6 +155,7 @@ public ChocolateFactoryData() { this.chocolate = 0L; this.chocolateAllTime = 0L; this.lastUpdated = System.currentTimeMillis(); + this.partialChocolate = 0.0; this.timeTowerLevel = 0; this.timeTowerCharges = 0; this.timeTowerLastUsed = 0L; @@ -275,8 +280,16 @@ public void updateChocolateFromProduction() { double secondsPassed = (now - lastUpdated) / 1000.0; if (secondsPassed > 0) { - long produced = (long) (getChocolatePerSecond() * secondsPassed); - addChocolate(produced); + // Accumulate fractional production + partialChocolate += getChocolatePerSecond() * secondsPassed; + + // Only add whole chocolate + long wholeChocolate = (long) partialChocolate; + if (wholeChocolate > 0) { + addChocolate(wholeChocolate); + partialChocolate -= wholeChocolate; // Keep the fractional part + } + lastUpdated = now; } } @@ -326,10 +339,10 @@ public static class EmployeeData { /** * Gets production per second for this employee - * Production scales with level + * Production = baseProduction * level */ public double getProductionPerSecond() { - return baseProduction * (1 + (level - 1) * 0.5); + return baseProduction * level; } } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java new file mode 100644 index 000000000..5f6cec6d3 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -0,0 +1,789 @@ +package net.swofty.type.skyblockgeneric.gui.inventories; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.event.inventory.InventoryCloseEvent; +import net.minestom.server.event.inventory.InventoryPreClickEvent; +import net.minestom.server.inventory.click.Click; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.generic.gui.inventory.RefreshingGUI; +import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; +import net.swofty.type.generic.gui.inventory.item.GUIItem; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class GUIChocolateFactory extends HypixelInventoryGUI implements RefreshingGUI { + // Texture IDs (the hash part of the minecraft texture URL) + private static final String CHOCOLATE_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; + private static final String HOPPITY_TEXTURE = "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692"; + private static final String COACH_JACKRABBIT_TEXTURE = "bc0cc67e79c228e541e68aeb1d81ed7af51166622ad4db9417d7a29d1b89af95"; + + private static final Sound CLICK_SOUND = Sound.sound(Key.key("block.note_block.bit"), Sound.Source.PLAYER, 1.0f, 1.21f); + private static final Sound NOT_ENOUGH_CHOCOLATE_SOUND = Sound.sound(Key.key("entity.enderman.teleport"), Sound.Source.PLAYER, 8.0f, 0.0f); + + // Employee slots (slots 28-34) + private static final int[] EMPLOYEE_SLOTS = {28, 29, 30, 31, 32, 33, 34}; + + // Employee names in order matching the slots + private static final String[] EMPLOYEE_NAMES = { + "Rabbit Bro", "Rabbit Cousin", "Rabbit Sis", "Rabbit Daddy", + "Rabbit Granny", "Rabbit Uncle", "Rabbit Dog" + }; + + // Employee texture IDs (the hash part of the minecraft texture URL) + private static final String[] EMPLOYEE_TEXTURES = { + "287934bdd9df2705b251bb997e029b18c1e94df12992b8107e74497b205ca7e8", // Rabbit Bro + "a982825c01b658f348a099b4579029a180d2e415183951b2e6e5e27257df4254", // Rabbit Cousin + "fd076e0e3d4072d0fffee0a87a5d726fc34b2bcec38c264fb9b67871a8ead633", // Rabbit Sis + "57cab0c34d7ddcf72db56ff36f2883f554cff76eb5d3b3e0562338036c976043", // Rabbit Daddy + "d6eb2d85ee8e3af1c2ec934beb70a39c5e766b23bdab63210bd2aacd73cbbfc8", // Rabbit Granny + "a865176723a0b9ee2916180a55a04cccb7704ad1f31fdf3e9d89c798f6802e6b", // Rabbit Uncle + "35ca98bede3865dd1205e4d091036cd9dc36791b83ea4e0ff4a99ad61b71e898" // Rabbit Dog + }; + + // Employee subtitles from readm.md + private static final String[] EMPLOYEE_SUBTITLES = { + "Ambition on two feet!", + "Laid-back legend!", + "Rebel with a cause!", + "CFO with nerves of steel!", + "Storyteller supreme!", + "Stuck in a highlight reel!", + "Making chocolate, not eating it!" + }; + + public GUIChocolateFactory() { + super("Chocolate Factory", InventoryType.CHEST_6_ROW); + } + + @Override + public void setItems(InventoryGUIOpenEvent e) { + // Fill with black glass panes + fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + + // Slot 13: Chocolate cookie (clickable) + set(new GUIClickableItem(13) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + ChocolateFactoryHelper.handleClick(player); + player.playSound(CLICK_SOUND); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + List lore = new ArrayList<>(); + lore.add("§7§6Chocolate§7, of course, is not a valid"); + lore.add("§7source of §anutrition§7. This, however,"); + lore.add("§7does not stop it from being §dawesome§7."); + lore.add(""); + lore.add("§7Chocolate Production"); + lore.add("§6" + String.format("%.2f", data.getChocolatePerSecond()) + " §8per second"); + lore.add(""); + lore.add("§7All-time Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(data.getChocolateAllTime())); + lore.add(""); + lore.add("§eClick to uncover the meaning of life!"); + + return ItemStackCreator.getStackHead( + "§e" + ChocolateFactoryHelper.formatChocolate(data.getChocolate()) + " §6Chocolate", + CHOCOLATE_TEXTURE, 1, lore); + } + }); + + // Slot 27: Prestige/Chocolate Factory level (dropper) + set(new GUIItem(27) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + int prestigeLevel = data.getPrestigeLevel(); + String prestigeColor = ChocolateFactoryHelper.getPrestigeRankColor(prestigeLevel); + String prestigeName = ChocolateFactoryHelper.getPrestigeRankName(prestigeLevel); + + List lore = new ArrayList<>(); + lore.add("§7Chocolate Production Multiplier: §6" + String.format("%.1fx", data.getShrineMultiplier() * data.getCoachMultiplier())); + lore.add("§7Max Rabbit Rarity: §b§lDIVINE"); + lore.add("§7Max Chocolate: §660B"); + lore.add("§7Max Employee: §7[220§7] §bBoard Member"); + lore.add("§7Max §cRabbit Hitman §7Slots: §628"); + lore.add(""); + lore.add("§7Chocolate this Prestige: §6" + ChocolateFactoryHelper.formatChocolate(data.getChocolateAllTime())); + lore.add(""); + if (prestigeLevel >= 6) { + lore.add("§aYou have reached max prestige!"); + } else { + lore.add("§eClick to prestige!"); + } + + return ItemStackCreator.getStack(prestigeColor + "Chocolate Factory " + toRoman(prestigeLevel + 1), Material.DROPPER, 1, lore); + } + }); + + // Slots 28-34: Employee rabbits + setupEmployeeSlots(); + + // Slot 35: Rabbit Barn (oak fence) + set(new GUIClickableItem(35) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.RABBIT_BARN)) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + player.sendMessage("§7Your §aRabbit Barn §7capacity has been increased to §a" + (data.getMaxRabbitSlots() + 2) + " Rabbits§7!"); + player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getRabbitBarnLevel(); + long cost = ChocolateFactoryHelper.getRabbitBarnCost(level); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add("§7Your §aRabbit Barn §7can only hold so"); + lore.add("§7many §aChocolate Rabbits§7."); + lore.add(""); + lore.add("§7If you try collecting more unique"); + lore.add("§7rabbits than your barn can hold,"); + lore.add("§7they will be §ccrushed§7."); + lore.add(""); + lore.add("§7Your Barn: §a" + data.getEmployeeCount() + "§7/§a" + (data.getMaxRabbitSlots() + 2) + " Rabbits"); + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + toRoman(level + 2)); + lore.add(" §a+2 Capacity"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + + return ItemStackCreator.getStack("§aRabbit Barn " + toRoman(level + 1), Material.OAK_FENCE, 1, lore); + } + }); + + // Slot 38: Hand-Baked Chocolate (cookie) + set(new GUIClickableItem(38) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.HAND_BAKED_CHOCOLATE)) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + player.sendMessage("§7You will now produce §6+" + data.getClickPower() + " Chocolate §7per click!"); + player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getHandBakedChocolateLevel(); + long cost = ChocolateFactoryHelper.getHandBakedChocolateCost(level); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add("§7A good boss can get down in the"); + lore.add("§7trenches and help out their"); + lore.add("§7workforce. In exchange for some"); + lore.add("§7§6Chocolate§7, you can increase the"); + lore.add("§7amount of §6Chocolate §7that you"); + lore.add("§7produce each time you click!"); + lore.add(""); + lore.add("§7Chocolate Per Click: §6+" + data.getClickPower() + " Chocolate"); + lore.add(""); + if (level >= 9) { + lore.add("§aYou have reached the maximum"); + lore.add("§aamount of upgrades!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dHand-Baked Chocolate " + toRoman(level + 2)); + lore.add(" §6+1 Chocolate Per Click"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } + + return ItemStackCreator.getStack("§dHand-Baked Chocolate " + toRoman(level + 1), Material.COOKIE, 1, lore); + } + }); + + // Slot 39: Time Tower (clock) + set(new GUIClickableItem(39) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Right-click to activate + if (event.getClick() instanceof Click.Right && data.getTimeTowerLevel() > 0 && data.getTimeTowerCharges() > 0 && !data.isTimeTowerActive()) { + if (ChocolateFactoryHelper.activateTimeTower(player)) { + player.sendMessage("§aTime Tower activated for 1 hour!"); + player.playSound(Sound.sound(Key.key("block.beacon.activate"), Sound.Source.PLAYER, 1.0f, 1.0f)); + return; + } + } + + // Left-click to upgrade + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { + player.sendMessage("§7You upgraded to §dTime Tower " + toRoman(data.getTimeTowerLevel() + 1) + "§7!"); + player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + } else if (data.getTimeTowerLevel() >= 15) { + player.sendMessage("§cThe Time Tower is already at its maximum level!"); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getTimeTowerLevel(); + long cost = ChocolateFactoryHelper.getTimeTowerCost(level); + boolean canAfford = data.getChocolate() >= cost; + boolean isActive = data.isTimeTowerActive(); + + List lore = new ArrayList<>(); + lore.add("§7When active, this ancient building"); + lore.add("§7increases the production of your"); + lore.add("§7§6Chocolate Factory §7by §6+" + String.format("%.1fx", data.getTimeTowerMultiplier() - 1.0 + (level > 0 ? 0.1 * level : 0.1)) + " §7for §a1h§7."); + lore.add(""); + if (isActive) { + long remaining = data.getTimeTowerActiveUntil() - System.currentTimeMillis(); + long minutes = remaining / 60000; + long seconds = (remaining % 60000) / 1000; + lore.add("§7Status: §a§lACTIVE"); + lore.add("§7Time Remaining: §a" + minutes + "m " + seconds + "s"); + } else { + lore.add("§7Status: §c§lINACTIVE"); + } + lore.add(""); + lore.add("§7Charges: §a" + data.getTimeTowerCharges() + "§7/§a3"); + lore.add(""); + if (level >= 15) { + lore.add("§aThe Time Tower is maxed out!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dTime Tower " + toRoman(level + 2)); + lore.add(" §6+0.1x Production Multiplier"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } + if (data.getTimeTowerCharges() > 0 && !isActive) { + lore.add("§dRight-click to activate!"); + } + + return ItemStackCreator.getStack("§dTime Tower " + toRoman(level + 1), Material.CLOCK, 1, lore); + } + }); + + // Slot 41: Rabbit Shrine (rabbit foot) + set(new GUIClickableItem(41) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.RABBIT_SHRINE)) { + player.sendMessage("§aUpgraded Rabbit Shrine!"); + player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getRabbitShrineLevel(); + long cost = ChocolateFactoryHelper.getRabbitShrineCost(level); + boolean canAfford = data.getChocolate() >= cost; + int oddsBonus = level * 2; // 2% per level + + List lore = new ArrayList<>(); + lore.add("§7The §dRabbit Shrine §7increases the"); + lore.add("§7§dodds §7of finding §aChocolate Rabbits §7of"); + lore.add("§7higher rarity during §dHoppity's Hunt"); + lore.add("§d§7by §a" + oddsBonus + "%§7."); + lore.add(""); + if (level >= 20) { + lore.add("§aYour Rabbit Shrine is at its maximum"); + lore.add("§alevel!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dRabbit Shrine " + toRoman(level + 2)); + lore.add(" §a+2% Rare Rabbit Odds"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } + + return ItemStackCreator.getStack("§dRabbit Shrine " + toRoman(level + 1), Material.RABBIT_FOOT, 1, lore); + } + }); + + // Slot 42: Coach Jackrabbit (player head) + set(new GUIClickableItem(42) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.COACH_JACKRABBIT)) { + player.sendMessage("§aUpgraded Coach Jackrabbit!"); + player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getCoachJackrabbitLevel(); + long cost = ChocolateFactoryHelper.getCoachJackrabbitCost(level); + boolean canAfford = data.getChocolate() >= cost; + double multiplierBonus = level * 0.01; + + List lore = new ArrayList<>(); + lore.add("§8§oPep talk pro!"); + lore.add(""); + lore.add("§7§dCoach Jackrabbit §7is a motivational"); + lore.add("§7speaker that is helping you reach"); + lore.add("§7your full potential by granting §6+" + String.format("%.1fx", multiplierBonus)); + lore.add("§6Chocolate §7per second!"); + lore.add(""); + if (level >= 20) { + lore.add("§aCoach Jackrabbit has already taught"); + lore.add("§ayou all that he can teach!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dCoach Jackrabbit " + toRoman(level + 2)); + lore.add(" §6+0.01x Production Multiplier"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } + + return ItemStackCreator.getStackHead("§dCoach Jackrabbit " + toRoman(level + 1), COACH_JACKRABBIT_TEXTURE, 1, lore); + } + }); + + // Slot 45: Chocolate Production info (cocoa beans) + set(new GUIItem(45) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + double baseProduction = 0; + for (DatapointChocolateFactory.EmployeeData employee : data.getEmployees().values()) { + baseProduction += employee.getProductionPerSecond(); + } + + double totalMultiplier = data.getShrineMultiplier() * data.getTimeTowerMultiplier() * data.getCoachMultiplier(); + + List lore = new ArrayList<>(); + lore.add("§6" + String.format("%.2f", data.getChocolatePerSecond()) + " Chocolate §8per second"); + lore.add(""); + lore.add("§7Base Chocolate: §6" + String.format("%.0f", baseProduction) + " §8per second"); + lore.add(" §6+" + String.format("%.0f", baseProduction) + " §8(Rabbit Employees§8)"); + lore.add(""); + lore.add("§7Total Multiplier: §6" + String.format("%.3fx", totalMultiplier)); + lore.add(" §6+1x §8(Base Multiplier)"); + if (data.getRabbitShrineLevel() > 0) { + lore.add(" §6+" + String.format("%.1fx", data.getShrineMultiplier() - 1.0) + " §8(Rabbit Shrine)"); + } + if (data.getCoachJackrabbitLevel() > 0) { + lore.add(" §6+" + String.format("%.2fx", data.getCoachMultiplier() - 1.0) + " §8(Coach Jackrabbit)"); + } + if (data.isTimeTowerActive()) { + lore.add(" §6+" + String.format("%.1fx", data.getTimeTowerMultiplier() - 1.0) + " §8(Time Tower)"); + } + + return ItemStackCreator.getStack("§6Chocolate Production", Material.COCOA_BEANS, 1, lore); + } + }); + + // Slot 47: Chocolate Shop (emerald) + set(new GUIClickableItem(47) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + // TODO: Open Chocolate Shop GUI + p.sendMessage("§7Opening Chocolate Shop... (Coming Soon)"); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7Spend your §6Chocolate §7on the world's"); + lore.add("§7most delectable treats in the"); + lore.add("§7§6Chocolate Shop§7."); + lore.add(""); + lore.add("§eClick to view!"); + + return ItemStackCreator.getStack("§6Chocolate Shop", Material.EMERALD, 1, lore); + } + }); + + // Slot 48: Go Back (arrow) + set(new GUIClickableItem(48) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + p.closeInventory(); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7To Calendar and Events"); + + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + } + }); + + // Slot 49: Close (barrier) + set(GUIClickableItem.getCloseItem(49)); + + // Slot 50: Hoppity's Collection (player head) + set(new GUIClickableItem(50) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + // TODO: Open Hoppity's Collection GUI + p.sendMessage("§7Opening Hoppity's Collection... (Coming Soon)"); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + int rabbitsFound = data.getEmployeeCount(); + int totalRabbits = 512; + double percentage = (rabbitsFound / (double) totalRabbits) * 100; + + List lore = new ArrayList<>(); + lore.add("§7Help §aHoppity §7find all of his §aChocolate"); + lore.add("§aRabbits §7during the §dHoppity's Hunt"); + lore.add("§d§7event!"); + lore.add(""); + lore.add("§7The more unique §aChocolate Rabbits"); + lore.add("§a§7that you find, the more your"); + lore.add("§7§6Chocolate Factory §7will produce!"); + lore.add(""); + lore.add("§7Finding duplicate Rabbits grants"); + lore.add("§7§a+10% §7extra §6Chocolate §7per duplicate,"); + lore.add("§7up to §a+100%§7!"); + lore.add(""); + lore.add("§7Rabbits Found: §e" + String.format("%.1f", percentage) + "§6%"); + lore.add("§2§l§m §f§l§m §r §e" + rabbitsFound + "§6/§e" + totalRabbits); + lore.add(""); + lore.add("§eClick to view!"); + + return ItemStackCreator.getStackHead("§aHoppity's Collection", HOPPITY_TEXTURE, 1, lore); + } + }); + + // Slot 51: Rabbit Hitman (bow) + set(new GUIClickableItem(51) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + // TODO: Open Rabbit Hitman GUI + p.sendMessage("§7Opening Rabbit Hitman... (Coming Soon)"); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7§7Hire this private rabbit to hunt eggs"); + lore.add("§7for you, they will collect eggs you"); + lore.add("§7missed!"); + lore.add(""); + lore.add("§7Available eggs: §a0"); + lore.add("§7Purchased slots: §e0§7/§a28"); + lore.add(""); + lore.add("§eClick to view!"); + + return ItemStackCreator.getStack("§cRabbit Hitman", Material.BOW, 1, lore); + } + }); + + // Slot 52: Chocolate Factory Ranking (milk bucket) + set(new GUIItem(52) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + List lore = new ArrayList<>(); + lore.add("§7§7You are §8#§b??? §7in all-time"); + lore.add("§7Chocolate."); + lore.add("§7§8You are in the top §e??%§8 of players!"); + + return ItemStackCreator.getStack("§dChocolate Factory Ranking", Material.MILK_BUCKET, 1, lore); + } + }); + + // Slot 53: Chocolate Factory Milestones (ladder) + set(new GUIClickableItem(53) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + // TODO: Open Milestones GUI + p.sendMessage("§7Opening Chocolate Factory Milestones... (Coming Soon)"); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7Unlock special §aChocolate Rabbits §7by"); + lore.add("§7reaching all-time §6Chocolate"); + lore.add("§6§7milestones!"); + lore.add(""); + lore.add("§eClick to view!"); + + return ItemStackCreator.getStack("§6Chocolate Factory Milestones", Material.LADDER, 1, lore); + } + }); + } + + private void setupEmployeeSlots() { + for (int i = 0; i < EMPLOYEE_SLOTS.length; i++) { + int slot = EMPLOYEE_SLOTS[i]; + String employeeName = EMPLOYEE_NAMES[i]; + String employeeTexture = EMPLOYEE_TEXTURES[i]; + String employeeSubtitle = EMPLOYEE_SUBTITLES[i]; + + set(new GUIClickableItem(slot) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check if employee is unlocked (prerequisite at level 20) + if (!ChocolateFactoryHelper.isEmployeeUnlocked(player, employeeName)) { + String prereq = ChocolateFactoryHelper.getEmployeePrerequisite(employeeName); + player.sendMessage("§cPromote §f" + prereq + " §cto §7[20§7] §fEmployee §cto unlock §f" + employeeName + "§c!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + + DatapointChocolateFactory.EmployeeData existingEmployee = data.getEmployees().get(employeeName); + + // Check if employee is not hired yet - show special message + if (existingEmployee == null) { + long cost = ChocolateFactoryHelper.getEmployeeCost(employeeName, 1); + if (data.getChocolate() < cost) { + player.sendMessage("§c" + employeeName + " does not work at your §6Chocolate Factory §cyet!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + } + + if (ChocolateFactoryHelper.hireOrUpgradeEmployee(player, employeeName)) { + // Get fresh data after the hire/upgrade + DatapointChocolateFactory.ChocolateFactoryData newData = ChocolateFactoryHelper.getData(player); + DatapointChocolateFactory.EmployeeData emp = newData.getEmployees().get(employeeName); + int level = emp != null ? emp.getLevel() : 1; + String rank = getEmployeeRank(level); + String rankColor = "§" + getEmployeeRankColor(level); + + player.sendMessage(rankColor + employeeName + " §7has been promoted to §7[" + level + "§7] " + rankColor + rank + "§7!"); + player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check if employee is unlocked (prerequisite at level 20) + boolean isUnlocked = ChocolateFactoryHelper.isEmployeeUnlocked(player, employeeName); + String prerequisite = ChocolateFactoryHelper.getEmployeePrerequisite(employeeName); + + if (!isUnlocked) { + // Locked - show grey dye with unlock requirement + DatapointChocolateFactory.EmployeeData prereqEmployee = data.getEmployees().get(prerequisite); + int prereqLevel = prereqEmployee != null ? prereqEmployee.getLevel() : 0; + + List lore = new ArrayList<>(); + lore.add("§8§o" + employeeSubtitle); + lore.add(""); + lore.add("§c§lLOCKED"); + lore.add(""); + lore.add("§7Promote §f" + prerequisite + " §7to §7[20§7]"); + lore.add("§fEmployee §7to unlock!"); + lore.add(""); + lore.add("§7Progress: §e" + prereqLevel + "§7/§a20"); + + return ItemStackCreator.getStack("§c" + employeeName, Material.GRAY_DYE, 1, lore); + } + + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(employeeName); + double baseProduction = ChocolateFactoryHelper.getEmployeeBaseProduction(employeeName); + + if (employee == null) { + // Not hired yet - show grey dye + long cost = ChocolateFactoryHelper.getEmployeeCost(player, employeeName, 1); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add("§8§o" + employeeSubtitle); + lore.add(""); + lore.add("§c" + employeeName + " §7does not work at"); + lore.add("§7your §6Chocolate Factory §7yet!"); + lore.add(""); + lore.add("§7Hire them and they will produce"); + lore.add("§6+" + String.format("%.0f", baseProduction) + " Chocolate §7per second!"); + lore.add(""); + lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to hire!" : "§cYou don't have enough Chocolate!"); + + return ItemStackCreator.getStack("§c" + employeeName, Material.GRAY_DYE, 1, lore); + } else { + // Already hired - show player head + int level = employee.getLevel(); + long cost = ChocolateFactoryHelper.getEmployeeCost(player, employeeName, level + 1); + boolean canAfford = data.getChocolate() >= cost; + double currentProduction = baseProduction * level; + String rank = getEmployeeRank(level); + String rankColor = getEmployeeRankColor(level); + + List lore = new ArrayList<>(); + lore.add("§8§o" + employeeSubtitle); + lore.add(""); + lore.add("§7§b" + employeeName + " §7is a §" + rankColor + rank + "§7. They"); + if (rank.equals("Board Member")) { + lore.add("§7are on the Board of Rabbits and"); + } else { + lore.add("§7are working hard and"); + } + lore.add("§7produce §6+" + String.format("%.0f", currentProduction) + " Chocolate §7per second!"); + lore.add(""); + + if (level >= 220) { + lore.add("§7§b" + employeeName + " §ahas climbed as far as the"); + lore.add("§acorporate ladder will allow!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lPROMOTE §8➜ §7[" + (level + 1) + "§7] §" + getEmployeeRankColor(level + 1) + getEmployeeRank(level + 1)); + double nextProduction = baseProduction * (level + 1); + lore.add(" §6+" + String.format("%.0f", baseProduction) + " Chocolate per second"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to promote!" : "§cYou don't have enough Chocolate!"); + } + + return ItemStackCreator.getStackHead("§b" + employeeName + "§8 - §7[" + level + "§7] §" + rankColor + rank, employeeTexture, 1, lore); + } + } + }); + } + } + + private String getEmployeeRank(int level) { + if (level >= 220) return "Board Member"; + if (level >= 200) return "Executive"; + if (level >= 180) return "Director"; + if (level >= 140) return "Manager"; + if (level >= 120) return "Assistant"; + if (level >= 20) return "Employee"; + if (level >= 1) return "Intern"; + return "Unemployed"; + } + + private String getEmployeeRankColor(int level) { + if (level >= 220) return "b"; // Aqua - Board Member + if (level >= 200) return "d"; // Light Purple - Executive + if (level >= 180) return "6"; // Gold - Director + if (level >= 140) return "5"; // Dark Purple - Manager + if (level >= 120) return "9"; // Blue - Assistant + if (level >= 20) return "a"; // Green - Employee + if (level >= 1) return "7"; // Gray - Intern + return "c"; // Red - Unemployed + } + + private String toRoman(int number) { + if (number <= 0) return "I"; + String[] thousands = {"", "M", "MM", "MMM"}; + String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[number / 1000] + + hundreds[(number % 1000) / 100] + + tens[(number % 100) / 10] + + ones[number % 10]; + } + + @Override + public void refreshItems(HypixelPlayer player) { + // Update production before refreshing + ChocolateFactoryHelper.updateProduction((SkyBlockPlayer) player); + + // Re-setup all items to refresh values + setItems(new InventoryGUIOpenEvent(player, this, getInventory())); + } + + @Override + public int refreshRate() { + return 20; // Refresh every second (20 ticks) + } + + @Override + public boolean allowHotkeying() { + return false; + } + + @Override + public void onClose(InventoryCloseEvent e, CloseReason reason) { + } + + @Override + public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { + } + + @Override + public void onBottomClick(InventoryPreClickEvent e) { + e.setCancelled(true); + } +} From e31b5c35d5c759d5df0947df2e9814be5f912c27 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 09:26:26 -0300 Subject: [PATCH 03/13] Upgrades Barn System and More --- .../ChocolateFactoryHelper.java | 109 ++++++++++++- .../gui/inventories/GUIChocolateFactory.java | 148 +++++++++++++++--- 2 files changed, 227 insertions(+), 30 deletions(-) diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java index 16dc50fa9..72a4ee0f7 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -88,30 +88,127 @@ public static long getHandBakedChocolateCost(int currentLevel) { /** * Gets the upgrade cost for Rabbit Barn + * Level 1 is unlocked by default (free) + * Level 2 costs 5,000 Chocolate + * Subsequent levels: floor(5000 × 1.05^(L-2)) where L is target level */ public static long getRabbitBarnCost(int currentLevel) { - return (long) (500 * Math.pow(2.5, currentLevel)); + // Level 1 is free (unlocked by default) + if (currentLevel == 0) { + return 0; + } + // Cost formula: 5000 × 1.05^(currentLevel-1), rounded + return Math.round(5000 * Math.pow(1.05, currentLevel - 1)); } /** * Gets the upgrade cost for Rabbit Shrine + * Max level is 20, providing 40% extra chance for higher rarity rabbits */ public static long getRabbitShrineCost(int currentLevel) { - return (long) (1000 * Math.pow(3, currentLevel)); + // Cost to upgrade TO level (currentLevel + 1) + return switch (currentLevel) { + case 0 -> 10_000_000L; // To level 1 + case 1 -> 20_000_000L; // To level 2 + case 2 -> 30_000_000L; // To level 3 + case 3 -> 40_000_000L; // To level 4 + case 4 -> 60_000_000L; // To level 5 + case 5 -> 80_000_000L; // To level 6 + case 6 -> 100_000_000L; // To level 7 + case 7 -> 120_000_000L; // To level 8 + case 8 -> 150_000_000L; // To level 9 + case 9 -> 200_000_000L; // To level 10 + case 10 -> 250_000_000L; // To level 11 + case 11 -> 300_000_000L; // To level 12 + case 12 -> 350_000_000L; // To level 13 + case 13 -> 400_000_000L; // To level 14 + case 14 -> 450_000_000L; // To level 15 + case 15 -> 500_000_000L; // To level 16 + case 16 -> 550_000_000L; // To level 17 + case 17 -> 600_000_000L; // To level 18 + case 18 -> 650_000_000L; // To level 19 + case 19 -> 700_000_000L; // To level 20 + default -> 0L; // Already maxed + }; } /** - * Gets the upgrade cost for Time Tower + * Gets the upgrade cost for Time Tower based on prestige level + * Level 1 is unlocked by default (free) + */ + public static long getTimeTowerCost(int currentLevel, int prestigeLevel) { + // Level 1 is free (unlocked by default) + if (currentLevel == 0) { + return 0; + } + + // Base cost depends on prestige (Factory II = prestige 1, Factory III = prestige 2, etc.) + long baseCost = switch (prestigeLevel) { + case 1 -> 5_500_000L; // Factory II + case 2 -> 6_000_000L; // Factory III + case 3 -> 6_500_000L; // Factory IV + case 4 -> 7_000_000L; // Factory V + default -> 7_500_000L; // Factory VI+ + }; + + // Multiplier for each level (upgrading TO level currentLevel+1) + int multiplier = switch (currentLevel) { + case 1 -> 1; // To level 2 + case 2 -> 2; // To level 3 + case 3 -> 3; // To level 4 + case 4 -> 4; // To level 5 + case 5 -> 6; // To level 6 + case 6 -> 8; // To level 7 + case 7 -> 10; // To level 8 + case 8 -> 12; // To level 9 + case 9 -> 14; // To level 10 + case 10 -> 16; // To level 11 + case 11 -> 20; // To level 12 + case 12 -> 24; // To level 13 + case 13 -> 30; // To level 14 + case 14 -> 40; // To level 15 + default -> 40; + }; + + return baseCost * multiplier; + } + + /** + * Gets the upgrade cost for Time Tower (legacy, uses minimum prestige) */ public static long getTimeTowerCost(int currentLevel) { - return (long) (2500 * Math.pow(2, currentLevel)); + return getTimeTowerCost(currentLevel, 1); } /** * Gets the upgrade cost for Coach Jackrabbit + * Max level is 20, providing +0.2x CpS multiplier */ public static long getCoachJackrabbitCost(int currentLevel) { - return (long) (5000 * Math.pow(1.5, currentLevel)); + // Cost to upgrade TO level (currentLevel + 1) + return switch (currentLevel) { + case 0 -> 2_200_000L; // To level 1 + case 1 -> 3_900_000L; // To level 2 + case 2 -> 5_300_000L; // To level 3 + case 3 -> 7_200_000L; // To level 4 + case 4 -> 9_700_000L; // To level 5 + case 5 -> 13_000_000L; // To level 6 + case 6 -> 18_000_000L; // To level 7 + case 7 -> 24_000_000L; // To level 8 + case 8 -> 32_000_000L; // To level 9 + case 9 -> 43_000_000L; // To level 10 + case 10 -> 59_000_000L; // To level 11 + case 11 -> 79_000_000L; // To level 12 + case 12 -> 110_000_000L; // To level 13 + case 13 -> 140_000_000L; // To level 14 + case 14 -> 190_000_000L; // To level 15 + case 15 -> 260_000_000L; // To level 16 + case 16 -> 350_000_000L; // To level 17 + case 17 -> 480_000_000L; // To level 18 + case 18 -> 650_000_000L; // To level 19 + case 19 -> 870_000_000L; // To level 20 + default -> 0L; // Already maxed + }; } /** @@ -242,7 +339,7 @@ public static boolean purchaseUpgrade(SkyBlockPlayer player, UpgradeType type) { case HAND_BAKED_CHOCOLATE -> getHandBakedChocolateCost(data.getHandBakedChocolateLevel()); case RABBIT_BARN -> getRabbitBarnCost(data.getRabbitBarnLevel()); case RABBIT_SHRINE -> getRabbitShrineCost(data.getRabbitShrineLevel()); - case TIME_TOWER -> getTimeTowerCost(data.getTimeTowerLevel()); + case TIME_TOWER -> getTimeTowerCost(data.getTimeTowerLevel(), data.getPrestigeLevel()); case COACH_JACKRABBIT -> getCoachJackrabbitCost(data.getCoachJackrabbitLevel()); }; diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java index 5f6cec6d3..5883aa1f9 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -141,10 +141,19 @@ public ItemStack.Builder getItem(HypixelPlayer p) { @Override public void run(InventoryPreClickEvent event, HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check max level (247) + if (data.getRabbitBarnLevel() >= 247) { + player.sendMessage("§cYour Rabbit Barn is already at maximum capacity!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.RABBIT_BARN)) { - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + data = ChocolateFactoryHelper.getData(player); player.sendMessage("§7Your §aRabbit Barn §7capacity has been increased to §a" + (data.getMaxRabbitSlots() + 2) + " Rabbits§7!"); - player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); } else { player.sendMessage("§cYou don't have enough Chocolate!"); player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); @@ -168,14 +177,19 @@ public ItemStack.Builder getItem(HypixelPlayer p) { lore.add("§7they will be §ccrushed§7."); lore.add(""); lore.add("§7Your Barn: §a" + data.getEmployeeCount() + "§7/§a" + (data.getMaxRabbitSlots() + 2) + " Rabbits"); - lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + toRoman(level + 2)); - lore.add(" §a+2 Capacity"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + if (level >= 247) { + lore.add(""); + lore.add("§aYour Rabbit Barn is at maximum capacity!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + toRoman(level + 2)); + lore.add(" §a+2 Capacity"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } return ItemStackCreator.getStack("§aRabbit Barn " + toRoman(level + 1), Material.OAK_FENCE, 1, lore); } @@ -186,8 +200,17 @@ public ItemStack.Builder getItem(HypixelPlayer p) { @Override public void run(InventoryPreClickEvent event, HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check max level + if (data.getHandBakedChocolateLevel() >= 10) { + player.sendMessage("§cYou only have so many fingers! You can't click any faster!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.HAND_BAKED_CHOCOLATE)) { - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + data = ChocolateFactoryHelper.getData(player); player.sendMessage("§7You will now produce §6+" + data.getClickPower() + " Chocolate §7per click!"); player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); } else { @@ -214,7 +237,7 @@ public ItemStack.Builder getItem(HypixelPlayer p) { lore.add(""); lore.add("§7Chocolate Per Click: §6+" + data.getClickPower() + " Chocolate"); lore.add(""); - if (level >= 9) { + if (level >= 10) { lore.add("§aYou have reached the maximum"); lore.add("§aamount of upgrades!"); } else { @@ -232,13 +255,20 @@ public ItemStack.Builder getItem(HypixelPlayer p) { } }); - // Slot 39: Time Tower (clock) + // Slot 39: Time Tower (clock) - Requires Prestige 2 set(new GUIClickableItem(39) { @Override public void run(InventoryPreClickEvent event, HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + // Check prestige requirement + if (data.getPrestigeLevel() < 1) { + player.sendMessage("§cThis requires Chocolate Factory II!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + // Right-click to activate if (event.getClick() instanceof Click.Right && data.getTimeTowerLevel() > 0 && data.getTimeTowerCharges() > 0 && !data.isTimeTowerActive()) { if (ChocolateFactoryHelper.activateTimeTower(player)) { @@ -249,11 +279,12 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { } // Left-click to upgrade - if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { - player.sendMessage("§7You upgraded to §dTime Tower " + toRoman(data.getTimeTowerLevel() + 1) + "§7!"); - player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); - } else if (data.getTimeTowerLevel() >= 15) { + if (data.getTimeTowerLevel() >= 15) { player.sendMessage("§cThe Time Tower is already at its maximum level!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } else if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { + player.sendMessage("§7You upgraded to §dTime Tower " + toRoman(data.getTimeTowerLevel() + 1) + "§7!"); + player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); } else { player.sendMessage("§cYou don't have enough Chocolate!"); player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); @@ -264,8 +295,18 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { public ItemStack.Builder getItem(HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check prestige requirement - requires Prestige 2 (index 1) + if (data.getPrestigeLevel() < 1) { + List lore = new ArrayList<>(); + lore.add("§7What does it do? Nobody knows..."); + lore.add(""); + lore.add("§cChocolate Factory II"); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } + int level = data.getTimeTowerLevel(); - long cost = ChocolateFactoryHelper.getTimeTowerCost(level); + long cost = ChocolateFactoryHelper.getTimeTowerCost(level, data.getPrestigeLevel()); boolean canAfford = data.getChocolate() >= cost; boolean isActive = data.isTimeTowerActive(); @@ -306,14 +347,30 @@ public ItemStack.Builder getItem(HypixelPlayer p) { } }); - // Slot 41: Rabbit Shrine (rabbit foot) + // Slot 41: Rabbit Shrine (rabbit foot) - Requires Prestige 3 set(new GUIClickableItem(41) { @Override public void run(InventoryPreClickEvent event, HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check prestige requirement + if (data.getPrestigeLevel() < 2) { + player.sendMessage("§cThis requires Chocolate Factory III!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + + // Check max level + if (data.getRabbitShrineLevel() >= 20) { + player.sendMessage("§cYour Rabbit Shrine is already at its maximum level!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.RABBIT_SHRINE)) { player.sendMessage("§aUpgraded Rabbit Shrine!"); - player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); } else { player.sendMessage("§cYou don't have enough Chocolate!"); player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); @@ -324,6 +381,16 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { public ItemStack.Builder getItem(HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check prestige requirement - requires Prestige 3 (index 2) + if (data.getPrestigeLevel() < 2) { + List lore = new ArrayList<>(); + lore.add("§7What does it do? Nobody knows..."); + lore.add(""); + lore.add("§cChocolate Factory III"); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } + int level = data.getRabbitShrineLevel(); long cost = ChocolateFactoryHelper.getRabbitShrineCost(level); boolean canAfford = data.getChocolate() >= cost; @@ -353,14 +420,30 @@ public ItemStack.Builder getItem(HypixelPlayer p) { } }); - // Slot 42: Coach Jackrabbit (player head) + // Slot 42: Coach Jackrabbit (player head) - Requires Prestige 4 set(new GUIClickableItem(42) { @Override public void run(InventoryPreClickEvent event, HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check prestige requirement + if (data.getPrestigeLevel() < 3) { + player.sendMessage("§cThis requires Chocolate Factory IV!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + + // Check max level + if (data.getCoachJackrabbitLevel() >= 20) { + player.sendMessage("§cCoach Jackrabbit has already taught you all that he can teach!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.COACH_JACKRABBIT)) { player.sendMessage("§aUpgraded Coach Jackrabbit!"); - player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); } else { player.sendMessage("§cYou don't have enough Chocolate!"); player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); @@ -371,6 +454,16 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { public ItemStack.Builder getItem(HypixelPlayer p) { SkyBlockPlayer player = (SkyBlockPlayer) p; DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Check prestige requirement - requires Prestige 4 (index 3) + if (data.getPrestigeLevel() < 3) { + List lore = new ArrayList<>(); + lore.add("§7What does it do? Nobody knows..."); + lore.add(""); + lore.add("§cChocolate Factory IV"); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } + int level = data.getCoachJackrabbitLevel(); long cost = ChocolateFactoryHelper.getCoachJackrabbitCost(level); boolean canAfford = data.getChocolate() >= cost; @@ -381,7 +474,7 @@ public ItemStack.Builder getItem(HypixelPlayer p) { lore.add(""); lore.add("§7§dCoach Jackrabbit §7is a motivational"); lore.add("§7speaker that is helping you reach"); - lore.add("§7your full potential by granting §6+" + String.format("%.1fx", multiplierBonus)); + lore.add("§7your full potential by granting §6+" + String.format("%.2fx", multiplierBonus)); lore.add("§6Chocolate §7per second!"); lore.add(""); if (level >= 20) { @@ -602,6 +695,13 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { DatapointChocolateFactory.EmployeeData existingEmployee = data.getEmployees().get(employeeName); + // Check if employee is already at max level (220) + if (existingEmployee != null && existingEmployee.getLevel() >= 220) { + player.sendMessage("§b" + employeeName + " §ccannot ascend the corporate ladder any further!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } + // Check if employee is not hired yet - show special message if (existingEmployee == null) { long cost = ChocolateFactoryHelper.getEmployeeCost(employeeName, 1); @@ -621,7 +721,7 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { String rankColor = "§" + getEmployeeRankColor(level); player.sendMessage(rankColor + employeeName + " §7has been promoted to §7[" + level + "§7] " + rankColor + rank + "§7!"); - player.playSound(Sound.sound(Key.key("entity.player.levelup"), Sound.Source.PLAYER, 1.0f, 1.0f)); + player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); } else { player.sendMessage("§cYou don't have enough Chocolate!"); player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); From 657a7f4946e9638091093db60fba6f6178d74128 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 12:21:44 -0300 Subject: [PATCH 04/13] Hoppitys Collection --- .../type/generic/entity/npc/HypixelNPC.java | 18 +- .../npc/configuration/NPCConfiguration.java | 11 + .../gui/v2/event/ActionInventoryOpen.java | 8 +- .../type/hub/npcs/ChocolateFactoryRank.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitBro.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitCousin.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitDaddy.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitDog.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitGranny.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitSis.java | 15 +- .../swofty/type/hub/npcs/NPCRabbitUncle.java | 15 +- .../ChocolateFactoryProductionLoop.java | 47 +- .../chocolatefactory/ChocolateMilestone.java | 159 ++ .../chocolatefactory/ChocolateRabbit.java | 683 +++++++++ .../ChocolateShopMilestone.java | 159 ++ .../datapoints/DatapointChocolateFactory.java | 66 +- .../gui/inventories/GUIChocolateFactory.java | 1363 ++++++++--------- .../GUIChocolateFactoryMilestones.java | 240 +++ .../gui/inventories/GUIChocolateShop.java | 243 +++ .../GUIChocolateShopMilestones.java | 240 +++ .../gui/inventories/GUIHoppityCollection.java | 481 ++++++ 21 files changed, 3051 insertions(+), 787 deletions(-) create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java 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 9b5d18592..300dd8a06 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 @@ -257,10 +257,20 @@ public void sendNPCMessage(HypixelPlayer player, String message) { 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); - } + public void sendNPCMessage(HypixelPlayer player, String message, Sound sound) { + String displayName = getDisplayName(player); + player.sendMessage("§e[NPC] " + displayName + "§f: " + message); + player.playSound(sound); + } + + /** + * Gets the display name for this NPC for a specific player. + * Uses per-player chatName if available, otherwise falls back to default name. + */ + public String getDisplayName(HypixelPlayer player) { + String perPlayerName = parameters.chatName(player); + return perPlayerName != null ? perPlayerName : getName(); + } protected DialogueController dialogue() { return dialogueController; diff --git a/type.generic/src/main/java/net/swofty/type/generic/entity/npc/configuration/NPCConfiguration.java b/type.generic/src/main/java/net/swofty/type/generic/entity/npc/configuration/NPCConfiguration.java index 76a7c0db3..78cebc545 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/entity/npc/configuration/NPCConfiguration.java +++ b/type.generic/src/main/java/net/swofty/type/generic/entity/npc/configuration/NPCConfiguration.java @@ -25,6 +25,17 @@ default String chatName() { return null; } + /** + * Gets the chat name for this NPC specific to a player. + * Override this for NPCs that need per-player colored names. + * @param player The player viewing this NPC + * @return The formatted chat name, or null to use default + */ + @Nullable + default String chatName(HypixelPlayer player) { + return chatName(); + } + default Instance instance() { return HypixelConst.getInstanceContainer(); } diff --git a/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java b/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java index 075f5285d..d80e652ae 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java +++ b/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java @@ -6,6 +6,7 @@ import net.swofty.type.generic.event.HypixelEvent; import net.swofty.type.generic.event.HypixelEventClass; import net.swofty.type.generic.gui.v2.ViewNavigator; +import net.swofty.type.generic.gui.v2.ViewSession; import net.swofty.type.generic.user.HypixelPlayer; public class ActionInventoryOpen implements HypixelEventClass { @@ -14,7 +15,12 @@ public class ActionInventoryOpen implements HypixelEventClass { public void onPlayerInventoryOpen(InventoryOpenEvent event) { MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { HypixelPlayer player = (HypixelPlayer) event.getPlayer(); - ViewNavigator.find(player).ifPresent(navigator -> navigator.getCurrentSession().onOpenEvent(event)); + ViewNavigator.find(player).ifPresent(navigator -> { + ViewSession session = navigator.getCurrentSession(); + if (session != null) { + session.onOpenEvent(event); + } + }); }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java index 7b2c97244..ad8197426 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java @@ -33,20 +33,29 @@ public String getColor() { } /** - * Gets the formatted hologram line for this rank. + * Gets the formatted hologram line for this rank using the rank's base level. * Format: §7[Lvl X] §{color}RankName * For UNEMPLOYED: §cUnemployed (no level) */ public String getHologramLine() { + return getHologramLine(level); + } + + /** + * Gets the formatted hologram line for this rank with a specific level. + * Format: §7[Lvl X] §{color}RankName + * For UNEMPLOYED: §cUnemployed (no level) + */ + public String getHologramLine(int actualLevel) { if (this == UNEMPLOYED) { return color + name; } - return "§7[Lvl " + level + "] " + color + name; + return "§7[" + actualLevel + "] " + color + name; } /** * Gets the formatted chat name for this rank. - * Format: §{color}RankName + * Format: §{color}NpcName */ public String getChatName(String npcName) { return color + npcName; diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java index 016d90ad6..0bafdf0b5 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitBro.java @@ -30,7 +30,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -60,6 +60,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java index 7d96f632e..7ba0f17e9 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitCousin.java @@ -33,7 +33,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -63,6 +63,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java index c5e1dd710..58664826c 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDaddy.java @@ -33,7 +33,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -63,6 +63,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java index 4630c1c50..87dfafc3f 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitDog.java @@ -35,7 +35,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -65,6 +65,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java index f7a9097e3..6bdb9869d 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitGranny.java @@ -33,7 +33,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -63,6 +63,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java index 837a83b08..aa03bd006 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitSis.java @@ -33,7 +33,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -63,6 +63,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java index a7a5a4884..1d3cc10a1 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCRabbitUncle.java @@ -33,7 +33,7 @@ public String[] holograms(HypixelPlayer player) { DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); if (employee != null) { ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); - return new String[]{rank.getHologramLine(), rank.getChatName(NPC_NAME), "§e§lCLICK"}; + return new String[]{rank.getHologramLine(employee.getLevel()), rank.getChatName(NPC_NAME), "§e§lCLICK"}; } } return new String[]{RANK.getHologramLine(), RANK.getChatName(NPC_NAME), "§e§lCLICK"}; @@ -63,6 +63,19 @@ public boolean looking(HypixelPlayer player) { public String chatName() { return RANK.getChatName(NPC_NAME); } + + @Override + public String chatName(HypixelPlayer player) { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(skyBlockPlayer); + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(NPC_NAME); + if (employee != null) { + ChocolateFactoryRank rank = ChocolateFactoryRank.fromLevel(employee.getLevel()); + return rank.getChatName(NPC_NAME); + } + } + return RANK.getChatName(NPC_NAME); + } }); } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java index e96bde5c0..4b12b640a 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java @@ -1,30 +1,39 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; -import net.minestom.server.MinecraftServer; -import net.minestom.server.timer.TaskSchedule; -import net.swofty.type.skyblockgeneric.SkyBlockGenericLoader; - /** - * Handles periodic chocolate production updates for all online players. - * This runs every second to update chocolate amounts based on production rates. + * @deprecated Production is now calculated on-demand based on time elapsed since last update. + * This approach: + * - Works for offline production (calculates when player logs in or opens GUI) + * - Is more efficient (no constant ticking for all players) + * - Matches how Hypixel handles chocolate production + * + * Production is calculated in: + * - GUIChocolateFactory.setItems() - when GUI opens + * - GUIChocolateFactory.refreshItems() - while GUI is open + * - Any purchase/interaction that needs current chocolate amount + * + * The calculation uses lastUpdated timestamp to compute elapsed time and apply production rate. */ +@Deprecated public class ChocolateFactoryProductionLoop { /** - * Starts the chocolate factory production loop. - * Should be called during server initialization. + * @deprecated No longer needed. Production is calculated on-demand. + * This method is kept for backwards compatibility but does nothing. */ + @Deprecated public static void start() { - MinecraftServer.getSchedulerManager().submitTask(() -> { - // Update chocolate production for all online players - SkyBlockGenericLoader.getLoadedPlayers().forEach(player -> { - try { - ChocolateFactoryHelper.updateProduction(player); - } catch (Exception e) { - // Silently ignore errors to prevent loop from stopping - } - }); - return TaskSchedule.seconds(1); - }); + // Production is now calculated on-demand when: + // 1. Player opens Chocolate Factory GUI + // 2. GUI refreshes while open + // 3. Any chocolate-related action occurs + // + // This is handled by ChocolateFactoryHelper.updateProduction() + // which uses time-based calculation from lastUpdated timestamp. + // + // Benefits: + // - Works offline (production accumulates based on time) + // - More efficient (no server tick overhead) + // - Matches Hypixel's implementation } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java new file mode 100644 index 000000000..8da1d6ad8 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java @@ -0,0 +1,159 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import net.minestom.server.item.Material; + +/** + * Represents chocolate factory milestones. + * Each milestone unlocks a special rabbit when reaching a certain all-time chocolate threshold. + */ +public enum ChocolateMilestone { + // Common milestones (White) + MILESTONE_1(1, 1_000L, "Almond Amaretto Rabbit", "f", 1, 0.002, + "b1b93d4e792bbf1522915b4c5f0444659fa3614c45a2b2922e61a888e61c714e", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_2(2, 10_000L, "Buttercream Blossom Rabbit", "f", 1, 0.002, + "137f6bd5e7c4e8fb22c88eb09537079bd3d4c144cf5a376ce6f2030110bd1680", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_3(3, 100_000L, "Cocoa Comet Rabbit", "f", 1, 0.002, + "6b38fa0a62113539b18fc31d1e04be34d47b7d500ef790b53f83d81c479ed29d", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_4(4, 1_000_000L, "Dulce Drizzle Rabbit", "f", 1, 0.002, + "e9eb72f7ed9e8c37a4c9ed9e8b9afc77f6ca4e01f2a76ebf9a8e8f7b2c3d4e5f", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_5(5, 2_500_000L, "Espresso Eclair Rabbit", "f", 1, 0.002, + "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", Material.WHITE_STAINED_GLASS_PANE), + + // Uncommon milestones (Lime/Green) + MILESTONE_6(6, 5_000_000L, "Fudge Fountain Rabbit", "a", 2, 0.003, + "c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_7(7, 10_000_000L, "Ginger Glaze Rabbit", "a", 2, 0.003, + "d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_8(8, 25_000_000L, "Honey Hazelnut Rabbit", "a", 2, 0.003, + "e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_9(9, 50_000_000L, "Icing Ivy Rabbit", "a", 2, 0.003, + "f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_10(10, 100_000_000L, "Jasmine Jello Rabbit", "a", 2, 0.003, + "a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3", Material.LIME_STAINED_GLASS_PANE), + + // Rare milestones (Blue) + MILESTONE_11(11, 250_000_000L, "Kiwi Kiss Rabbit", "9", 4, 0.004, + "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_12(12, 500_000_000L, "Lavender Lemon Rabbit", "9", 4, 0.004, + "c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_13(13, 1_000_000_000L, "Maple Mirage Rabbit", "9", 4, 0.004, + "d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_14(14, 2_500_000_000L, "Nougat Nebula Rabbit", "9", 4, 0.004, + "e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_15(15, 5_000_000_000L, "Orange Obsidian Rabbit", "9", 4, 0.004, + "f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3", Material.BLUE_STAINED_GLASS_PANE), + + // Epic milestones (Purple) + MILESTONE_16(16, 10_000_000_000L, "Peppermint Pearl Rabbit", "5", 10, 0.005, + "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_17(17, 25_000_000_000L, "Quince Quark Rabbit", "5", 10, 0.005, + "b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_18(18, 50_000_000_000L, "Raspberry Ripple Rabbit", "5", 10, 0.005, + "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_19(19, 100_000_000_000L, "Saffron Swirl Rabbit", "5", 10, 0.005, + "d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_20(20, 150_000_000_000L, "Toffee Tulip Rabbit", "5", 10, 0.005, + "e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", Material.PURPLE_STAINED_GLASS_PANE), + + // Legendary milestones (Orange) - These only give multiplier, no flat chocolate + MILESTONE_21(21, 200_000_000_000L, "Ube Unicorn Rabbit", "6", 0, 0.02, + "f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4", Material.ORANGE_STAINED_GLASS_PANE), + MILESTONE_22(22, 300_000_000_000L, "Vanilla Vortex Rabbit", "6", 0, 0.02, + "a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5", Material.ORANGE_STAINED_GLASS_PANE), + MILESTONE_23(23, 400_000_000_000L, "Walnut Whirl Rabbit", "6", 0, 0.02, + "b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5", Material.ORANGE_STAINED_GLASS_PANE), + MILESTONE_24(24, 500_000_000_000L, "Xocolatl Xenon Rabbit", "6", 0, 0.02, + "c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5", Material.ORANGE_STAINED_GLASS_PANE); + + private final int number; + private final long requiredChocolate; + private final String rabbitName; + private final String colorCode; + private final int chocolateBonus; + private final double multiplierBonus; + private final String textureId; + private final Material glassPaneMaterial; + + ChocolateMilestone(int number, long requiredChocolate, String rabbitName, String colorCode, + int chocolateBonus, double multiplierBonus, String textureId, Material glassPaneMaterial) { + this.number = number; + this.requiredChocolate = requiredChocolate; + this.rabbitName = rabbitName; + this.colorCode = colorCode; + this.chocolateBonus = chocolateBonus; + this.multiplierBonus = multiplierBonus; + this.textureId = textureId; + this.glassPaneMaterial = glassPaneMaterial; + } + + public int getNumber() { + return number; + } + + public long getRequiredChocolate() { + return requiredChocolate; + } + + public String getRabbitName() { + return rabbitName; + } + + public String getColorCode() { + return colorCode; + } + + public int getChocolateBonus() { + return chocolateBonus; + } + + public double getMultiplierBonus() { + return multiplierBonus; + } + + public String getTextureId() { + return textureId; + } + + public Material getGlassPaneMaterial() { + return glassPaneMaterial; + } + + public String getRomanNumeral() { + return toRoman(number); + } + + public String getFormattedRequirement() { + return ChocolateFactoryHelper.formatChocolate(requiredChocolate); + } + + public boolean isUnlocked(long allTimeChocolate) { + return allTimeChocolate >= requiredChocolate; + } + + public double getProgress(long allTimeChocolate) { + if (allTimeChocolate >= requiredChocolate) return 100.0; + return (allTimeChocolate / (double) requiredChocolate) * 100.0; + } + + public static ChocolateMilestone fromNumber(int number) { + for (ChocolateMilestone milestone : values()) { + if (milestone.number == number) { + return milestone; + } + } + return null; + } + + private static String toRoman(int number) { + if (number <= 0) return "I"; + String[] thousands = {"", "M", "MM", "MMM"}; + String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[number / 1000] + + hundreds[(number % 1000) / 100] + + tens[(number % 100) / 10] + + ones[number % 10]; + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java new file mode 100644 index 000000000..999b9cd3e --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java @@ -0,0 +1,683 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +/** + * Represents all chocolate rabbits that can be collected in Hoppity's Collection. + * Total: 513 rabbits across all rarities. + */ +public enum ChocolateRabbit { + // ==================== COMMON RABBITS (223) ==================== + AARON("Aaron", Rarity.COMMON, null, null, null), + ABLE("Able", Rarity.COMMON, null, null, null), + ACKER("Acker", Rarity.COMMON, null, null, null), + ALBUS("Albus", Rarity.COMMON, null, "Crimson Isle", null), + ALFIE("Alfie", Rarity.COMMON, null, null, null), + ALICE("Alice", Rarity.COMMON, null, null, null), + ALMOND_AMARETTO("Almond Amaretto", Rarity.COMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + ALPHA("Alpha", Rarity.COMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + ANGUS("Angus", Rarity.COMMON, null, null, null), + ANNABELLE("Annabelle", Rarity.COMMON, null, null, null), + ARCHIE("Archie", Rarity.COMMON, null, null, null), + ARNIE("Arnie", Rarity.COMMON, null, null, null), + AUDI("Audi", Rarity.COMMON, null, null, null), + AUGUSTUS("Augustus", Rarity.COMMON, null, null, null), + BABY("Baby", Rarity.COMMON, null, null, null), + BADGER("Badger", Rarity.COMMON, null, null, null), + BAGEL("Bagel", Rarity.COMMON, null, null, null), + BALDWIN("Baldwin", Rarity.COMMON, null, null, null), + BALOO("Baloo", Rarity.COMMON, null, null, null), + BARNEY("Barney", Rarity.COMMON, null, null, null), + BARTHOLOMEW("Bartholomew", Rarity.COMMON, null, null, null), + BASKET("Basket", Rarity.COMMON, null, null, null), + BAXTER("Baxter", Rarity.COMMON, null, null, null), + BAYOU("Bayou", Rarity.COMMON, null, "Backwater Bayou", "Find §a15 §7unique egg locations in the §2⏣ Backwater Bayou§7."), + BEATRICE("Beatrice", Rarity.COMMON, null, null, null), + BERTHA("Bertha", Rarity.COMMON, null, null, null), + BIBSY("Bibsy", Rarity.COMMON, null, null, null), + BILLY("Billy", Rarity.COMMON, null, null, null), + BINDI("Bindi", Rarity.COMMON, null, null, null), + BINKY("Binky", Rarity.COMMON, null, null, null), + BLAKE("Blake", Rarity.COMMON, null, null, null), + BOB("Bob", Rarity.COMMON, null, null, null), + BRAMBLE("Bramble", Rarity.COMMON, null, null, null), + BREEZE("Breeze", Rarity.COMMON, null, null, null), + BRIAN("Brian", Rarity.COMMON, null, null, null), + BRIE("Brie", Rarity.COMMON, null, null, null), + BRONSON("Bronson", Rarity.COMMON, null, null, null), + BROOKS("Brooks", Rarity.COMMON, null, null, null), + BRUCE("Bruce", Rarity.COMMON, null, null, null), + BRUNO("Bruno", Rarity.COMMON, null, null, null), + BUD("Bud", Rarity.COMMON, null, null, null), + BUGSTER("Bugster", Rarity.COMMON, null, null, null), + BUGSY("Bugsy", Rarity.COMMON, null, null, null), + BUTTERCREAM_BLOSSOM("Buttercream Blossom", Rarity.COMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + CADET("Cadet", Rarity.COMMON, null, null, null), + CALLIE("Callie", Rarity.COMMON, null, null, null), + CARROT("Carrot", Rarity.COMMON, null, null, "Kill a §cRabbit Sprite Carrot King§7."), + CAVE("Cave", Rarity.COMMON, null, "Deep Caverns", "Find §a15 §7unique egg locations in the §b⏣ Deep Caverns§7."), + CHASE("Chase", Rarity.COMMON, null, null, null), + CHESTER("Chester", Rarity.COMMON, null, null, null), + CHIP("Chip", Rarity.COMMON, null, null, null), + CHOMPER("Chomper", Rarity.COMMON, null, null, null), + CHOMPSKY("Chompsky", Rarity.COMMON, null, null, null), + CLAUDE("Claude", Rarity.COMMON, null, null, null), + COCOA_COMET("Cocoa Comet", Rarity.COMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + COLLIN("Collin", Rarity.COMMON, null, null, null), + COPPER("Copper", Rarity.COMMON, null, null, null), + COTTONTAIL("Cottontail", Rarity.COMMON, null, null, null), + CRICKET("Cricket", Rarity.COMMON, null, null, null), + CRUSH("Crush", Rarity.COMMON, null, null, "Find §a100 §7duplicate rabbits."), + CUDDLES("Cuddles", Rarity.COMMON, null, null, null), + CUPCAKE("Cupcake", Rarity.COMMON, null, null, null), + DELBOY("Delboy", Rarity.COMMON, null, null, null), + DELILAH("Delilah", Rarity.COMMON, null, null, null), + DEMI("Demi", Rarity.COMMON, null, null, null), + DIGGER("Digger", Rarity.COMMON, null, null, null), + DUCHESS("Duchess", Rarity.COMMON, null, null, null), + DULCE_DRIZZLE("Dulce Drizzle", Rarity.COMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + DUSTY("Dusty", Rarity.COMMON, null, null, null), + ELLIE("Ellie", Rarity.COMMON, null, null, null), + EMBER("Ember", Rarity.COMMON, null, "Crimson Isle", "Find §a15 §7unique egg locations in the §c⏣ Crimson Isle§7."), + EMERSON("Emerson", Rarity.COMMON, null, null, null), + EMMA("Emma", Rarity.COMMON, null, "Deep Caverns", null), + ESPRESSO_ECLAIR("Espresso Eclair", Rarity.COMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + ETA("Eta", Rarity.COMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + FERGIE("Fergie", Rarity.COMMON, null, null, null), + FIEVEL("Fievel", Rarity.COMMON, null, null, null), + FLUFFY("Fluffy", Rarity.COMMON, null, null, null), + FRANCINE("Francine", Rarity.COMMON, null, null, null), + FRANK("Frank", Rarity.COMMON, null, null, null), + FRANKIE("Frankie", Rarity.COMMON, null, null, null), + FUDGE("Fudge", Rarity.COMMON, null, null, null), + FUZZY("Fuzzy", Rarity.COMMON, null, null, null), + GEORGE("George", Rarity.COMMON, null, null, null), + GINGER("Ginger", Rarity.COMMON, null, null, null), + GINNY("Ginny", Rarity.COMMON, null, null, null), + GIZMO("Gizmo", Rarity.COMMON, null, null, null), + GLORIA("Gloria", Rarity.COMMON, null, null, null), + GOUDA("Gouda", Rarity.COMMON, null, null, null), + GRACIE("Gracie", Rarity.COMMON, null, null, null), + GUINNESS("Guinness", Rarity.COMMON, null, null, null), + GUNTHER("Gunther", Rarity.COMMON, null, null, null), + HADLEY("Hadley", Rarity.COMMON, null, null, null), + HARLAND("Harland", Rarity.COMMON, null, "Spider's Den", null), + HARLEY("Harley", Rarity.COMMON, null, null, null), + HEFNER("Hefner", Rarity.COMMON, null, null, null), + HEIDIE("Heidie", Rarity.COMMON, null, null, null), + HERBIE("Herbie", Rarity.COMMON, null, null, null), + HERSHEY("Hershey", Rarity.COMMON, null, null, null), + HONDO("Hondo", Rarity.COMMON, null, null, null), + HOPPER("Hopper", Rarity.COMMON, null, null, null), + HUBBY("Hubby", Rarity.COMMON, null, "Hub", "Find §a15 §7unique egg locations in the §bHub§7."), + HUCK("Huck", Rarity.COMMON, null, null, null), + HUGO("Hugo", Rarity.COMMON, null, null, null), + HUMPHREY("Humphrey", Rarity.COMMON, null, null, null), + HUNTER("Hunter", Rarity.COMMON, null, null, null), + IGGY("Iggy", Rarity.COMMON, null, null, null), + INDIE("Indie", Rarity.COMMON, null, null, null), + JACQUELINE("Jacqueline", Rarity.COMMON, null, "The Park", null), + JADE("Jade", Rarity.COMMON, null, "Crystal Hollows", "Find §a15 §7unique egg locations in the §5⏣ Crystal Hollows§7."), + JAKE("Jake", Rarity.COMMON, null, null, null), + JAMES("James", Rarity.COMMON, null, null, null), + JAMMER("Jammer", Rarity.COMMON, null, null, null), + JASMINE("Jasmine", Rarity.COMMON, null, null, null), + JAZMIN("Jazmin", Rarity.COMMON, null, null, null), + JEFFERY("Jeffery", Rarity.COMMON, null, null, null), + JERRY("Jerry", Rarity.COMMON, null, null, "Kill a §6Villager Sprite Golden Jerry§7."), + JOEY("Joey", Rarity.COMMON, null, null, null), + JONAH("Jonah", Rarity.COMMON, null, null, null), + JOSEPHINE("Josephine", Rarity.COMMON, null, null, null), + KRONK("Kronk", Rarity.COMMON, null, "Crystal Hollows", null), + LAZY("Lazy", Rarity.COMMON, null, "Gold Mine", null), + LENNY("Lenny", Rarity.COMMON, null, null, null), + LILY("Lily", Rarity.COMMON, null, null, null), + LONE_RANGER("Lone Ranger", Rarity.COMMON, null, null, null), + LOTTE("Lotte", Rarity.COMMON, null, null, null), + LOUIE("Louie", Rarity.COMMON, null, null, null), + MANDY("Mandy", Rarity.COMMON, null, null, null), + MARLOW("Marlow", Rarity.COMMON, null, null, null), + MARSHALL("Marshall", Rarity.COMMON, null, "Galatea", null), + MAUI("Maui", Rarity.COMMON, null, null, null), + MAX("Max", Rarity.COMMON, null, null, null), + MICKEY("Mickey", Rarity.COMMON, null, null, null), + MILES("Miles", Rarity.COMMON, null, null, null), + MILLY("Milly", Rarity.COMMON, null, null, null), + MIMI("Mimi", Rarity.COMMON, null, "Dwarven Mines", "Find §a15 §7unique egg locations in the §2⏣ Dwarven Mines§7."), + MITE("Mite", Rarity.COMMON, null, "The End", null), + MOCHI("Mochi", Rarity.COMMON, null, null, null), + MOLLY("Molly", Rarity.COMMON, null, null, null), + MONA("Mona", Rarity.COMMON, null, null, null), + MOODY("Moody", Rarity.COMMON, null, null, null), + MOOKIE("Mookie", Rarity.COMMON, null, null, null), + MOPSY("Mopsy", Rarity.COMMON, null, null, null), + MORRIS("Morris", Rarity.COMMON, null, null, null), + MORTY("Morty", Rarity.COMMON, null, "Dungeon Hub", "Find §a15 §7unique egg locations in the §c⏣ Dungeon Hub§7."), + MOTFER("Motfer", Rarity.COMMON, null, "Dwarven Mines", null), + NATALIE("Natalie", Rarity.COMMON, null, null, null), + NED("Ned", Rarity.COMMON, null, null, null), + NIBBLES("Nibbles", Rarity.COMMON, null, null, null), + NIKO("Niko", Rarity.COMMON, null, null, null), + NIZA("Niza", Rarity.COMMON, null, null, null), + NU("Nu", Rarity.COMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + NUTMEG("Nutmeg", Rarity.COMMON, null, null, null), + OLETTA("Oletta", Rarity.COMMON, null, null, null), + OLIVER("Oliver", Rarity.COMMON, null, null, null), + OLIVETTE("Olivette", Rarity.COMMON, null, null, null), + OLIVIER("Olivier", Rarity.COMMON, null, null, null), + OLLIE("Ollie", Rarity.COMMON, null, null, null), + ONION("Onion", Rarity.COMMON, null, "Backwater Bayou", null), + PADDY("Paddy", Rarity.COMMON, null, null, null), + PATCH("Patch", Rarity.COMMON, null, null, null), + PEBBLES("Pebbles", Rarity.COMMON, null, null, null), + PENNY("Penny", Rarity.COMMON, null, null, null), + PEONY("Peony", Rarity.COMMON, null, null, null), + PETUNIA("Petunia", Rarity.COMMON, null, null, null), + PICKLES("Pickles", Rarity.COMMON, null, null, null), + PICKY("Picky", Rarity.COMMON, null, "Gold Mine", "Find §a15 §7unique egg locations in the §6⏣ Gold Mine§7."), + PINKY("Pinky", Rarity.COMMON, null, null, null), + POPPY("Poppy", Rarity.COMMON, null, null, null), + PORTER("Porter", Rarity.COMMON, null, null, null), + POTATO("Potato", Rarity.COMMON, null, "The Farming Islands", null), + QUENTIN("Quentin", Rarity.COMMON, null, null, null), + RAZZIE("Razzie", Rarity.COMMON, null, null, null), + REGINALD("Reginald", Rarity.COMMON, null, null, null), + REMI("Remi", Rarity.COMMON, null, null, null), + RESSIE("Ressie", Rarity.COMMON, null, null, null), + RICKY("Ricky", Rarity.COMMON, null, null, null), + RILEY("Riley", Rarity.COMMON, null, null, null), + ROGUE("Rogue", Rarity.COMMON, null, "Hub", null), + ROLF("Rolf", Rarity.COMMON, null, null, null), + ROSCO("Rosco", Rarity.COMMON, null, null, null), + ROSS("Ross", Rarity.COMMON, null, null, null), + ROWDY("Rowdy", Rarity.COMMON, null, null, null), + RUBEN("Ruben", Rarity.COMMON, null, null, null), + RUPERT("Rupert", Rarity.COMMON, null, null, null), + RYDER("Ryder", Rarity.COMMON, null, null, null), + SASSY("Sassy", Rarity.COMMON, null, null, null), + SCOOTER("Scooter", Rarity.COMMON, null, null, null), + SCOTCH("Scotch", Rarity.COMMON, null, null, null), + SCOUT("Scout", Rarity.COMMON, null, null, null), + SCUBA("Scuba", Rarity.COMMON, null, null, null), + SELENE("Selene", Rarity.COMMON, null, null, null), + SKIPPE("Skippe", Rarity.COMMON, null, null, null), + SMOKEY("Smokey", Rarity.COMMON, null, null, null), + SNIFFLES("Sniffles", Rarity.COMMON, null, null, null), + SNOPPY("Snoppy", Rarity.COMMON, null, null, null), + SNUFFY("Snuffy", Rarity.COMMON, null, null, null), + SOON("Soon", Rarity.COMMON, null, "Dungeon Hub", null), + SOPHIE("Sophie", Rarity.COMMON, null, null, null), + SORBET("Sorbet", Rarity.COMMON, null, null, null), + SPENCER("Spencer", Rarity.COMMON, null, null, null), + SPOT("Spot", Rarity.COMMON, null, null, null), + STANLEY("Stanley", Rarity.COMMON, null, null, null), + STINKY("Stinky", Rarity.COMMON, null, "The Farming Islands", "Find §a15 §7unique egg locations in §eThe Farming Islands§7."), + STRIKER("Striker", Rarity.COMMON, null, "Spider's Den", null), + STUART("Stuart", Rarity.COMMON, null, null, null), + SURI("Suri", Rarity.COMMON, null, null, null), + TAGALONG("Tagalong", Rarity.COMMON, null, null, null), + TAU("Tau", Rarity.COMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + TEDDY("Teddy", Rarity.COMMON, null, null, null), + THALAI("Thalai", Rarity.COMMON, null, null, null), + THEO("Theo", Rarity.COMMON, null, null, null), + THEODORE("Theodore", Rarity.COMMON, null, null, null), + THUMPER("Thumper", Rarity.COMMON, null, null, null), + TICKY("Ticky", Rarity.COMMON, null, null, null), + TOBI("Tobi", Rarity.COMMON, null, null, null), + WEBB("Webb", Rarity.COMMON, null, "Spider's Den", "Find §a15 §7unique egg locations in the §c⏣ Spider's Den§7."), + WILLIAM("William", Rarity.COMMON, null, null, null), + WINSTON("Winston", Rarity.COMMON, null, null, null), + WOODY("Woody", Rarity.COMMON, null, "The Park", "Find §a15 §7unique egg locations in the §a⏣ The Park§7."), + ZACK("Zack", Rarity.COMMON, null, null, null), + ZEA("Zea", Rarity.COMMON, null, "The End", "Find §a15 §7unique egg locations in §d⏣ The End§7."), + + // ==================== UNCOMMON RABBITS (125) ==================== + ABI("Abi", Rarity.UNCOMMON, "Obtained through getting called by §aHoppity§7.", null, null), + ABIGAIL("Abigail", Rarity.UNCOMMON, null, null, null), + ALEXA("Alexa", Rarity.UNCOMMON, null, null, null), + ALEXANDER("Alexander", Rarity.UNCOMMON, null, null, null), + ALPACA("Alpaca", Rarity.UNCOMMON, null, null, null), + AMAZON("Amazon", Rarity.UNCOMMON, null, null, null), + ARACHNO("Arachno", Rarity.UNCOMMON, null, null, "Defeat §cTarantula Broodfather Tier III§7."), + ASHES("Ashes", Rarity.UNCOMMON, null, null, null), + ASTERIX("Asterix", Rarity.UNCOMMON, null, null, null), + ATTILA("Attila", Rarity.UNCOMMON, null, "Crimson Isle", null), + AVERAGE("Average", Rarity.UNCOMMON, null, null, "Collect §a100 §7unique §fCOMMON §7Rabbits."), + AZURE("Azure", Rarity.UNCOMMON, null, "Deep Caverns", null), + BAMBAM("Bambam", Rarity.UNCOMMON, null, null, null), + BANDIT("Bandit", Rarity.UNCOMMON, null, null, null), + BARCODE("Barcode", Rarity.UNCOMMON, null, null, null), + BENJI("Benji", Rarity.UNCOMMON, null, null, null), + BETA("Beta", Rarity.UNCOMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + BILBO("Bilbo", Rarity.UNCOMMON, null, null, null), + BLOSSOM("Blossom", Rarity.UNCOMMON, null, null, null), + BLUEBERRY("Blueberry", Rarity.UNCOMMON, null, null, null), + BRICK("Brick", Rarity.UNCOMMON, null, "Gold Mine", null), + BRUTUS("Brutus", Rarity.UNCOMMON, null, null, null), + BUBBLES("Bubbles", Rarity.UNCOMMON, null, null, null), + BUCKWHEAT("Buckwheat", Rarity.UNCOMMON, null, null, null), + BUFFALO("Buffalo", Rarity.UNCOMMON, null, null, null), + BUGS("Bugs", Rarity.UNCOMMON, null, null, null), + BUMPER("Bumper", Rarity.UNCOMMON, null, null, null), + BUSTER("Buster", Rarity.UNCOMMON, null, null, null), + BUTTERS("Butters", Rarity.UNCOMMON, null, null, null), + CANDI("Candi", Rarity.UNCOMMON, null, null, null), + CARTER("Carter", Rarity.UNCOMMON, null, null, null), + CASPER("Casper", Rarity.UNCOMMON, null, null, null), + CASSIDY("Cassidy", Rarity.UNCOMMON, null, null, null), + CHARMIN("Charmin", Rarity.UNCOMMON, null, null, null), + CHEWY("Chewy", Rarity.UNCOMMON, null, null, null), + CHILLI("Chilli", Rarity.UNCOMMON, null, null, null), + CHUBBY("Chubby", Rarity.UNCOMMON, null, null, null), + CLOUDY("Cloudy", Rarity.UNCOMMON, null, null, null), + COOKIE("Cookie", Rarity.UNCOMMON, null, null, null), + COOPER("Cooper", Rarity.UNCOMMON, null, null, null), + COTTON("Cotton", Rarity.UNCOMMON, null, null, null), + COTTON_PUFF("Cotton Puff", Rarity.UNCOMMON, null, null, null), + COTTONBALL("Cottonball", Rarity.UNCOMMON, null, null, null), + DALTON("Dalton", Rarity.UNCOMMON, null, null, null), + DANDELION("Dandelion", Rarity.UNCOMMON, null, null, null), + DARLA("Darla", Rarity.UNCOMMON, null, null, null), + DASH("Dash", Rarity.UNCOMMON, null, null, null), + DEMARCUS("Demarcus", Rarity.UNCOMMON, null, null, null), + DEMETRIOUS("Demetrious", Rarity.UNCOMMON, null, null, null), + DESTINY("Destiny", Rarity.UNCOMMON, null, null, null), + DOMINO("Domino", Rarity.UNCOMMON, null, null, null), + DONKEY("Donkey", Rarity.UNCOMMON, null, "Backwater Bayou", null), + EASTWOOD("Eastwood", Rarity.UNCOMMON, null, null, null), + ELLA("Ella", Rarity.UNCOMMON, null, null, null), + ENDER("Ender", Rarity.UNCOMMON, null, "The End", null), + FIGGY("Figgy", Rarity.UNCOMMON, null, "Galatea", null), + FITCH("Fitch", Rarity.UNCOMMON, null, null, null), + FLIP_FLOP("Flip Flop", Rarity.UNCOMMON, null, null, null), + FORREST("Forrest", Rarity.UNCOMMON, null, null, null), + FUDGE_FOUNTAIN("Fudge Fountain", Rarity.UNCOMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + GADGET("Gadget", Rarity.UNCOMMON, null, null, null), + GEE_GEE("Gee-Gee", Rarity.UNCOMMON, null, null, null), + GINGER_GLAZE("Ginger Glaze", Rarity.UNCOMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + GOOFY("Goofy", Rarity.UNCOMMON, null, null, null), + GROG("Grog", Rarity.UNCOMMON, null, "Crystal Hollows", null), + HALMENROG("Halmenrog", Rarity.UNCOMMON, null, "Dwarven Mines", null), + HARMONY("Harmony", Rarity.UNCOMMON, null, null, null), + HONEY_HAZELNUT("Honey Hazelnut", Rarity.UNCOMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + HOP_A_LONG("Hop-a-long", Rarity.UNCOMMON, null, null, null), + ICING_IVY("Icing Ivy", Rarity.UNCOMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + INFERNO("Inferno", Rarity.UNCOMMON, null, null, "Defeat §cInferno Demonlord Tier III§7."), + IRENA("Irena", Rarity.UNCOMMON, null, null, null), + JASMINE_JELLO("Jasmine Jello", Rarity.UNCOMMON, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + JAZZ("Jazz", Rarity.UNCOMMON, null, null, null), + JELLY_BEAN("Jelly Bean", Rarity.UNCOMMON, null, null, null), + KOBI("Kobi", Rarity.UNCOMMON, null, null, null), + LEOPOLD("Leopold", Rarity.UNCOMMON, null, null, null), + LULU("Lulu", Rarity.UNCOMMON, null, null, null), + MAYBELLINE("Maybelline", Rarity.UNCOMMON, null, null, null), + MEDIOCRITY("Mediocrity", Rarity.UNCOMMON, null, null, "Collect §a200 §7unique §fCOMMON §7Rabbits."), + MELON("Melon", Rarity.UNCOMMON, null, "The Farming Islands", null), + MILO("Milo", Rarity.UNCOMMON, null, null, null), + MODEST("Modest", Rarity.UNCOMMON, null, null, "Collect §a50 §7unique §aUNCOMMON §7Rabbits."), + MORGAN("Morgan", Rarity.UNCOMMON, null, null, null), + OAKLEY("Oakley", Rarity.UNCOMMON, null, null, null), + OAKLY("Oakly", Rarity.UNCOMMON, null, "Hub", null), + OBELIX("Obelix", Rarity.UNCOMMON, null, null, null), + ORCHID("Orchid", Rarity.UNCOMMON, null, "The Park", null), + OREO("Oreo", Rarity.UNCOMMON, null, null, null), + OTTO("Otto", Rarity.UNCOMMON, null, null, null), + OZWALD("Ozwald", Rarity.UNCOMMON, null, null, null), + PANCAKE("Pancake", Rarity.UNCOMMON, null, null, null), + PATCHES("Patches", Rarity.UNCOMMON, null, null, null), + PENELOPE("Penelope", Rarity.UNCOMMON, null, null, null), + PEPSI("Pepsi", Rarity.UNCOMMON, null, null, null), + PILLSBURY("Pillsbury", Rarity.UNCOMMON, null, null, null), + POLKA_DOT("Polka Dot", Rarity.UNCOMMON, null, null, null), + PORSCHE("Porsche", Rarity.UNCOMMON, null, null, null), + PRESTIGE("Prestige", Rarity.UNCOMMON, null, null, "Reach §6Chocolate Factory III§7."), + PRETZEL("Pretzel", Rarity.UNCOMMON, null, null, null), + PRUDENT("Prudent", Rarity.UNCOMMON, null, null, "Collect §a100 §7unique §aUNCOMMON §7Rabbits."), + QUINCY("Quincy", Rarity.UNCOMMON, null, null, null), + RAVEN("Raven", Rarity.UNCOMMON, null, null, null), + RINGO("Ringo", Rarity.UNCOMMON, null, null, null), + RUINA("Ruina", Rarity.UNCOMMON, null, "Dungeon Hub", null), + RUSTY("Rusty", Rarity.UNCOMMON, null, null, null), + SARGENT("Sargent", Rarity.UNCOMMON, null, null, null), + SEINFIELD("Seinfield", Rarity.UNCOMMON, null, null, null), + SNOOPY("Snoopy", Rarity.UNCOMMON, null, null, null), + SPRINKLES("Sprinkles", Rarity.UNCOMMON, null, null, null), + SQUISH("Squish", Rarity.UNCOMMON, null, null, "Find §a500 §7duplicate Rabbits."), + STEWART("Stewart", Rarity.UNCOMMON, null, null, null), + SVEN("Sven", Rarity.UNCOMMON, null, null, "Defeat §9Sven Packmaster Tier III§7."), + SWEETPEA("Sweetpea", Rarity.UNCOMMON, null, null, null), + SYLVESTER("Sylvester", Rarity.UNCOMMON, null, null, null), + THETA("Theta", Rarity.UNCOMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + TOBY("Toby", Rarity.UNCOMMON, null, null, null), + TRIXIE("Trixie", Rarity.UNCOMMON, null, null, null), + UNA("Una", Rarity.UNCOMMON, null, null, null), + UPSILON("Upsilon", Rarity.UNCOMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + VOID("Void", Rarity.UNCOMMON, null, null, "Defeat §5Voidgloom Seraph Tier III§7."), + WADSWORTH("Wadsworth", Rarity.UNCOMMON, null, null, null), + WAFFLE("Waffle", Rarity.UNCOMMON, null, null, null), + XI("Xi", Rarity.UNCOMMON, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + ZOMBIE("Zombie", Rarity.UNCOMMON, null, null, "Defeat §5Revenant Horror Tier III§7."), + + // ==================== RARE RABBITS (82) ==================== + ALADDIN("Aladdin", Rarity.RARE, null, null, null), + ALOYSIUS("Aloysius", Rarity.RARE, null, null, null), + BARBIE("Barbie", Rarity.RARE, null, null, null), + BISHOP("Bishop", Rarity.RARE, null, null, null), + BLACKBERRY("Blackberry", Rarity.RARE, null, null, null), + BLACKJACK("Blackjack", Rarity.RARE, null, null, null), + BOULDER("Boulder", Rarity.RARE, null, "Gold Mine", null), + BUGATTI("Bugatti", Rarity.RARE, null, null, null), + BUN_BUN("Bun Bun", Rarity.RARE, null, null, null), + BURST("Burst", Rarity.RARE, null, null, "Find §a1,000 §7duplicate Rabbits."), + CAJUN("Cajun", Rarity.RARE, null, null, null), + CANOPUS("Canopus", Rarity.RARE, null, "The Park", null), + CARAMEL("Caramel", Rarity.RARE, null, null, null), + CARMINE("Carmine", Rarity.RARE, null, "Deep Caverns", null), + CASANOVA("Casanova", Rarity.RARE, null, null, null), + CHEVY("Chevy", Rarity.RARE, null, null, null), + CINNAMON("Cinnamon", Rarity.RARE, null, null, null), + COLTRON("Coltron", Rarity.RARE, null, "Crystal Hollows", null), + CRYSTAL("Crystal", Rarity.RARE, null, null, null), + DALLAS("Dallas", Rarity.RARE, null, null, null), + DRACO("Draco", Rarity.RARE, null, null, null), + EASTER("Easter", Rarity.RARE, null, null, null), + EGG("Egg", Rarity.RARE, null, "The End", null), + ELVIS("Elvis", Rarity.RARE, null, null, null), + FAVOR("Favor", Rarity.RARE, null, null, "Collect §a50 §7unique §9RARE §7Rabbits."), + FIGARO("Figaro", Rarity.RARE, null, null, null), + FISH_THE_RABBIT("Fish the Rabbit", Rarity.RARE, "Obtained by finding the §6Stray Rabbit §7in the Chocolate Factory menu.", null, null), + FRODO("Frodo", Rarity.RARE, null, null, null), + GAMMA("Gamma", Rarity.RARE, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + GRAVLER("Gravler", Rarity.RARE, null, "Spider's Den", null), + GREMLIN("Gremlin", Rarity.RARE, null, null, null), + HONEY("Honey", Rarity.RARE, null, null, null), + HOPE("Hope", Rarity.RARE, null, null, null), + HYDE("Hyde", Rarity.RARE, null, null, null), + IOTA("Iota", Rarity.RARE, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + JASPER("Jasper", Rarity.RARE, null, null, null), + JYNX("Jynx", Rarity.RARE, null, null, null), + KALUMDAI("Kalumdai", Rarity.RARE, null, "Dwarven Mines", null), + KIWI_KISS("Kiwi Kiss", Rarity.RARE, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + LAVENDER_LEMON("Lavender Lemon", Rarity.RARE, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + LINUS("Linus", Rarity.RARE, null, null, null), + LUMIE("Lumie", Rarity.RARE, null, "Galatea", null), + MAPLE_MIRAGE("Maple Mirage", Rarity.RARE, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + MIDNIGHT("Midnight", Rarity.RARE, null, null, null), + MONALISA("Monalisa", Rarity.RARE, null, null, null), + MURPHY("Murphy", Rarity.RARE, null, null, null), + MUSH_MUSH("Mush Mush", Rarity.RARE, null, "Dungeon Hub", null), + MUTONIO("Mutonio", Rarity.RARE, null, "The Farming Islands", null), + NEPTUNE("Neptune", Rarity.RARE, null, null, null), + NOUGAT_NEBULA("Nougat Nebula", Rarity.RARE, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + OLYMPE("Olympe", Rarity.RARE, null, null, null), + OMICRON("Omicron", Rarity.RARE, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + ONYX("Onyx", Rarity.RARE, null, null, null), + ORANGE_OBSIDIAN("Orange Obsidian", Rarity.RARE, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + ORLANDO("Orlando", Rarity.RARE, null, null, null), + PADDINGTON("Paddington", Rarity.RARE, null, null, null), + PEANUT("Peanut", Rarity.RARE, null, null, null), + PEPPER("Pepper", Rarity.RARE, null, null, null), + PHANTOM("Phantom", Rarity.RARE, null, null, null), + PHI("Phi", Rarity.RARE, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + POPCORN("Popcorn", Rarity.RARE, null, null, null), + PRIDE("Pride", Rarity.RARE, null, null, null), + PUMPKIN("Pumpkin", Rarity.RARE, null, null, null), + RIVER("River", Rarity.RARE, null, null, null), + SAGE("Sage", Rarity.RARE, null, null, null), + SHERK("Sherk", Rarity.RARE, null, "Backwater Bayou", null), + SNOWBALL("Snowball", Rarity.RARE, null, null, null), + SPIRIT("Spirit", Rarity.RARE, null, null, null), + SPOOKY("Spooky", Rarity.RARE, null, null, null), + STORMY("Stormy", Rarity.RARE, null, null, null), + SULFUR("Sulfur", Rarity.RARE, null, "Crimson Isle", null), + SUNNY("Sunny", Rarity.RARE, null, null, null), + TORNADO("Tornado", Rarity.RARE, null, null, null), + TOWNY("Towny", Rarity.RARE, null, "Hub", null), + TRICKY("Tricky", Rarity.RARE, null, null, null), + UNCLE_BUCK("Uncle Buck", Rarity.RARE, null, null, null), + VLAD("Vlad", Rarity.RARE, null, null, null), + WESSON("Wesson", Rarity.RARE, null, null, null), + WIDGET("Widget", Rarity.RARE, null, null, null), + WILLOW("Willow", Rarity.RARE, null, null, null), + ZERO("Zero", Rarity.RARE, null, null, null), + + // ==================== EPIC RABBITS (45) ==================== + ACE("Ace", Rarity.EPIC, null, null, null), + ACHILLES("Achilles", Rarity.EPIC, null, null, null), + ALPINE("Alpine", Rarity.EPIC, null, null, null), + ANGEL("Angel", Rarity.EPIC, null, null, null), + CALYPSO("Calypso", Rarity.EPIC, null, null, null), + CASTLE("Castle", Rarity.EPIC, null, "Hub", null), + CHI("Chi", Rarity.EPIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + COMET("Comet", Rarity.EPIC, null, null, null), + DELTA("Delta", Rarity.EPIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + DOJO("Dojo", Rarity.EPIC, null, "Crimson Isle", null), + EISEN("Eisen", Rarity.EPIC, null, "Crimson Isle", null), + GATSBY("Gatsby", Rarity.EPIC, null, null, null), + GROVE("Grove", Rarity.EPIC, null, "Galatea", null), + HOLE("Hole", Rarity.EPIC, null, "Gold Mine", null), + IMPLODE("Implode", Rarity.EPIC, null, null, "Find §a2,000 §7duplicate Rabbits."), + JEDI("Jedi", Rarity.EPIC, null, null, null), + KAPPA("Kappa", Rarity.EPIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + KEN("Ken", Rarity.EPIC, null, null, null), + KIERA("Kiera", Rarity.EPIC, null, null, null), + KODO("Kodo", Rarity.EPIC, null, null, null), + KRYS("Krys", Rarity.EPIC, null, "Dungeon Hub", null), + MAXIMA("Maxima", Rarity.EPIC, null, "Spider's Den", null), + MERLIN("Merlin", Rarity.EPIC, null, null, null), + NEARQUAAD("Nearquaad", Rarity.EPIC, null, "Backwater Bayou", null), + NECRON("Necron", Rarity.EPIC, null, null, "§cThe Catacombs Floor VII §7Completion"), + OCTAVIA("Octavia", Rarity.EPIC, null, "Dwarven Mines", null), + PEARL("Pearl", Rarity.EPIC, null, "The End", null), + PEPPERMINT_PEARL("Peppermint Pearl", Rarity.EPIC, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + PI("Pi", Rarity.EPIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + PRINCE("Prince", Rarity.EPIC, null, null, null), + PUNCH("Punch", Rarity.EPIC, null, null, null), + QUINCE_QUARK("Quince Quark", Rarity.EPIC, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + RAMBO("Rambo", Rarity.EPIC, null, null, null), + RASPBERRY_RIPPLE("Raspberry Ripple", Rarity.EPIC, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + ROCHIE("Rochie", Rarity.EPIC, null, "The Farming Islands", null), + SAGA("Saga", Rarity.EPIC, null, null, "Collect §a25 §7unique §5EPIC §7Rabbits."), + SAVANNAH("Savannah", Rarity.EPIC, null, "The Park", null), + SIMBA("Simba", Rarity.EPIC, null, null, null), + STRAWBERRY_SWIRL("Strawberry Swirl", Rarity.EPIC, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + THOR("Thor", Rarity.EPIC, null, null, null), + TOFFEE_TULIP("Toffee Tulip", Rarity.EPIC, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + TRIX("Trix", Rarity.EPIC, null, null, null), + TURBO("Turbo", Rarity.EPIC, null, null, null), + VIOLET("Violet", Rarity.EPIC, null, "Crystal Hollows", null), + + // ==================== LEGENDARY RABBITS (24) ==================== + APOLLO("Apollo", Rarity.LEGENDARY, null, null, null), + APRIL("April", Rarity.LEGENDARY, null, null, null), + ATLAS("Atlas", Rarity.LEGENDARY, null, null, null), + ECHO("Echo", Rarity.LEGENDARY, null, null, null), + EL_DORADO("El Dorado", Rarity.LEGENDARY, "Obtained by finding the §6Stray Rabbit §7in the Chocolate Factory menu.", null, null), + EPSILON("Epsilon", Rarity.LEGENDARY, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + EXTRA("Extra", Rarity.LEGENDARY, null, null, "Collect §a20 §7unique §6LEGENDARY §7Rabbits."), + GENERAL("General", Rarity.LEGENDARY, null, null, null), + HOUDINI("Houdini", Rarity.LEGENDARY, null, null, null), + KAEMAN("Kaeman", Rarity.LEGENDARY, null, null, "§cMaster Mode The Catacombs Floor VII §7Completion"), + LAMBDA("Lambda", Rarity.LEGENDARY, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + MAGIC("Magic", Rarity.LEGENDARY, null, null, null), + MYSTIC("Mystic", Rarity.LEGENDARY, null, null, null), + NOVA("Nova", Rarity.LEGENDARY, null, null, null), + PSI("Psi", Rarity.LEGENDARY, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + RHO("Rho", Rarity.LEGENDARY, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + SHADOW("Shadow", Rarity.LEGENDARY, null, null, null), + SOLOMON("Solomon", Rarity.LEGENDARY, null, null, null), + STORM("Storm", Rarity.LEGENDARY, null, null, null), + UBE_UNICORN("Ube Unicorn", Rarity.LEGENDARY, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + VANILLA_VORTEX("Vanilla Vortex", Rarity.LEGENDARY, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + WALNUT_WHIRL("Walnut Whirl", Rarity.LEGENDARY, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + XOCO_XANUDU("Xoco Xanudu", Rarity.LEGENDARY, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + YOGURT_YUCCA("Yogurt Yucca", Rarity.LEGENDARY, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + + // ==================== MYTHIC RABBITS (11) ==================== + DANTE("Dante", Rarity.MYTHIC, null, null, null), + EINSTEIN("Einstein", Rarity.MYTHIC, null, null, null), + GALAXY("Galaxy", Rarity.MYTHIC, null, null, null), + KING("King", Rarity.MYTHIC, null, null, null), + MU("Mu", Rarity.MYTHIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + NAPOLEON("Napoleon", Rarity.MYTHIC, null, null, null), + OMEGA("Omega", Rarity.MYTHIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + SIGMA("Sigma", Rarity.MYTHIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + ZEST_ZEPHYR("Zest Zephyr", Rarity.MYTHIC, "Obtained through your §6Chocolate Factory Milestones§7.", null, null), + ZETA("Zeta", Rarity.MYTHIC, "Obtained through your §6Chocolate Shop Milestones§7.", null, null), + ZORRO("Zorro", Rarity.MYTHIC, null, null, null), + + // ==================== DIVINE RABBITS (5) ==================== + AURORA("Aurora", Rarity.DIVINE, null, null, null), + CELESTIA("Celestia", Rarity.DIVINE, null, null, null), + ORION("Orion", Rarity.DIVINE, null, null, null), + STARFIRE("Starfire", Rarity.DIVINE, null, null, null), + VEGA("Vega", Rarity.DIVINE, null, null, null); + + private final String displayName; + private final Rarity rarity; + private final String obtainMethod; + private final String location; + private final String requirement; + + ChocolateRabbit(String displayName, Rarity rarity, String obtainMethod, String location, String requirement) { + this.displayName = displayName; + this.rarity = rarity; + this.obtainMethod = obtainMethod; + this.location = location; + this.requirement = requirement; + } + + public String getDisplayName() { + return displayName; + } + + public Rarity getRarity() { + return rarity; + } + + public String getObtainMethod() { + return obtainMethod; + } + + public String getLocation() { + return location; + } + + public String getRequirement() { + return requirement; + } + + public int getChocolateBonus() { + return rarity.getChocolateBonus(); + } + + public double getMultiplierBonus() { + return rarity.getMultiplierBonus(); + } + + public String getColorCode() { + return rarity.getColorCode(); + } + + public String getFormattedName() { + return "§" + rarity.getColorCode() + displayName; + } + + /** + * Gets the formatted location string with appropriate color + */ + public String getFormattedLocation() { + if (location == null) return null; + return switch (location) { + case "Crimson Isle" -> "§c⏣ Crimson Isle"; + case "Deep Caverns" -> "§b⏣ Deep Caverns"; + case "Backwater Bayou" -> "§2⏣ Backwater Bayou"; + case "Gold Mine" -> "§6⏣ Gold Mine"; + case "Hub" -> "§bHub"; + case "The Park" -> "§a⏣ The Park"; + case "Crystal Hollows" -> "§5⏣ Crystal Hollows"; + case "The End" -> "§d⏣ The End"; + case "Dungeon Hub" -> "§c⏣ Dungeon Hub"; + case "Dwarven Mines" -> "§2⏣ Dwarven Mines"; + case "The Farming Islands" -> "§eThe Farming Islands"; + case "Spider's Den" -> "§c⏣ Spider's Den"; + case "Galatea" -> "§2⏣ Galatea"; + default -> "§7" + location; + }; + } + + /** + * Gets the resident label with color for the location + */ + public String getResidentLabel() { + if (location == null) return null; + return switch (location) { + case "Crimson Isle" -> "§cCrimson Isle Resident"; + case "Deep Caverns" -> "§bDeep Caverns Resident"; + case "Backwater Bayou" -> "§2Backwater Bayou Resident"; + case "Gold Mine" -> "§6Gold Mine Resident"; + case "Hub" -> "§bHub Resident"; + case "The Park" -> "§aThe Park Resident"; + case "Crystal Hollows" -> "§5Crystal Hollows Resident"; + case "The End" -> "§dThe End Resident"; + case "Dungeon Hub" -> "§cDungeon Hub Resident"; + case "Dwarven Mines" -> "§2Dwarven Mines Resident"; + case "The Farming Islands" -> "§eThe Farming Islands Resident"; + case "Spider's Den" -> "§cSpider's Den Resident"; + case "Galatea" -> "§2Galatea Resident"; + default -> "§7" + location + " Resident"; + }; + } + + public static int getTotalRabbits() { + return values().length; + } + + public static int getRabbitCountByRarity(Rarity rarity) { + int count = 0; + for (ChocolateRabbit rabbit : values()) { + if (rabbit.rarity == rarity) count++; + } + return count; + } + + public enum Rarity { + COMMON("f", "COMMON", 1, 0.002), + UNCOMMON("a", "UNCOMMON", 2, 0.003), + RARE("9", "RARE", 4, 0.004), + EPIC("5", "EPIC", 10, 0.005), + LEGENDARY("6", "LEGENDARY", 0, 0.02), + MYTHIC("d", "MYTHIC", 0, 0.05), + DIVINE("b", "DIVINE", 0, 0.03); + + private final String colorCode; + private final String displayName; + private final int chocolateBonus; + private final double multiplierBonus; + + Rarity(String colorCode, String displayName, int chocolateBonus, double multiplierBonus) { + this.colorCode = colorCode; + this.displayName = displayName; + this.chocolateBonus = chocolateBonus; + this.multiplierBonus = multiplierBonus; + } + + public String getColorCode() { + return colorCode; + } + + public String getDisplayName() { + return displayName; + } + + public int getChocolateBonus() { + return chocolateBonus; + } + + public double getMultiplierBonus() { + return multiplierBonus; + } + + public String getFormattedName() { + return "§" + colorCode + "§l" + displayName + " RABBIT"; + } + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java new file mode 100644 index 000000000..aec815614 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java @@ -0,0 +1,159 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import net.minestom.server.item.Material; + +/** + * Represents chocolate shop milestones. + * Each milestone unlocks a special rabbit when spending a certain amount of chocolate in the shop. + */ +public enum ChocolateShopMilestone { + // Common milestones (White) + MILESTONE_1(1, 1_000L, "Alpha Rabbit", "f", 1, 0.002, + "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_2(2, 5_000L, "Beta Rabbit", "f", 1, 0.002, + "b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_3(3, 25_000L, "Gamma Rabbit", "f", 1, 0.002, + "c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_4(4, 100_000L, "Delta Rabbit", "f", 1, 0.002, + "d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2", Material.WHITE_STAINED_GLASS_PANE), + MILESTONE_5(5, 500_000L, "Epsilon Rabbit", "f", 1, 0.002, + "e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2", Material.WHITE_STAINED_GLASS_PANE), + + // Uncommon milestones (Lime/Green) + MILESTONE_6(6, 1_000_000L, "Zeta Rabbit", "a", 2, 0.003, + "f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_7(7, 2_500_000L, "Eta Rabbit", "a", 2, 0.003, + "a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_8(8, 5_000_000L, "Theta Rabbit", "a", 2, 0.003, + "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_9(9, 10_000_000L, "Iota Rabbit", "a", 2, 0.003, + "c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3", Material.LIME_STAINED_GLASS_PANE), + MILESTONE_10(10, 25_000_000L, "Kappa Rabbit", "a", 2, 0.003, + "d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3", Material.LIME_STAINED_GLASS_PANE), + + // Rare milestones (Blue) + MILESTONE_11(11, 50_000_000L, "Lambda Rabbit", "9", 4, 0.004, + "e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_12(12, 100_000_000L, "Mu Rabbit", "9", 4, 0.004, + "f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_13(13, 250_000_000L, "Nu Rabbit", "9", 4, 0.004, + "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_14(14, 500_000_000L, "Xi Rabbit", "9", 4, 0.004, + "b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4", Material.BLUE_STAINED_GLASS_PANE), + MILESTONE_15(15, 1_000_000_000L, "Omicron Rabbit", "9", 4, 0.004, + "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4", Material.BLUE_STAINED_GLASS_PANE), + + // Epic milestones (Purple) + MILESTONE_16(16, 2_500_000_000L, "Pi Rabbit", "5", 10, 0.005, + "d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_17(17, 5_000_000_000L, "Rho Rabbit", "5", 10, 0.005, + "e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_18(18, 10_000_000_000L, "Sigma Rabbit", "5", 10, 0.005, + "f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_19(19, 25_000_000_000L, "Tau Rabbit", "5", 10, 0.005, + "a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5", Material.PURPLE_STAINED_GLASS_PANE), + MILESTONE_20(20, 50_000_000_000L, "Upsilon Rabbit", "5", 10, 0.005, + "b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5", Material.PURPLE_STAINED_GLASS_PANE), + + // Legendary milestones (Orange) + MILESTONE_21(21, 100_000_000_000L, "Phi Rabbit", "6", 0, 0.02, + "c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5", Material.ORANGE_STAINED_GLASS_PANE), + MILESTONE_22(22, 150_000_000_000L, "Chi Rabbit", "6", 0, 0.02, + "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5", Material.ORANGE_STAINED_GLASS_PANE), + MILESTONE_23(23, 200_000_000_000L, "Psi Rabbit", "6", 0, 0.02, + "e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5", Material.ORANGE_STAINED_GLASS_PANE), + MILESTONE_24(24, 300_000_000_000L, "Omega Rabbit", "6", 0, 0.02, + "f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5", Material.ORANGE_STAINED_GLASS_PANE); + + private final int number; + private final long requiredSpent; + private final String rabbitName; + private final String colorCode; + private final int chocolateBonus; + private final double multiplierBonus; + private final String textureId; + private final Material glassPaneMaterial; + + ChocolateShopMilestone(int number, long requiredSpent, String rabbitName, String colorCode, + int chocolateBonus, double multiplierBonus, String textureId, Material glassPaneMaterial) { + this.number = number; + this.requiredSpent = requiredSpent; + this.rabbitName = rabbitName; + this.colorCode = colorCode; + this.chocolateBonus = chocolateBonus; + this.multiplierBonus = multiplierBonus; + this.textureId = textureId; + this.glassPaneMaterial = glassPaneMaterial; + } + + public int getNumber() { + return number; + } + + public long getRequiredSpent() { + return requiredSpent; + } + + public String getRabbitName() { + return rabbitName; + } + + public String getColorCode() { + return colorCode; + } + + public int getChocolateBonus() { + return chocolateBonus; + } + + public double getMultiplierBonus() { + return multiplierBonus; + } + + public String getTextureId() { + return textureId; + } + + public Material getGlassPaneMaterial() { + return glassPaneMaterial; + } + + public String getRomanNumeral() { + return toRoman(number); + } + + public String getFormattedRequirement() { + return ChocolateFactoryHelper.formatChocolate(requiredSpent); + } + + public boolean isUnlocked(long totalSpent) { + return totalSpent >= requiredSpent; + } + + public double getProgress(long totalSpent) { + if (totalSpent >= requiredSpent) return 100.0; + return (totalSpent / (double) requiredSpent) * 100.0; + } + + public static ChocolateShopMilestone fromNumber(int number) { + for (ChocolateShopMilestone milestone : values()) { + if (milestone.number == number) { + return milestone; + } + } + return null; + } + + private static String toRoman(int number) { + if (number <= 0) return "I"; + String[] thousands = {"", "M", "MM", "MMM"}; + String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[number / 1000] + + hundreds[(number % 1000) / 100] + + tens[(number % 100) / 10] + + ones[number % 10]; + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java index 9d9a458ac..563885850 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java @@ -7,8 +7,12 @@ import net.swofty.type.skyblockgeneric.data.SkyBlockDatapoint; import org.json.JSONObject; +import org.json.JSONArray; + import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; public class DatapointChocolateFactory extends SkyBlockDatapoint { private static final Serializer serializer = new Serializer<>() { @@ -45,6 +49,16 @@ public String serialize(ChocolateFactoryData value) { json.put("totalClicks", value.totalClicks); json.put("totalTimeTowerUsages", value.totalTimeTowerUsages); + // Found rabbits + JSONArray foundRabbitsArray = new JSONArray(); + for (String rabbit : value.foundRabbits) { + foundRabbitsArray.put(rabbit); + } + json.put("foundRabbits", foundRabbitsArray); + + // Shop stats + json.put("totalChocolateSpent", value.totalChocolateSpent); + return json.toString(); } @@ -64,6 +78,14 @@ public ChocolateFactoryData deserialize(String json) { } } + Set foundRabbits = new HashSet<>(); + if (jsonObject.has("foundRabbits")) { + JSONArray foundRabbitsArray = jsonObject.getJSONArray("foundRabbits"); + for (int i = 0; i < foundRabbitsArray.length(); i++) { + foundRabbits.add(foundRabbitsArray.getString(i)); + } + } + return new ChocolateFactoryData( jsonObject.optLong("chocolate", 0L), jsonObject.optLong("chocolateAllTime", 0L), @@ -79,7 +101,9 @@ public ChocolateFactoryData deserialize(String json) { jsonObject.optInt("coachJackrabbitLevel", 0), employees, jsonObject.optLong("totalClicks", 0L), - jsonObject.optInt("totalTimeTowerUsages", 0) + jsonObject.optInt("totalTimeTowerUsages", 0), + foundRabbits, + jsonObject.optLong("totalChocolateSpent", 0L) ); } @@ -108,7 +132,9 @@ public ChocolateFactoryData clone(ChocolateFactoryData value) { value.coachJackrabbitLevel, clonedEmployees, value.totalClicks, - value.totalTimeTowerUsages + value.totalTimeTowerUsages, + new HashSet<>(value.foundRabbits), + value.totalChocolateSpent ); } }; @@ -151,6 +177,12 @@ public static class ChocolateFactoryData { private long totalClicks; private int totalTimeTowerUsages; + // Collection + private Set foundRabbits; + + // Shop stats + private long totalChocolateSpent; + public ChocolateFactoryData() { this.chocolate = 0L; this.chocolateAllTime = 0L; @@ -167,6 +199,8 @@ public ChocolateFactoryData() { this.employees = new HashMap<>(); this.totalClicks = 0L; this.totalTimeTowerUsages = 0; + this.foundRabbits = new HashSet<>(); + this.totalChocolateSpent = 0L; } /** @@ -328,6 +362,34 @@ public int getPrestigeLevel() { if (chocolateAllTime >= 1_000_000L) return 1; // 1M return 0; } + + /** + * Adds a rabbit to the found collection + */ + public void addFoundRabbit(String rabbitName) { + foundRabbits.add(rabbitName); + } + + /** + * Checks if a rabbit has been found + */ + public boolean hasFoundRabbit(String rabbitName) { + return foundRabbits.contains(rabbitName); + } + + /** + * Gets the count of found rabbits + */ + public int getFoundRabbitCount() { + return foundRabbits.size(); + } + + /** + * Adds to total chocolate spent (for shop milestones tracking) + */ + public void addChocolateSpent(long amount) { + this.totalChocolateSpent += amount; + } } @AllArgsConstructor diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java index 5883aa1f9..07ac103bb 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -2,822 +2,708 @@ import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; -import net.minestom.server.event.inventory.InventoryCloseEvent; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.inventory.click.Click; -import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; +import net.minestom.server.inventory.click.Click; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; import net.swofty.type.generic.gui.inventory.ItemStackCreator; -import net.swofty.type.generic.gui.inventory.RefreshingGUI; -import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; -import net.swofty.type.generic.gui.inventory.item.GUIItem; -import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.generic.gui.v2.*; +import net.swofty.type.generic.gui.v2.context.ClickContext; +import net.swofty.type.generic.gui.v2.context.ViewContext; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateRabbit; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import java.time.Duration; import java.util.ArrayList; import java.util.List; -public class GUIChocolateFactory extends HypixelInventoryGUI implements RefreshingGUI { - // Texture IDs (the hash part of the minecraft texture URL) +public class GUIChocolateFactory implements StatefulView { + // Texture IDs private static final String CHOCOLATE_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; private static final String HOPPITY_TEXTURE = "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692"; private static final String COACH_JACKRABBIT_TEXTURE = "bc0cc67e79c228e541e68aeb1d81ed7af51166622ad4db9417d7a29d1b89af95"; private static final Sound CLICK_SOUND = Sound.sound(Key.key("block.note_block.bit"), Sound.Source.PLAYER, 1.0f, 1.21f); private static final Sound NOT_ENOUGH_CHOCOLATE_SOUND = Sound.sound(Key.key("entity.enderman.teleport"), Sound.Source.PLAYER, 8.0f, 0.0f); + private static final Sound UPGRADE_SOUND = Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f); // Employee slots (slots 28-34) private static final int[] EMPLOYEE_SLOTS = {28, 29, 30, 31, 32, 33, 34}; - - // Employee names in order matching the slots private static final String[] EMPLOYEE_NAMES = { "Rabbit Bro", "Rabbit Cousin", "Rabbit Sis", "Rabbit Daddy", "Rabbit Granny", "Rabbit Uncle", "Rabbit Dog" }; - - // Employee texture IDs (the hash part of the minecraft texture URL) private static final String[] EMPLOYEE_TEXTURES = { - "287934bdd9df2705b251bb997e029b18c1e94df12992b8107e74497b205ca7e8", // Rabbit Bro - "a982825c01b658f348a099b4579029a180d2e415183951b2e6e5e27257df4254", // Rabbit Cousin - "fd076e0e3d4072d0fffee0a87a5d726fc34b2bcec38c264fb9b67871a8ead633", // Rabbit Sis - "57cab0c34d7ddcf72db56ff36f2883f554cff76eb5d3b3e0562338036c976043", // Rabbit Daddy - "d6eb2d85ee8e3af1c2ec934beb70a39c5e766b23bdab63210bd2aacd73cbbfc8", // Rabbit Granny - "a865176723a0b9ee2916180a55a04cccb7704ad1f31fdf3e9d89c798f6802e6b", // Rabbit Uncle - "35ca98bede3865dd1205e4d091036cd9dc36791b83ea4e0ff4a99ad61b71e898" // Rabbit Dog + "287934bdd9df2705b251bb997e029b18c1e94df12992b8107e74497b205ca7e8", + "a982825c01b658f348a099b4579029a180d2e415183951b2e6e5e27257df4254", + "fd076e0e3d4072d0fffee0a87a5d726fc34b2bcec38c264fb9b67871a8ead633", + "57cab0c34d7ddcf72db56ff36f2883f554cff76eb5d3b3e0562338036c976043", + "d6eb2d85ee8e3af1c2ec934beb70a39c5e766b23bdab63210bd2aacd73cbbfc8", + "a865176723a0b9ee2916180a55a04cccb7704ad1f31fdf3e9d89c798f6802e6b", + "35ca98bede3865dd1205e4d091036cd9dc36791b83ea4e0ff4a99ad61b71e898" }; - - // Employee subtitles from readm.md private static final String[] EMPLOYEE_SUBTITLES = { - "Ambition on two feet!", - "Laid-back legend!", - "Rebel with a cause!", - "CFO with nerves of steel!", - "Storyteller supreme!", - "Stuck in a highlight reel!", - "Making chocolate, not eating it!" + "Ambition on two feet!", "Laid-back legend!", "Rebel with a cause!", + "CFO with nerves of steel!", "Storyteller supreme!", + "Stuck in a highlight reel!", "Making chocolate, not eating it!" }; - public GUIChocolateFactory() { - super("Chocolate Factory", InventoryType.CHEST_6_ROW); + public record State() {} + + @Override + public State initialState() { + return new State(); + } + + @Override + public ViewConfiguration configuration() { + return ViewConfiguration.withString((state, ctx) -> "Chocolate Factory", InventoryType.CHEST_6_ROW); + } + + @Override + public void onOpen(State state, ViewContext ctx) { + // Update production based on time elapsed (handles offline production) + ChocolateFactoryHelper.updateProduction((SkyBlockPlayer) ctx.player()); } @Override - public void setItems(InventoryGUIOpenEvent e) { - // Fill with black glass panes - fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + public void layout(ViewLayout layout, State state, ViewContext ctx) { + Components.fill(layout); + + SkyBlockPlayer player = (SkyBlockPlayer) ctx.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); // Slot 13: Chocolate cookie (clickable) - set(new GUIClickableItem(13) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - ChocolateFactoryHelper.handleClick(player); - player.playSound(CLICK_SOUND); + layout.slot(13, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); + + List lore = new ArrayList<>(); + lore.add("§7§6Chocolate§7, of course, is not a valid"); + lore.add("§7source of §anutrition§7. This, however,"); + lore.add("§7does not stop it from being §dawesome§7."); + lore.add(""); + lore.add("§7Chocolate Production"); + lore.add("§6" + String.format("%.2f", d.getChocolatePerSecond()) + " §8per second"); + lore.add(""); + lore.add("§7All-time Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(d.getChocolateAllTime())); + lore.add(""); + lore.add("§eClick to uncover the meaning of life!"); + + return ItemStackCreator.getStackHead( + "§e" + ChocolateFactoryHelper.formatChocolate(d.getChocolate()) + " §6Chocolate", + CHOCOLATE_TEXTURE, 1, lore); + }, (click, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + ChocolateFactoryHelper.handleClick(p); + p.playSound(CLICK_SOUND); + c.session(State.class).refresh(); + }); + + // Slot 27: Prestige/Chocolate Factory level + layout.slot(27, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); + int prestigeLevel = d.getPrestigeLevel(); + String prestigeColor = ChocolateFactoryHelper.getPrestigeRankColor(prestigeLevel); + + List lore = new ArrayList<>(); + lore.add("§7Chocolate Production Multiplier: §6" + String.format("%.1fx", d.getShrineMultiplier() * d.getCoachMultiplier())); + lore.add("§7Max Rabbit Rarity: §b§lDIVINE"); + lore.add("§7Max Chocolate: §660B"); + lore.add("§7Max Employee: §7[220§7] §bBoard Member"); + lore.add("§7Max §cRabbit Hitman §7Slots: §628"); + lore.add(""); + lore.add("§7Chocolate this Prestige: §6" + ChocolateFactoryHelper.formatChocolate(d.getChocolateAllTime())); + lore.add(""); + lore.add(prestigeLevel >= 6 ? "§aYou have reached max prestige!" : "§eClick to prestige!"); + + return ItemStackCreator.getStack(prestigeColor + "Chocolate Factory " + toRoman(prestigeLevel + 1), Material.DROPPER, 1, lore); + }); + + // Employee slots (28-34) + setupEmployeeSlots(layout, ctx); + + // Slot 35: Rabbit Barn + layout.slot(35, (s, c) -> createRabbitBarnItem((SkyBlockPlayer) c.player()), (click, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); + + if (d.getRabbitBarnLevel() >= 247) { + p.sendMessage("§cYour Rabbit Barn is already at maximum capacity!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - List lore = new ArrayList<>(); - lore.add("§7§6Chocolate§7, of course, is not a valid"); - lore.add("§7source of §anutrition§7. This, however,"); - lore.add("§7does not stop it from being §dawesome§7."); - lore.add(""); - lore.add("§7Chocolate Production"); - lore.add("§6" + String.format("%.2f", data.getChocolatePerSecond()) + " §8per second"); - lore.add(""); - lore.add("§7All-time Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(data.getChocolateAllTime())); - lore.add(""); - lore.add("§eClick to uncover the meaning of life!"); - - return ItemStackCreator.getStackHead( - "§e" + ChocolateFactoryHelper.formatChocolate(data.getChocolate()) + " §6Chocolate", - CHOCOLATE_TEXTURE, 1, lore); + if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.RABBIT_BARN)) { + d = ChocolateFactoryHelper.getData(p); + p.sendMessage("§7Your §aRabbit Barn §7capacity has been increased to §a" + (d.getMaxRabbitSlots() + 2) + " Rabbits§7!"); + p.playSound(UPGRADE_SOUND); + } else { + p.sendMessage("§cYou don't have enough Chocolate!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } + c.session(State.class).refresh(); }); - // Slot 27: Prestige/Chocolate Factory level (dropper) - set(new GUIItem(27) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - int prestigeLevel = data.getPrestigeLevel(); - String prestigeColor = ChocolateFactoryHelper.getPrestigeRankColor(prestigeLevel); - String prestigeName = ChocolateFactoryHelper.getPrestigeRankName(prestigeLevel); - - List lore = new ArrayList<>(); - lore.add("§7Chocolate Production Multiplier: §6" + String.format("%.1fx", data.getShrineMultiplier() * data.getCoachMultiplier())); - lore.add("§7Max Rabbit Rarity: §b§lDIVINE"); - lore.add("§7Max Chocolate: §660B"); - lore.add("§7Max Employee: §7[220§7] §bBoard Member"); - lore.add("§7Max §cRabbit Hitman §7Slots: §628"); - lore.add(""); - lore.add("§7Chocolate this Prestige: §6" + ChocolateFactoryHelper.formatChocolate(data.getChocolateAllTime())); - lore.add(""); - if (prestigeLevel >= 6) { - lore.add("§aYou have reached max prestige!"); - } else { - lore.add("§eClick to prestige!"); - } + // Slot 38: Hand-Baked Chocolate + layout.slot(38, (s, c) -> createHandBakedChocolateItem((SkyBlockPlayer) c.player()), (click, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); + + if (d.getHandBakedChocolateLevel() >= 10) { + p.sendMessage("§cYou only have so many fingers! You can't click any faster!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } - return ItemStackCreator.getStack(prestigeColor + "Chocolate Factory " + toRoman(prestigeLevel + 1), Material.DROPPER, 1, lore); + if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.HAND_BAKED_CHOCOLATE)) { + d = ChocolateFactoryHelper.getData(p); + p.sendMessage("§7You will now produce §6+" + d.getClickPower() + " Chocolate §7per click!"); + p.playSound(UPGRADE_SOUND); + } else { + p.sendMessage("§cYou don't have enough Chocolate!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } + c.session(State.class).refresh(); }); - // Slots 28-34: Employee rabbits - setupEmployeeSlots(); + // Slot 39: Time Tower + layout.slot(39, (s, c) -> createTimeTowerItem((SkyBlockPlayer) c.player()), (click, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); - // Slot 35: Rabbit Barn (oak fence) - set(new GUIClickableItem(35) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + if (d.getPrestigeLevel() < 1) { + p.sendMessage("§cThis requires Chocolate Factory II!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } - // Check max level (247) - if (data.getRabbitBarnLevel() >= 247) { - player.sendMessage("§cYour Rabbit Barn is already at maximum capacity!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + // Right-click to activate + if (click.click() instanceof Click.Right && d.getTimeTowerLevel() > 0 && d.getTimeTowerCharges() > 0 && !d.isTimeTowerActive()) { + if (ChocolateFactoryHelper.activateTimeTower(p)) { + p.sendMessage("§aTime Tower activated for 1 hour!"); + p.playSound(Sound.sound(Key.key("block.beacon.activate"), Sound.Source.PLAYER, 1.0f, 1.0f)); + c.session(State.class).refresh(); return; } - - if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.RABBIT_BARN)) { - data = ChocolateFactoryHelper.getData(player); - player.sendMessage("§7Your §aRabbit Barn §7capacity has been increased to §a" + (data.getMaxRabbitSlots() + 2) + " Rabbits§7!"); - player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - int level = data.getRabbitBarnLevel(); - long cost = ChocolateFactoryHelper.getRabbitBarnCost(level); - boolean canAfford = data.getChocolate() >= cost; - - List lore = new ArrayList<>(); - lore.add("§7Your §aRabbit Barn §7can only hold so"); - lore.add("§7many §aChocolate Rabbits§7."); - lore.add(""); - lore.add("§7If you try collecting more unique"); - lore.add("§7rabbits than your barn can hold,"); - lore.add("§7they will be §ccrushed§7."); - lore.add(""); - lore.add("§7Your Barn: §a" + data.getEmployeeCount() + "§7/§a" + (data.getMaxRabbitSlots() + 2) + " Rabbits"); - if (level >= 247) { - lore.add(""); - lore.add("§aYour Rabbit Barn is at maximum capacity!"); - } else { - lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + toRoman(level + 2)); - lore.add(" §a+2 Capacity"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); - } - - return ItemStackCreator.getStack("§aRabbit Barn " + toRoman(level + 1), Material.OAK_FENCE, 1, lore); + // Left-click to upgrade + if (d.getTimeTowerLevel() >= 15) { + p.sendMessage("§cThe Time Tower is already at its maximum level!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } else if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { + p.sendMessage("§7You upgraded to §dTime Tower " + toRoman(d.getTimeTowerLevel() + 1) + "§7!"); + p.playSound(UPGRADE_SOUND); + } else { + p.sendMessage("§cYou don't have enough Chocolate!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } + c.session(State.class).refresh(); }); - // Slot 38: Hand-Baked Chocolate (cookie) - set(new GUIClickableItem(38) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check max level - if (data.getHandBakedChocolateLevel() >= 10) { - player.sendMessage("§cYou only have so many fingers! You can't click any faster!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } + // Slot 41: Rabbit Shrine + layout.slot(41, (s, c) -> createRabbitShrineItem((SkyBlockPlayer) c.player()), (click, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); - if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.HAND_BAKED_CHOCOLATE)) { - data = ChocolateFactoryHelper.getData(player); - player.sendMessage("§7You will now produce §6+" + data.getClickPower() + " Chocolate §7per click!"); - player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } + if (d.getPrestigeLevel() < 2) { + p.sendMessage("§cThis requires Chocolate Factory III!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - int level = data.getHandBakedChocolateLevel(); - long cost = ChocolateFactoryHelper.getHandBakedChocolateCost(level); - boolean canAfford = data.getChocolate() >= cost; - - List lore = new ArrayList<>(); - lore.add("§7A good boss can get down in the"); - lore.add("§7trenches and help out their"); - lore.add("§7workforce. In exchange for some"); - lore.add("§7§6Chocolate§7, you can increase the"); - lore.add("§7amount of §6Chocolate §7that you"); - lore.add("§7produce each time you click!"); - lore.add(""); - lore.add("§7Chocolate Per Click: §6+" + data.getClickPower() + " Chocolate"); - lore.add(""); - if (level >= 10) { - lore.add("§aYou have reached the maximum"); - lore.add("§aamount of upgrades!"); - } else { - lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dHand-Baked Chocolate " + toRoman(level + 2)); - lore.add(" §6+1 Chocolate Per Click"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); - } + if (d.getRabbitShrineLevel() >= 20) { + p.sendMessage("§cYour Rabbit Shrine is already at its maximum level!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } - return ItemStackCreator.getStack("§dHand-Baked Chocolate " + toRoman(level + 1), Material.COOKIE, 1, lore); + if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.RABBIT_SHRINE)) { + p.sendMessage("§aUpgraded Rabbit Shrine!"); + p.playSound(UPGRADE_SOUND); + } else { + p.sendMessage("§cYou don't have enough Chocolate!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } + c.session(State.class).refresh(); }); - // Slot 39: Time Tower (clock) - Requires Prestige 2 - set(new GUIClickableItem(39) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check prestige requirement - if (data.getPrestigeLevel() < 1) { - player.sendMessage("§cThis requires Chocolate Factory II!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } + // Slot 42: Coach Jackrabbit + layout.slot(42, (s, c) -> createCoachJackrabbitItem((SkyBlockPlayer) c.player()), (click, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); - // Right-click to activate - if (event.getClick() instanceof Click.Right && data.getTimeTowerLevel() > 0 && data.getTimeTowerCharges() > 0 && !data.isTimeTowerActive()) { - if (ChocolateFactoryHelper.activateTimeTower(player)) { - player.sendMessage("§aTime Tower activated for 1 hour!"); - player.playSound(Sound.sound(Key.key("block.beacon.activate"), Sound.Source.PLAYER, 1.0f, 1.0f)); - return; - } - } - - // Left-click to upgrade - if (data.getTimeTowerLevel() >= 15) { - player.sendMessage("§cThe Time Tower is already at its maximum level!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } else if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { - player.sendMessage("§7You upgraded to §dTime Tower " + toRoman(data.getTimeTowerLevel() + 1) + "§7!"); - player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } + if (d.getPrestigeLevel() < 3) { + p.sendMessage("§cThis requires Chocolate Factory IV!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check prestige requirement - requires Prestige 2 (index 1) - if (data.getPrestigeLevel() < 1) { - List lore = new ArrayList<>(); - lore.add("§7What does it do? Nobody knows..."); - lore.add(""); - lore.add("§cChocolate Factory II"); - return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); - } - - int level = data.getTimeTowerLevel(); - long cost = ChocolateFactoryHelper.getTimeTowerCost(level, data.getPrestigeLevel()); - boolean canAfford = data.getChocolate() >= cost; - boolean isActive = data.isTimeTowerActive(); - - List lore = new ArrayList<>(); - lore.add("§7When active, this ancient building"); - lore.add("§7increases the production of your"); - lore.add("§7§6Chocolate Factory §7by §6+" + String.format("%.1fx", data.getTimeTowerMultiplier() - 1.0 + (level > 0 ? 0.1 * level : 0.1)) + " §7for §a1h§7."); - lore.add(""); - if (isActive) { - long remaining = data.getTimeTowerActiveUntil() - System.currentTimeMillis(); - long minutes = remaining / 60000; - long seconds = (remaining % 60000) / 1000; - lore.add("§7Status: §a§lACTIVE"); - lore.add("§7Time Remaining: §a" + minutes + "m " + seconds + "s"); - } else { - lore.add("§7Status: §c§lINACTIVE"); - } - lore.add(""); - lore.add("§7Charges: §a" + data.getTimeTowerCharges() + "§7/§a3"); - lore.add(""); - if (level >= 15) { - lore.add("§aThe Time Tower is maxed out!"); - } else { - lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dTime Tower " + toRoman(level + 2)); - lore.add(" §6+0.1x Production Multiplier"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); - } - if (data.getTimeTowerCharges() > 0 && !isActive) { - lore.add("§dRight-click to activate!"); - } + if (d.getCoachJackrabbitLevel() >= 20) { + p.sendMessage("§cCoach Jackrabbit has already taught you all that he can teach!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } - return ItemStackCreator.getStack("§dTime Tower " + toRoman(level + 1), Material.CLOCK, 1, lore); + if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.COACH_JACKRABBIT)) { + p.sendMessage("§aUpgraded Coach Jackrabbit!"); + p.playSound(UPGRADE_SOUND); + } else { + p.sendMessage("§cYou don't have enough Chocolate!"); + p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } + c.session(State.class).refresh(); }); - // Slot 41: Rabbit Shrine (rabbit foot) - Requires Prestige 3 - set(new GUIClickableItem(41) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check prestige requirement - if (data.getPrestigeLevel() < 2) { - player.sendMessage("§cThis requires Chocolate Factory III!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } + // Slot 45: Chocolate Production info + layout.slot(45, (s, c) -> createProductionInfoItem((SkyBlockPlayer) c.player())); + + // Slot 49: Close + Components.close(layout, 49); + + // Slot 50: Hoppity's Collection + layout.slot(50, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); + + int rabbitsFound = d.getFoundRabbitCount(); + int totalRabbits = 512; + double percentage = (rabbitsFound / (double) totalRabbits) * 100; + + List lore = new ArrayList<>(); + lore.add("§7Help §aHoppity §7find all of his §aChocolate"); + lore.add("§aRabbits §7during the §dHoppity's Hunt"); + lore.add("§d§7event!"); + lore.add(""); + lore.add("§7The more unique §aChocolate Rabbits"); + lore.add("§a§7that you find, the more your"); + lore.add("§7§6Chocolate Factory §7will produce!"); + lore.add(""); + lore.add("§7Rabbits Found: §e" + String.format("%.1f", percentage) + "§6%"); + lore.add("§2§l§m §f§l§m §r §e" + rabbitsFound + "§6/§e" + totalRabbits); + lore.add(""); + lore.add("§eClick to view!"); + + return ItemStackCreator.getStackHead("§aHoppity's Collection", HOPPITY_TEXTURE, 1, lore); + }, (click, c) -> new GUIHoppityCollection().open((SkyBlockPlayer) c.player())); + + // Slot 51: Rabbit Hitman + layout.slot(51, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7§7Hire this private rabbit to hunt eggs"); + lore.add("§7for you, they will collect eggs you"); + lore.add("§7missed!"); + lore.add(""); + lore.add("§7Available eggs: §a0"); + lore.add("§7Purchased slots: §e0§7/§a28"); + lore.add(""); + lore.add("§eClick to view!"); + return ItemStackCreator.getStack("§cRabbit Hitman", Material.BOW, 1, lore); + }, (click, c) -> c.player().sendMessage("§7Opening Rabbit Hitman... (Coming Soon)")); + + // Slot 52: Chocolate Factory Ranking + layout.slot(52, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7§7You are §8#§b??? §7in all-time"); + lore.add("§7Chocolate."); + lore.add("§7§8You are in the top §e??%§8 of players!"); + return ItemStackCreator.getStack("§dChocolate Factory Ranking", Material.MILK_BUCKET, 1, lore); + }); - // Check max level - if (data.getRabbitShrineLevel() >= 20) { - player.sendMessage("§cYour Rabbit Shrine is already at its maximum level!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } + // Slot 53: Chocolate Factory Milestones + layout.slot(53, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7Unlock special §aChocolate Rabbits §7by"); + lore.add("§7reaching all-time §6Chocolate"); + lore.add("§6§7milestones!"); + lore.add(""); + lore.add("§eClick to view!"); + return ItemStackCreator.getStack("§6Chocolate Factory Milestones", Material.LADDER, 1, lore); + }, (click, c) -> new GUIChocolateFactoryMilestones().open((SkyBlockPlayer) c.player())); + } - if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.RABBIT_SHRINE)) { - player.sendMessage("§aUpgraded Rabbit Shrine!"); - player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } - } + private void setupEmployeeSlots(ViewLayout layout, ViewContext ctx) { + for (int i = 0; i < EMPLOYEE_SLOTS.length; i++) { + int slot = EMPLOYEE_SLOTS[i]; + String employeeName = EMPLOYEE_NAMES[i]; + String employeeTexture = EMPLOYEE_TEXTURES[i]; + String employeeSubtitle = EMPLOYEE_SUBTITLES[i]; - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check prestige requirement - requires Prestige 3 (index 2) - if (data.getPrestigeLevel() < 2) { - List lore = new ArrayList<>(); - lore.add("§7What does it do? Nobody knows..."); - lore.add(""); - lore.add("§cChocolate Factory III"); - return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); - } + layout.slot(slot, (s, c) -> createEmployeeItem((SkyBlockPlayer) c.player(), employeeName, employeeTexture, employeeSubtitle), + (click, c) -> { + handleEmployeeClick((SkyBlockPlayer) c.player(), employeeName); + c.session(State.class).refresh(); + }); + } + } - int level = data.getRabbitShrineLevel(); - long cost = ChocolateFactoryHelper.getRabbitShrineCost(level); - boolean canAfford = data.getChocolate() >= cost; - int oddsBonus = level * 2; // 2% per level - - List lore = new ArrayList<>(); - lore.add("§7The §dRabbit Shrine §7increases the"); - lore.add("§7§dodds §7of finding §aChocolate Rabbits §7of"); - lore.add("§7higher rarity during §dHoppity's Hunt"); - lore.add("§d§7by §a" + oddsBonus + "%§7."); - lore.add(""); - if (level >= 20) { - lore.add("§aYour Rabbit Shrine is at its maximum"); - lore.add("§alevel!"); - } else { - lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dRabbit Shrine " + toRoman(level + 2)); - lore.add(" §a+2% Rare Rabbit Odds"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); - } + private void handleEmployeeClick(SkyBlockPlayer player, String employeeName) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - return ItemStackCreator.getStack("§dRabbit Shrine " + toRoman(level + 1), Material.RABBIT_FOOT, 1, lore); - } - }); + if (!ChocolateFactoryHelper.isEmployeeUnlocked(player, employeeName)) { + String prereq = ChocolateFactoryHelper.getEmployeePrerequisite(employeeName); + player.sendMessage("§cPromote §f" + prereq + " §cto §7[20§7] §fEmployee §cto unlock §f" + employeeName + "§c!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } - // Slot 42: Coach Jackrabbit (player head) - Requires Prestige 4 - set(new GUIClickableItem(42) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check prestige requirement - if (data.getPrestigeLevel() < 3) { - player.sendMessage("§cThis requires Chocolate Factory IV!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } + DatapointChocolateFactory.EmployeeData existingEmployee = data.getEmployees().get(employeeName); - // Check max level - if (data.getCoachJackrabbitLevel() >= 20) { - player.sendMessage("§cCoach Jackrabbit has already taught you all that he can teach!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } + if (existingEmployee != null && existingEmployee.getLevel() >= 220) { + player.sendMessage("§b" + employeeName + " §ccannot ascend the corporate ladder any further!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; + } - if (ChocolateFactoryHelper.purchaseUpgrade(player, ChocolateFactoryHelper.UpgradeType.COACH_JACKRABBIT)) { - player.sendMessage("§aUpgraded Coach Jackrabbit!"); - player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } + if (existingEmployee == null) { + long cost = ChocolateFactoryHelper.getEmployeeCost(employeeName, 1); + if (data.getChocolate() < cost) { + player.sendMessage("§c" + employeeName + " does not work at your §6Chocolate Factory §cyet!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + return; } + } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check prestige requirement - requires Prestige 4 (index 3) - if (data.getPrestigeLevel() < 3) { - List lore = new ArrayList<>(); - lore.add("§7What does it do? Nobody knows..."); - lore.add(""); - lore.add("§cChocolate Factory IV"); - return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); - } + if (ChocolateFactoryHelper.hireOrUpgradeEmployee(player, employeeName)) { + DatapointChocolateFactory.ChocolateFactoryData newData = ChocolateFactoryHelper.getData(player); + DatapointChocolateFactory.EmployeeData emp = newData.getEmployees().get(employeeName); + int level = emp != null ? emp.getLevel() : 1; + String rank = getEmployeeRank(level); + String rankColor = "§" + getEmployeeRankColor(level); + + player.sendMessage(rankColor + employeeName + " §7has been promoted to §7[" + level + "§7] " + rankColor + rank + "§7!"); + player.playSound(UPGRADE_SOUND); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + } + } - int level = data.getCoachJackrabbitLevel(); - long cost = ChocolateFactoryHelper.getCoachJackrabbitCost(level); - boolean canAfford = data.getChocolate() >= cost; - double multiplierBonus = level * 0.01; - - List lore = new ArrayList<>(); - lore.add("§8§oPep talk pro!"); - lore.add(""); - lore.add("§7§dCoach Jackrabbit §7is a motivational"); - lore.add("§7speaker that is helping you reach"); - lore.add("§7your full potential by granting §6+" + String.format("%.2fx", multiplierBonus)); - lore.add("§6Chocolate §7per second!"); - lore.add(""); - if (level >= 20) { - lore.add("§aCoach Jackrabbit has already taught"); - lore.add("§ayou all that he can teach!"); - } else { - lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dCoach Jackrabbit " + toRoman(level + 2)); - lore.add(" §6+0.01x Production Multiplier"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); - } + private ItemStack.Builder createEmployeeItem(SkyBlockPlayer player, String employeeName, String texture, String subtitle) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + boolean isUnlocked = ChocolateFactoryHelper.isEmployeeUnlocked(player, employeeName); + String prerequisite = ChocolateFactoryHelper.getEmployeePrerequisite(employeeName); - return ItemStackCreator.getStackHead("§dCoach Jackrabbit " + toRoman(level + 1), COACH_JACKRABBIT_TEXTURE, 1, lore); - } - }); + if (!isUnlocked) { + List lore = new ArrayList<>(); + lore.add("§8§o" + subtitle); + lore.add(""); + lore.add("§7Promote §f" + prerequisite + " §7to §7[20§7]"); + lore.add("§fEmployee §7to unlock!"); - // Slot 45: Chocolate Production info (cocoa beans) - set(new GUIItem(45) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - double baseProduction = 0; - for (DatapointChocolateFactory.EmployeeData employee : data.getEmployees().values()) { - baseProduction += employee.getProductionPerSecond(); - } + return ItemStackCreator.getStack("§c" + employeeName, Material.GRAY_DYE, 1, lore); + } - double totalMultiplier = data.getShrineMultiplier() * data.getTimeTowerMultiplier() * data.getCoachMultiplier(); - - List lore = new ArrayList<>(); - lore.add("§6" + String.format("%.2f", data.getChocolatePerSecond()) + " Chocolate §8per second"); - lore.add(""); - lore.add("§7Base Chocolate: §6" + String.format("%.0f", baseProduction) + " §8per second"); - lore.add(" §6+" + String.format("%.0f", baseProduction) + " §8(Rabbit Employees§8)"); - lore.add(""); - lore.add("§7Total Multiplier: §6" + String.format("%.3fx", totalMultiplier)); - lore.add(" §6+1x §8(Base Multiplier)"); - if (data.getRabbitShrineLevel() > 0) { - lore.add(" §6+" + String.format("%.1fx", data.getShrineMultiplier() - 1.0) + " §8(Rabbit Shrine)"); - } - if (data.getCoachJackrabbitLevel() > 0) { - lore.add(" §6+" + String.format("%.2fx", data.getCoachMultiplier() - 1.0) + " §8(Coach Jackrabbit)"); - } - if (data.isTimeTowerActive()) { - lore.add(" §6+" + String.format("%.1fx", data.getTimeTowerMultiplier() - 1.0) + " §8(Time Tower)"); - } + DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(employeeName); + double baseProduction = ChocolateFactoryHelper.getEmployeeBaseProduction(employeeName); + + if (employee == null) { + long cost = ChocolateFactoryHelper.getEmployeeCost(player, employeeName, 1); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add("§8§o" + subtitle); + lore.add(""); + lore.add("§c" + employeeName + " §7does not work at"); + lore.add("§7your §6Chocolate Factory §7yet!"); + lore.add(""); + lore.add("§7Hire them and they will produce"); + lore.add("§6+" + String.format("%.0f", baseProduction) + " Chocolate §7per second!"); + lore.add(""); + lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to hire!" : "§cYou don't have enough Chocolate!"); + + return ItemStackCreator.getStackHead("§c" + employeeName, texture, 1, lore); + } - return ItemStackCreator.getStack("§6Chocolate Production", Material.COCOA_BEANS, 1, lore); - } - }); + int level = employee.getLevel(); + long cost = ChocolateFactoryHelper.getEmployeeCost(player, employeeName, level + 1); + boolean canAfford = data.getChocolate() >= cost; + double currentProduction = baseProduction * level; + String rank = getEmployeeRank(level); + String rankColor = getEmployeeRankColor(level); + boolean isUnemployed = level < 1; + + List lore = new ArrayList<>(); + lore.add("§8§o" + subtitle); + lore.add(""); + lore.add("§7§" + rankColor + employeeName + " §7is " + (isUnemployed ? "" : "a ") + "§" + rankColor + rank + "§7. They"); + lore.add(rank.equals("Board Member") ? "§7are on the Board of Rabbits and" : "§7are working hard and"); + lore.add("§7produce §6+" + String.format("%.0f", currentProduction) + " Chocolate §7per second!"); + lore.add(""); + + if (level >= 220) { + lore.add("§7§" + rankColor + employeeName + " §ahas climbed as far as the"); + lore.add("§acorporate ladder will allow!"); + } else { + lore.add("§8§m-----------------"); + String action = isUnemployed ? "§a§lHIRE" : "§a§lPROMOTE"; + lore.add(action + " §8➜ §7[" + (level + 1) + "§7] §" + getEmployeeRankColor(level + 1) + getEmployeeRank(level + 1)); + lore.add(" §6+" + String.format("%.0f", baseProduction) + " Chocolate per second"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + String clickAction = isUnemployed ? "§eClick to hire!" : "§eClick to promote!"; + lore.add(canAfford ? clickAction : "§cYou don't have enough Chocolate!"); + } - // Slot 47: Chocolate Shop (emerald) - set(new GUIClickableItem(47) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - // TODO: Open Chocolate Shop GUI - p.sendMessage("§7Opening Chocolate Shop... (Coming Soon)"); - } + String title = isUnemployed + ? "§" + rankColor + employeeName + "§8 - §" + rankColor + rank + : "§" + rankColor + employeeName + "§8 - §7[" + level + "§7] §" + rankColor + rank; + return ItemStackCreator.getStackHead(title, texture, 1, lore); + } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7Spend your §6Chocolate §7on the world's"); - lore.add("§7most delectable treats in the"); - lore.add("§7§6Chocolate Shop§7."); - lore.add(""); - lore.add("§eClick to view!"); + private ItemStack.Builder createRabbitBarnItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getRabbitBarnLevel(); + long cost = ChocolateFactoryHelper.getRabbitBarnCost(level); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add("§7Your §aRabbit Barn §7can only hold so"); + lore.add("§7many §aChocolate Rabbits§7."); + lore.add(""); + lore.add("§7If you try collecting more unique"); + lore.add("§7rabbits than your barn can hold,"); + lore.add("§7they will be §ccrushed§7."); + lore.add(""); + lore.add("§7Your Barn: §a" + data.getEmployeeCount() + "§7/§a" + (data.getMaxRabbitSlots() + 2) + " Rabbits"); + if (level >= 247) { + lore.add(""); + lore.add("§aYour Rabbit Barn is at maximum capacity!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + toRoman(level + 2)); + lore.add(" §a+2 Capacity"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } - return ItemStackCreator.getStack("§6Chocolate Shop", Material.EMERALD, 1, lore); - } - }); + return ItemStackCreator.getStack("§aRabbit Barn " + toRoman(level + 1), Material.OAK_FENCE, 1, lore); + } - // Slot 48: Go Back (arrow) - set(new GUIClickableItem(48) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - p.closeInventory(); - } + private ItemStack.Builder createHandBakedChocolateItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getHandBakedChocolateLevel(); + long cost = ChocolateFactoryHelper.getHandBakedChocolateCost(level); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add("§7A good boss can get down in the"); + lore.add("§7trenches and help out their"); + lore.add("§7workforce. In exchange for some"); + lore.add("§7§6Chocolate§7, you can increase the"); + lore.add("§7amount of §6Chocolate §7that you"); + lore.add("§7produce each time you click!"); + lore.add(""); + lore.add("§7Chocolate Per Click: §6+" + data.getClickPower() + " Chocolate"); + lore.add(""); + if (level >= 10) { + lore.add("§aYou have reached the maximum"); + lore.add("§aamount of upgrades!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dHand-Baked Chocolate " + toRoman(level + 2)); + lore.add(" §6+1 Chocolate Per Click"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7To Calendar and Events"); + return ItemStackCreator.getStack("§dHand-Baked Chocolate " + toRoman(level + 1), Material.COOKIE, 1, lore); + } - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - } - }); + private ItemStack.Builder createTimeTowerItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - // Slot 49: Close (barrier) - set(GUIClickableItem.getCloseItem(49)); + if (data.getPrestigeLevel() < 1) { + List lore = new ArrayList<>(); + lore.add("§7What does it do? Nobody knows..."); + lore.add(""); + lore.add("§cChocolate Factory II"); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } - // Slot 50: Hoppity's Collection (player head) - set(new GUIClickableItem(50) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - // TODO: Open Hoppity's Collection GUI - p.sendMessage("§7Opening Hoppity's Collection... (Coming Soon)"); - } + int level = data.getTimeTowerLevel(); + long cost = ChocolateFactoryHelper.getTimeTowerCost(level, data.getPrestigeLevel()); + boolean canAfford = data.getChocolate() >= cost; + boolean isActive = data.isTimeTowerActive(); + + List lore = new ArrayList<>(); + lore.add("§7When active, this ancient building"); + lore.add("§7increases the production of your"); + lore.add("§7§6Chocolate Factory §7by §6+" + String.format("%.1fx", (level > 0 ? 0.1 * level : 0.1)) + " §7for §a1h§7."); + lore.add(""); + if (isActive) { + long remaining = data.getTimeTowerActiveUntil() - System.currentTimeMillis(); + long minutes = remaining / 60000; + long seconds = (remaining % 60000) / 1000; + lore.add("§7Status: §a§lACTIVE"); + lore.add("§7Time Remaining: §a" + minutes + "m " + seconds + "s"); + } else { + lore.add("§7Status: §c§lINACTIVE"); + } + lore.add(""); + lore.add("§7Charges: §a" + data.getTimeTowerCharges() + "§7/§a3"); + lore.add(""); + if (level >= 15) { + lore.add("§aThe Time Tower is maxed out!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dTime Tower " + toRoman(level + 2)); + lore.add(" §6+0.1x Production Multiplier"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } + if (data.getTimeTowerCharges() > 0 && !isActive) { + lore.add("§dRight-click to activate!"); + } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - int rabbitsFound = data.getEmployeeCount(); - int totalRabbits = 512; - double percentage = (rabbitsFound / (double) totalRabbits) * 100; - - List lore = new ArrayList<>(); - lore.add("§7Help §aHoppity §7find all of his §aChocolate"); - lore.add("§aRabbits §7during the §dHoppity's Hunt"); - lore.add("§d§7event!"); - lore.add(""); - lore.add("§7The more unique §aChocolate Rabbits"); - lore.add("§a§7that you find, the more your"); - lore.add("§7§6Chocolate Factory §7will produce!"); - lore.add(""); - lore.add("§7Finding duplicate Rabbits grants"); - lore.add("§7§a+10% §7extra §6Chocolate §7per duplicate,"); - lore.add("§7up to §a+100%§7!"); - lore.add(""); - lore.add("§7Rabbits Found: §e" + String.format("%.1f", percentage) + "§6%"); - lore.add("§2§l§m §f§l§m §r §e" + rabbitsFound + "§6/§e" + totalRabbits); - lore.add(""); - lore.add("§eClick to view!"); - - return ItemStackCreator.getStackHead("§aHoppity's Collection", HOPPITY_TEXTURE, 1, lore); - } - }); + return ItemStackCreator.getStack("§dTime Tower " + toRoman(level + 1), Material.CLOCK, 1, lore); + } - // Slot 51: Rabbit Hitman (bow) - set(new GUIClickableItem(51) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - // TODO: Open Rabbit Hitman GUI - p.sendMessage("§7Opening Rabbit Hitman... (Coming Soon)"); - } + private ItemStack.Builder createRabbitShrineItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7§7Hire this private rabbit to hunt eggs"); - lore.add("§7for you, they will collect eggs you"); - lore.add("§7missed!"); - lore.add(""); - lore.add("§7Available eggs: §a0"); - lore.add("§7Purchased slots: §e0§7/§a28"); - lore.add(""); - lore.add("§eClick to view!"); - - return ItemStackCreator.getStack("§cRabbit Hitman", Material.BOW, 1, lore); - } - }); + if (data.getPrestigeLevel() < 2) { + List lore = new ArrayList<>(); + lore.add("§7What does it do? Nobody knows..."); + lore.add(""); + lore.add("§cChocolate Factory III"); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } - // Slot 52: Chocolate Factory Ranking (milk bucket) - set(new GUIItem(52) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + int level = data.getRabbitShrineLevel(); + long cost = ChocolateFactoryHelper.getRabbitShrineCost(level); + boolean canAfford = data.getChocolate() >= cost; + int oddsBonus = level * 2; + + List lore = new ArrayList<>(); + lore.add("§7The §dRabbit Shrine §7increases the"); + lore.add("§7§dodds §7of finding §aChocolate Rabbits §7of"); + lore.add("§7higher rarity during §dHoppity's Hunt"); + lore.add("§d§7by §a" + oddsBonus + "%§7."); + lore.add(""); + if (level >= 20) { + lore.add("§aYour Rabbit Shrine is at its maximum"); + lore.add("§alevel!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dRabbit Shrine " + toRoman(level + 2)); + lore.add(" §a+2% Rare Rabbit Odds"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } - List lore = new ArrayList<>(); - lore.add("§7§7You are §8#§b??? §7in all-time"); - lore.add("§7Chocolate."); - lore.add("§7§8You are in the top §e??%§8 of players!"); + return ItemStackCreator.getStack("§dRabbit Shrine " + toRoman(level + 1), Material.RABBIT_FOOT, 1, lore); + } - return ItemStackCreator.getStack("§dChocolate Factory Ranking", Material.MILK_BUCKET, 1, lore); - } - }); + private ItemStack.Builder createCoachJackrabbitItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - // Slot 53: Chocolate Factory Milestones (ladder) - set(new GUIClickableItem(53) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - // TODO: Open Milestones GUI - p.sendMessage("§7Opening Chocolate Factory Milestones... (Coming Soon)"); - } + if (data.getPrestigeLevel() < 3) { + List lore = new ArrayList<>(); + lore.add("§7What does it do? Nobody knows..."); + lore.add(""); + lore.add("§cChocolate Factory IV"); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7Unlock special §aChocolate Rabbits §7by"); - lore.add("§7reaching all-time §6Chocolate"); - lore.add("§6§7milestones!"); - lore.add(""); - lore.add("§eClick to view!"); + int level = data.getCoachJackrabbitLevel(); + long cost = ChocolateFactoryHelper.getCoachJackrabbitCost(level); + boolean canAfford = data.getChocolate() >= cost; + double multiplierBonus = level * 0.01; + + List lore = new ArrayList<>(); + lore.add("§8§oPep talk pro!"); + lore.add(""); + lore.add("§7§dCoach Jackrabbit §7is a motivational"); + lore.add("§7speaker that is helping you reach"); + lore.add("§7your full potential by granting §6+" + String.format("%.2fx", multiplierBonus)); + lore.add("§6Chocolate §7per second!"); + lore.add(""); + if (level >= 20) { + lore.add("§aCoach Jackrabbit has already taught"); + lore.add("§ayou all that he can teach!"); + } else { + lore.add("§8§m-----------------"); + lore.add("§a§lUPGRADE §8➜ §dCoach Jackrabbit " + toRoman(level + 2)); + lore.add(" §6+0.01x Production Multiplier"); + lore.add(""); + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); + } - return ItemStackCreator.getStack("§6Chocolate Factory Milestones", Material.LADDER, 1, lore); - } - }); + return ItemStackCreator.getStackHead("§dCoach Jackrabbit " + toRoman(level + 1), COACH_JACKRABBIT_TEXTURE, 1, lore); } - private void setupEmployeeSlots() { - for (int i = 0; i < EMPLOYEE_SLOTS.length; i++) { - int slot = EMPLOYEE_SLOTS[i]; - String employeeName = EMPLOYEE_NAMES[i]; - String employeeTexture = EMPLOYEE_TEXTURES[i]; - String employeeSubtitle = EMPLOYEE_SUBTITLES[i]; + private ItemStack.Builder createProductionInfoItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - set(new GUIClickableItem(slot) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check if employee is unlocked (prerequisite at level 20) - if (!ChocolateFactoryHelper.isEmployeeUnlocked(player, employeeName)) { - String prereq = ChocolateFactoryHelper.getEmployeePrerequisite(employeeName); - player.sendMessage("§cPromote §f" + prereq + " §cto §7[20§7] §fEmployee §cto unlock §f" + employeeName + "§c!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } - - DatapointChocolateFactory.EmployeeData existingEmployee = data.getEmployees().get(employeeName); - - // Check if employee is already at max level (220) - if (existingEmployee != null && existingEmployee.getLevel() >= 220) { - player.sendMessage("§b" + employeeName + " §ccannot ascend the corporate ladder any further!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } - - // Check if employee is not hired yet - show special message - if (existingEmployee == null) { - long cost = ChocolateFactoryHelper.getEmployeeCost(employeeName, 1); - if (data.getChocolate() < cost) { - player.sendMessage("§c" + employeeName + " does not work at your §6Chocolate Factory §cyet!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - return; - } - } - - if (ChocolateFactoryHelper.hireOrUpgradeEmployee(player, employeeName)) { - // Get fresh data after the hire/upgrade - DatapointChocolateFactory.ChocolateFactoryData newData = ChocolateFactoryHelper.getData(player); - DatapointChocolateFactory.EmployeeData emp = newData.getEmployees().get(employeeName); - int level = emp != null ? emp.getLevel() : 1; - String rank = getEmployeeRank(level); - String rankColor = "§" + getEmployeeRankColor(level); - - player.sendMessage(rankColor + employeeName + " §7has been promoted to §7[" + level + "§7] " + rankColor + rank + "§7!"); - player.playSound(Sound.sound(Key.key("block.note_block.pling"), Sound.Source.PLAYER, 8.0f, 4.05f)); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); - } - } + double employeeProduction = 0; + for (DatapointChocolateFactory.EmployeeData employee : data.getEmployees().values()) { + employeeProduction += employee.getProductionPerSecond(); + } - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - // Check if employee is unlocked (prerequisite at level 20) - boolean isUnlocked = ChocolateFactoryHelper.isEmployeeUnlocked(player, employeeName); - String prerequisite = ChocolateFactoryHelper.getEmployeePrerequisite(employeeName); - - if (!isUnlocked) { - // Locked - show grey dye with unlock requirement - DatapointChocolateFactory.EmployeeData prereqEmployee = data.getEmployees().get(prerequisite); - int prereqLevel = prereqEmployee != null ? prereqEmployee.getLevel() : 0; - - List lore = new ArrayList<>(); - lore.add("§8§o" + employeeSubtitle); - lore.add(""); - lore.add("§c§lLOCKED"); - lore.add(""); - lore.add("§7Promote §f" + prerequisite + " §7to §7[20§7]"); - lore.add("§fEmployee §7to unlock!"); - lore.add(""); - lore.add("§7Progress: §e" + prereqLevel + "§7/§a20"); - - return ItemStackCreator.getStack("§c" + employeeName, Material.GRAY_DYE, 1, lore); - } - - DatapointChocolateFactory.EmployeeData employee = data.getEmployees().get(employeeName); - double baseProduction = ChocolateFactoryHelper.getEmployeeBaseProduction(employeeName); - - if (employee == null) { - // Not hired yet - show grey dye - long cost = ChocolateFactoryHelper.getEmployeeCost(player, employeeName, 1); - boolean canAfford = data.getChocolate() >= cost; - - List lore = new ArrayList<>(); - lore.add("§8§o" + employeeSubtitle); - lore.add(""); - lore.add("§c" + employeeName + " §7does not work at"); - lore.add("§7your §6Chocolate Factory §7yet!"); - lore.add(""); - lore.add("§7Hire them and they will produce"); - lore.add("§6+" + String.format("%.0f", baseProduction) + " Chocolate §7per second!"); - lore.add(""); - lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to hire!" : "§cYou don't have enough Chocolate!"); - - return ItemStackCreator.getStack("§c" + employeeName, Material.GRAY_DYE, 1, lore); - } else { - // Already hired - show player head - int level = employee.getLevel(); - long cost = ChocolateFactoryHelper.getEmployeeCost(player, employeeName, level + 1); - boolean canAfford = data.getChocolate() >= cost; - double currentProduction = baseProduction * level; - String rank = getEmployeeRank(level); - String rankColor = getEmployeeRankColor(level); - - List lore = new ArrayList<>(); - lore.add("§8§o" + employeeSubtitle); - lore.add(""); - lore.add("§7§b" + employeeName + " §7is a §" + rankColor + rank + "§7. They"); - if (rank.equals("Board Member")) { - lore.add("§7are on the Board of Rabbits and"); - } else { - lore.add("§7are working hard and"); - } - lore.add("§7produce §6+" + String.format("%.0f", currentProduction) + " Chocolate §7per second!"); - lore.add(""); - - if (level >= 220) { - lore.add("§7§b" + employeeName + " §ahas climbed as far as the"); - lore.add("§acorporate ladder will allow!"); - } else { - lore.add("§8§m-----------------"); - lore.add("§a§lPROMOTE §8➜ §7[" + (level + 1) + "§7] §" + getEmployeeRankColor(level + 1) + getEmployeeRank(level + 1)); - double nextProduction = baseProduction * (level + 1); - lore.add(" §6+" + String.format("%.0f", baseProduction) + " Chocolate per second"); - lore.add(""); - lore.add("§7Cost"); - lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to promote!" : "§cYou don't have enough Chocolate!"); - } - - return ItemStackCreator.getStackHead("§b" + employeeName + "§8 - §7[" + level + "§7] §" + rankColor + rank, employeeTexture, 1, lore); - } - } - }); + // Calculate bonuses from Hoppity's Collection + int rabbitChocolateBonus = 0; + double rabbitMultiplierBonus = 0; + for (String rabbitName : data.getFoundRabbits()) { + try { + ChocolateRabbit rabbit = ChocolateRabbit.valueOf(rabbitName); + rabbitChocolateBonus += rabbit.getChocolateBonus(); + rabbitMultiplierBonus += rabbit.getMultiplierBonus(); + } catch (IllegalArgumentException ignored) { + } + } + + double baseProduction = employeeProduction + rabbitChocolateBonus; + double totalMultiplier = data.getShrineMultiplier() * data.getTimeTowerMultiplier() * data.getCoachMultiplier() + rabbitMultiplierBonus; + + List lore = new ArrayList<>(); + lore.add("§6" + String.format("%.2f", data.getChocolatePerSecond()) + " Chocolate §8per second"); + lore.add(""); + lore.add("§7Base Chocolate: §6" + String.format("%.0f", baseProduction) + " §8per second"); + lore.add(" §6+" + String.format("%.0f", employeeProduction) + " §8(Rabbit Employees§8)"); + if (rabbitChocolateBonus > 0) { + lore.add(" §6+" + rabbitChocolateBonus + " §8(Hoppity's Collection§8)"); + } + lore.add(""); + lore.add("§7Total Multiplier: §6" + String.format("%.3fx", totalMultiplier)); + lore.add(" §6+1x §8(Base Multiplier)"); + if (rabbitMultiplierBonus > 0) { + lore.add(" §6+" + String.format("%.3fx", rabbitMultiplierBonus) + " §8(Hoppity's Collection§8)"); + } + if (data.getRabbitShrineLevel() > 0) { + lore.add(" §6+" + String.format("%.1fx", data.getShrineMultiplier() - 1.0) + " §8(Rabbit Shrine)"); + } + if (data.getCoachJackrabbitLevel() > 0) { + lore.add(" §6+" + String.format("%.2fx", data.getCoachMultiplier() - 1.0) + " §8(Coach Jackrabbit)"); } + if (data.isTimeTowerActive()) { + lore.add(" §6+" + String.format("%.1fx", data.getTimeTowerMultiplier() - 1.0) + " §8(Time Tower)"); + } + + return ItemStackCreator.getStack("§6Chocolate Production", Material.COCOA_BEANS, 1, lore); } private String getEmployeeRank(int level) { @@ -832,14 +718,14 @@ private String getEmployeeRank(int level) { } private String getEmployeeRankColor(int level) { - if (level >= 220) return "b"; // Aqua - Board Member - if (level >= 200) return "d"; // Light Purple - Executive - if (level >= 180) return "6"; // Gold - Director - if (level >= 140) return "5"; // Dark Purple - Manager - if (level >= 120) return "9"; // Blue - Assistant - if (level >= 20) return "a"; // Green - Employee - if (level >= 1) return "7"; // Gray - Intern - return "c"; // Red - Unemployed + if (level >= 220) return "b"; + if (level >= 200) return "d"; + if (level >= 180) return "6"; + if (level >= 140) return "5"; + if (level >= 120) return "9"; + if (level >= 20) return "a"; + if (level >= 1) return "f"; + return "c"; } private String toRoman(int number) { @@ -855,35 +741,10 @@ private String toRoman(int number) { ones[number % 10]; } - @Override - public void refreshItems(HypixelPlayer player) { - // Update production before refreshing - ChocolateFactoryHelper.updateProduction((SkyBlockPlayer) player); - - // Re-setup all items to refresh values - setItems(new InventoryGUIOpenEvent(player, this, getInventory())); - } - - @Override - public int refreshRate() { - return 20; // Refresh every second (20 ticks) - } - - @Override - public boolean allowHotkeying() { - return false; - } - - @Override - public void onClose(InventoryCloseEvent e, CloseReason reason) { - } - - @Override - public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { - } - - @Override - public void onBottomClick(InventoryPreClickEvent e) { - e.setCancelled(true); + /** + * Opens the Chocolate Factory GUI for a player with auto-refresh every second. + */ + public static void open(SkyBlockPlayer player) { + ViewNavigator.get(player).push(new GUIChocolateFactory()).refreshEvery(Duration.ofSeconds(1)); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java new file mode 100644 index 000000000..51d2a5979 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java @@ -0,0 +1,240 @@ +package net.swofty.type.skyblockgeneric.gui.inventories; + +import net.minestom.server.event.inventory.InventoryCloseEvent; +import net.minestom.server.event.inventory.InventoryPreClickEvent; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; +import net.swofty.type.generic.gui.inventory.item.GUIItem; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateMilestone; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class GUIChocolateFactoryMilestones extends HypixelInventoryGUI { + // Slot mapping for milestones (milestone number -> slot) + // Layout based on the original design + private static final int[] MILESTONE_SLOTS = { + 27, // 1 + 18, // 2 + 9, // 3 + 0, // 4 + 1, // 5 + 2, // 6 + 11, // 7 + 20, // 8 + 29, // 9 + 30, // 10 + 31, // 11 + 22, // 12 + 13, // 13 + 4, // 14 + 5, // 15 + 6, // 16 + 15, // 17 + 24, // 18 + 33, // 19 + 34, // 20 + 35, // 21 + 26, // 22 + 17, // 23 + 8 // 24 + }; + + public GUIChocolateFactoryMilestones() { + super("Chocolate Factory Milestones", InventoryType.CHEST_6_ROW); + } + + @Override + public void setItems(InventoryGUIOpenEvent e) { + // Fill with black glass panes + fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + + SkyBlockPlayer player = (SkyBlockPlayer) e.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + long allTimeChocolate = data.getChocolateAllTime(); + + // Set milestone items + for (ChocolateMilestone milestone : ChocolateMilestone.values()) { + int slot = MILESTONE_SLOTS[milestone.getNumber() - 1]; + set(createMilestoneItem(slot, milestone, allTimeChocolate)); + } + + // Go Back button (slot 48) + set(new GUIClickableItem(48) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + GUIChocolateFactory.open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Factory"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + } + }); + + // Close button (slot 49) + set(GUIClickableItem.getCloseItem(49)); + } + + private GUIItem createMilestoneItem(int slot, ChocolateMilestone milestone, long allTimeChocolate) { + boolean unlocked = milestone.isUnlocked(allTimeChocolate); + + if (unlocked) { + // Show player head when unlocked + return new GUIItem(slot) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + return createUnlockedMilestoneItem(milestone); + } + }; + } else { + // Show colored glass pane when locked + return new GUIItem(slot) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + return createLockedMilestoneItem(milestone, data.getChocolateAllTime()); + } + }; + } + } + + private ItemStack.Builder createUnlockedMilestoneItem(ChocolateMilestone milestone) { + List lore = new ArrayList<>(); + String formattedReq = formatRequirement(milestone.getRequiredChocolate()); + + lore.add("§7Reach §6" + formattedReq + " Chocolate §7all-time to"); + lore.add("§7unlock this special §aChocolate Rabbit§7!"); + lore.add(""); + lore.add("§7Milestone " + milestone.getRomanNumeral() + " Reward"); + lore.add("§" + milestone.getColorCode() + milestone.getRabbitName()); + lore.add(""); + + if (milestone.getChocolateBonus() > 0) { + lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", milestone.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§7§6Chocolate Factory§7."); + } else { + lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } + lore.add(""); + lore.add("§a§lUNLOCKED"); + + return ItemStackCreator.getStackHead( + "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Chocolate Milestone", + milestone.getTextureId(), + 1, + lore + ); + } + + private ItemStack.Builder createLockedMilestoneItem(ChocolateMilestone milestone, long allTimeChocolate) { + List lore = new ArrayList<>(); + String formattedReq = formatRequirement(milestone.getRequiredChocolate()); + String formattedCurrent = formatRequirement(allTimeChocolate); + double progress = milestone.getProgress(allTimeChocolate); + + lore.add("§7Reach §6" + formattedReq + " Chocolate §7all-time to"); + lore.add("§7unlock this special §aChocolate Rabbit§7!"); + lore.add(""); + lore.add("§7Progress to Milestone " + milestone.getRomanNumeral() + ": §b" + String.format("%.0f", progress) + "%"); + lore.add(createProgressBar(progress) + " §b" + formattedCurrent + "§3/§b" + formattedReq); + lore.add(""); + lore.add("§7Milestone " + milestone.getRomanNumeral() + " Reward"); + lore.add("§" + milestone.getColorCode() + milestone.getRabbitName()); + lore.add(""); + + if (milestone.getChocolateBonus() > 0) { + lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", milestone.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§7§6Chocolate Factory§7."); + } else { + lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } + lore.add(""); + lore.add("§cRequires " + formattedReq + " all-time Chocolate!"); + + return ItemStackCreator.getStack( + "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Chocolate Milestone", + milestone.getGlassPaneMaterial(), + 1, + lore + ); + } + + private String createProgressBar(double progress) { + int filled = (int) (progress / 4); // 25 characters total, each represents 4% + int empty = 25 - filled; + + StringBuilder bar = new StringBuilder("§3§l§m"); + for (int i = 0; i < filled; i++) { + bar.append(" "); + } + bar.append("§f§l§m"); + for (int i = 0; i < empty; i++) { + bar.append(" "); + } + bar.append("§r"); + + return bar.toString(); + } + + private String formatRequirement(long amount) { + if (amount >= 1_000_000_000_000L) { + double val = amount / 1_000_000_000_000.0; + return val == (long) val ? String.format("%.0fT", val) : String.format("%.1fT", val); + } else if (amount >= 1_000_000_000L) { + double val = amount / 1_000_000_000.0; + return val == (long) val ? String.format("%.0fB", val) : String.format("%.1fB", val); + } else if (amount >= 1_000_000L) { + double val = amount / 1_000_000.0; + return val == (long) val ? String.format("%.0fM", val) : String.format("%.1fM", val); + } else if (amount >= 1_000L) { + double val = amount / 1_000.0; + return val == (long) val ? String.format("%.0fk", val) : String.format("%.1fk", val); + } + return String.valueOf(amount); + } + + private String getOrdinal(int number) { + String[] suffixes = {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"}; + if (number % 100 >= 11 && number % 100 <= 13) { + return number + "th"; + } + return number + suffixes[number % 10]; + } + + @Override + public boolean allowHotkeying() { + return false; + } + + @Override + public void onClose(InventoryCloseEvent e, CloseReason reason) { + } + + @Override + public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { + } + + @Override + public void onBottomClick(InventoryPreClickEvent e) { + e.setCancelled(true); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java new file mode 100644 index 000000000..93a7d7d24 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java @@ -0,0 +1,243 @@ +package net.swofty.type.skyblockgeneric.gui.inventories; + +import net.minestom.server.event.inventory.InventoryCloseEvent; +import net.minestom.server.event.inventory.InventoryPreClickEvent; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; +import net.swofty.type.generic.gui.inventory.item.GUIItem; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class GUIChocolateShop extends HypixelInventoryGUI { + private static final String SHOP_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; + + public GUIChocolateShop() { + super("Chocolate Shop", InventoryType.CHEST_6_ROW); + } + + @Override + public void setItems(InventoryGUIOpenEvent e) { + // Fill with black glass panes + fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + + SkyBlockPlayer player = (SkyBlockPlayer) e.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + // Slot 4: Shop Info + set(new GUIItem(4) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer skyPlayer = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData playerData = ChocolateFactoryHelper.getData(skyPlayer); + + List lore = new ArrayList<>(); + lore.add("§7Spend your §6Chocolate §7on the world's"); + lore.add("§7most delectable treats!"); + lore.add(""); + lore.add("§7Your Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(playerData.getChocolate())); + lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(playerData.getTotalChocolateSpent())); + + return ItemStackCreator.getStackHead("§6Chocolate Shop", SHOP_TEXTURE, 1, lore); + } + }); + + // Row 2: Shop Items + // Slot 10: Chocolate Rabbit (consumable) + set(createShopItem(10, "§6Chocolate Rabbit", Material.PLAYER_HEAD, + "§7A delicious chocolate rabbit that", + "§7restores §c+50 Health §7when consumed.", + 1000, SHOP_TEXTURE)); + + // Slot 11: Chocolate Bar + set(createShopItem(11, "§6Chocolate Bar", Material.COOKIE, + "§7A tasty chocolate bar that", + "§7restores §c+25 Health §7when consumed.", + 250, null)); + + // Slot 12: Golden Chocolate + set(createShopItem(12, "§6Golden Chocolate", Material.GOLD_INGOT, + "§7Premium chocolate infused with gold.", + "§7Grants §6+100 Chocolate §7when used.", + 5000, null)); + + // Slot 13: Time Tower Charge + set(createShopItem(13, "§dTime Tower Charge", Material.CLOCK, + "§7Adds §a+1 charge §7to your", + "§dTime Tower§7.", + 50000, null)); + + // Slot 14: Rabbit Foot + set(createShopItem(14, "§aLucky Rabbit Foot", Material.RABBIT_FOOT, + "§7Increases your chance of finding", + "§7rare rabbits by §a+5% §7for 1 hour.", + 25000, null)); + + // Slot 15: Cocoa Beans Pack + set(createShopItem(15, "§6Cocoa Beans Pack", Material.COCOA_BEANS, + "§7A pack of premium cocoa beans.", + "§7Grants §6+500 Chocolate §7instantly.", + 2500, null)); + + // Slot 16: Chocolate Fountain + set(createShopItem(16, "§5Chocolate Fountain", Material.CAULDRON, + "§7A decorative chocolate fountain", + "§7for your island.", + 100000, null)); + + // Row 3: More Items + // Slot 19: Rabbit Cage + set(createShopItem(19, "§aRabbit Cage", Material.IRON_BARS, + "§7Expands your §aRabbit Barn §7by", + "§a+5 slots §7permanently.", + 75000, null)); + + // Slot 20: Chocolate Recipe + set(createShopItem(20, "§9Chocolate Recipe", Material.PAPER, + "§7Learn a new chocolate recipe", + "§7for your factory.", + 10000, null)); + + // Slot 21: Rabbit Whistle + set(createShopItem(21, "§dRabbit Whistle", Material.GOAT_HORN, + "§7Summons a random rabbit to", + "§7help in your factory for 1 hour.", + 30000, null)); + + // Slot 22: Chocolate Essence + set(createShopItem(22, "§5Chocolate Essence", Material.EXPERIENCE_BOTTLE, + "§7Concentrated chocolate power.", + "§7Grants §6+1,000 Chocolate §7instantly.", + 5000, null)); + + // Slot 23: Factory Blueprint + set(createShopItem(23, "§6Factory Blueprint", Material.MAP, + "§7Unlocks additional factory", + "§7customization options.", + 200000, null)); + + // Slot 24: Chocolate Crown + set(createShopItem(24, "§6§lChocolate Crown", Material.GOLDEN_HELMET, + "§7The ultimate symbol of chocolate", + "§7mastery. Purely cosmetic.", + 1000000, null)); + + // Slot 25: Mystery Egg + set(createShopItem(25, "§d§lMystery Egg", Material.DRAGON_EGG, + "§7Contains a random rabbit!", + "§7Could be any rarity.", + 500000, null)); + + // Slot 31: Shop Milestones + set(new GUIClickableItem(31) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + new GUIChocolateShopMilestones().open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer skyPlayer = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData playerData = ChocolateFactoryHelper.getData(skyPlayer); + + List lore = new ArrayList<>(); + lore.add("§7Spend §6Chocolate §7in the shop to"); + lore.add("§7unlock special §aChocolate Rabbits§7!"); + lore.add(""); + lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(playerData.getTotalChocolateSpent())); + lore.add(""); + lore.add("§eClick to view milestones!"); + + return ItemStackCreator.getStack("§6Chocolate Shop Milestones", Material.LADDER, 1, lore); + } + }); + + // Slot 48: Go Back + set(new GUIClickableItem(48) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + GUIChocolateFactory.open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Factory"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + } + }); + + // Slot 49: Close + set(GUIClickableItem.getCloseItem(49)); + } + + private GUIClickableItem createShopItem(int slot, String name, Material material, + String desc1, String desc2, long cost, String textureId) { + return new GUIClickableItem(slot) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + if (data.getChocolate() >= cost) { + data.removeChocolate(cost); + data.addChocolateSpent(cost); + ChocolateFactoryHelper.getDatapoint(player).setValue(data); + player.sendMessage("§aPurchased " + name + " §afor §6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate§a!"); + + // Refresh the GUI + new GUIChocolateShop().open(player); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + } + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add(desc1); + lore.add(desc2); + lore.add(""); + lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to purchase!" : "§cNot enough Chocolate!"); + + if (textureId != null && material == Material.PLAYER_HEAD) { + return ItemStackCreator.getStackHead(name, textureId, 1, lore); + } + return ItemStackCreator.getStack(name, material, 1, lore); + } + }; + } + + @Override + public boolean allowHotkeying() { + return false; + } + + @Override + public void onClose(InventoryCloseEvent e, CloseReason reason) { + } + + @Override + public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { + } + + @Override + public void onBottomClick(InventoryPreClickEvent e) { + e.setCancelled(true); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java new file mode 100644 index 000000000..ea2e8e6fd --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java @@ -0,0 +1,240 @@ +package net.swofty.type.skyblockgeneric.gui.inventories; + +import net.minestom.server.event.inventory.InventoryCloseEvent; +import net.minestom.server.event.inventory.InventoryPreClickEvent; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; +import net.swofty.type.generic.gui.inventory.item.GUIItem; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateShopMilestone; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class GUIChocolateShopMilestones extends HypixelInventoryGUI { + // Slot mapping for milestones (milestone number -> slot) + // Layout based on the original design (spiral pattern) + private static final int[] MILESTONE_SLOTS = { + 27, // 1 + 18, // 2 + 9, // 3 + 0, // 4 + 1, // 5 + 2, // 6 + 11, // 7 + 20, // 8 + 29, // 9 + 30, // 10 + 31, // 11 + 22, // 12 + 13, // 13 + 4, // 14 + 5, // 15 + 6, // 16 + 15, // 17 + 24, // 18 + 33, // 19 + 34, // 20 + 35, // 21 + 26, // 22 + 17, // 23 + 8 // 24 + }; + + public GUIChocolateShopMilestones() { + super("Chocolate Shop Milestones", InventoryType.CHEST_6_ROW); + } + + @Override + public void setItems(InventoryGUIOpenEvent e) { + // Fill with black glass panes + fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + + SkyBlockPlayer player = (SkyBlockPlayer) e.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + long totalSpent = data.getTotalChocolateSpent(); + + // Set milestone items + for (ChocolateShopMilestone milestone : ChocolateShopMilestone.values()) { + int slot = MILESTONE_SLOTS[milestone.getNumber() - 1]; + set(createMilestoneItem(slot, milestone, totalSpent)); + } + + // Go Back button (slot 48) + set(new GUIClickableItem(48) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + new GUIChocolateShop().open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Shop"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + } + }); + + // Close button (slot 49) + set(GUIClickableItem.getCloseItem(49)); + } + + private GUIItem createMilestoneItem(int slot, ChocolateShopMilestone milestone, long totalSpent) { + boolean unlocked = milestone.isUnlocked(totalSpent); + + if (unlocked) { + // Show player head when unlocked + return new GUIItem(slot) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + return createUnlockedMilestoneItem(milestone); + } + }; + } else { + // Show colored glass pane when locked + return new GUIItem(slot) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + SkyBlockPlayer player = (SkyBlockPlayer) p; + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + return createLockedMilestoneItem(milestone, data.getTotalChocolateSpent()); + } + }; + } + } + + private ItemStack.Builder createUnlockedMilestoneItem(ChocolateShopMilestone milestone) { + List lore = new ArrayList<>(); + String formattedReq = formatRequirement(milestone.getRequiredSpent()); + + lore.add("§7Spend §6" + formattedReq + " Chocolate §7in the shop"); + lore.add("§7to unlock this special §aChocolate Rabbit§7!"); + lore.add(""); + lore.add("§7Milestone " + milestone.getRomanNumeral() + " Reward"); + lore.add("§" + milestone.getColorCode() + milestone.getRabbitName()); + lore.add(""); + + if (milestone.getChocolateBonus() > 0) { + lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", milestone.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§7§6Chocolate Factory§7."); + } else { + lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } + lore.add(""); + lore.add("§a§lUNLOCKED"); + + return ItemStackCreator.getStackHead( + "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Shop Milestone", + milestone.getTextureId(), + 1, + lore + ); + } + + private ItemStack.Builder createLockedMilestoneItem(ChocolateShopMilestone milestone, long totalSpent) { + List lore = new ArrayList<>(); + String formattedReq = formatRequirement(milestone.getRequiredSpent()); + String formattedCurrent = formatRequirement(totalSpent); + double progress = milestone.getProgress(totalSpent); + + lore.add("§7Spend §6" + formattedReq + " Chocolate §7in the shop"); + lore.add("§7to unlock this special §aChocolate Rabbit§7!"); + lore.add(""); + lore.add("§7Progress to Milestone " + milestone.getRomanNumeral() + ": §b" + String.format("%.0f", progress) + "%"); + lore.add(createProgressBar(progress) + " §b" + formattedCurrent + "§3/§b" + formattedReq); + lore.add(""); + lore.add("§7Milestone " + milestone.getRomanNumeral() + " Reward"); + lore.add("§" + milestone.getColorCode() + milestone.getRabbitName()); + lore.add(""); + + if (milestone.getChocolateBonus() > 0) { + lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", milestone.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§7§6Chocolate Factory§7."); + } else { + lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } + lore.add(""); + lore.add("§cRequires spending " + formattedReq + " Chocolate!"); + + return ItemStackCreator.getStack( + "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Shop Milestone", + milestone.getGlassPaneMaterial(), + 1, + lore + ); + } + + private String createProgressBar(double progress) { + int filled = (int) (progress / 4); // 25 characters total, each represents 4% + int empty = 25 - filled; + + StringBuilder bar = new StringBuilder("§3§l§m"); + for (int i = 0; i < filled; i++) { + bar.append(" "); + } + bar.append("§f§l§m"); + for (int i = 0; i < empty; i++) { + bar.append(" "); + } + bar.append("§r"); + + return bar.toString(); + } + + private String formatRequirement(long amount) { + if (amount >= 1_000_000_000_000L) { + double val = amount / 1_000_000_000_000.0; + return val == (long) val ? String.format("%.0fT", val) : String.format("%.1fT", val); + } else if (amount >= 1_000_000_000L) { + double val = amount / 1_000_000_000.0; + return val == (long) val ? String.format("%.0fB", val) : String.format("%.1fB", val); + } else if (amount >= 1_000_000L) { + double val = amount / 1_000_000.0; + return val == (long) val ? String.format("%.0fM", val) : String.format("%.1fM", val); + } else if (amount >= 1_000L) { + double val = amount / 1_000.0; + return val == (long) val ? String.format("%.0fk", val) : String.format("%.1fk", val); + } + return String.valueOf(amount); + } + + private String getOrdinal(int number) { + String[] suffixes = {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"}; + if (number % 100 >= 11 && number % 100 <= 13) { + return number + "th"; + } + return number + suffixes[number % 10]; + } + + @Override + public boolean allowHotkeying() { + return false; + } + + @Override + public void onClose(InventoryCloseEvent e, CloseReason reason) { + } + + @Override + public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { + } + + @Override + public void onBottomClick(InventoryPreClickEvent e) { + e.setCancelled(true); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java new file mode 100644 index 000000000..a8ed76a1c --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java @@ -0,0 +1,481 @@ +package net.swofty.type.skyblockgeneric.gui.inventories; + +import net.minestom.server.event.inventory.InventoryCloseEvent; +import net.minestom.server.event.inventory.InventoryPreClickEvent; +import net.minestom.server.inventory.click.Click; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; +import net.swofty.type.generic.gui.inventory.ItemStackCreator; +import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; +import net.swofty.type.generic.gui.inventory.item.GUIItem; +import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; +import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateRabbit; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.*; +import java.util.stream.Collectors; + +public class GUIHoppityCollection extends HypixelInventoryGUI { + private static final String HOPPITY_TEXTURE = "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692"; + private static final String LOCATION_TEXTURE = "d7cc6687423d0570d556ac53e0676cb563bbdd9717cd8269bdebed6f6d4e7bf8"; + + // Rabbit display slots (7 per row, 4 rows) + private static final int[] RABBIT_SLOTS = { + 10, 11, 12, 13, 14, 15, 16, + 19, 20, 21, 22, 23, 24, 25, + 28, 29, 30, 31, 32, 33, 34, + 37, 38, 39, 40, 41, 42, 43 + }; + + private static final int RABBITS_PER_PAGE = RABBIT_SLOTS.length; + private static final int TOTAL_RABBITS = 512; // Total rabbits in Hypixel + + private int currentPage; + private SortType sortType; + private FilterType filterType; + + public GUIHoppityCollection() { + this(1, SortType.A_TO_Z, FilterType.NONE); + } + + public GUIHoppityCollection(int page, SortType sortType, FilterType filterType) { + super(buildTitle(page, sortType, filterType), InventoryType.CHEST_6_ROW); + this.currentPage = page; + this.sortType = sortType; + this.filterType = filterType; + } + + private static String buildTitle(int page, SortType sortType, FilterType filterType) { + // Calculate total pages based on filtered rabbits (approximation for title) + int totalRabbits = ChocolateRabbit.values().length; + int totalPages = Math.max(1, (int) Math.ceil(totalRabbits / (double) RABBITS_PER_PAGE)); + return "(" + page + "/" + totalPages + ") Hoppity's Collection"; + } + + // Border slots (top row, bottom row, and left/right edges) + private static final int[] BORDER_SLOTS = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, // Top row + 9, 17, // Row 2 edges + 18, 26, // Row 3 edges + 27, 35, // Row 4 edges + 36, 44, // Row 5 edges + 45, 46, 47, 48, 49, 50, 51, 52, 53 // Bottom row + }; + + @Override + public void setItems(InventoryGUIOpenEvent e) { + SkyBlockPlayer player = (SkyBlockPlayer) e.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + Set foundRabbits = data.getFoundRabbits(); + + // Get and filter rabbits + List rabbits = getFilteredAndSortedRabbits(foundRabbits); + int totalPages = Math.max(1, (int) Math.ceil(rabbits.size() / (double) RABBITS_PER_PAGE)); + + // Only fill border with black glass panes + for (int slot : BORDER_SLOTS) { + set(new GUIItem(slot) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + return ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " "); + } + }); + } + + // Slot 4: Hoppity's Collection info + set(new GUIItem(4) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + return createCollectionInfoItem((SkyBlockPlayer) p); + } + }); + + // Set rabbit items for current page (empty slots will be AIR by default) + int startIndex = (currentPage - 1) * RABBITS_PER_PAGE; + for (int i = 0; i < RABBIT_SLOTS.length; i++) { + int rabbitIndex = startIndex + i; + int slot = RABBIT_SLOTS[i]; + + if (rabbitIndex < rabbits.size()) { + ChocolateRabbit rabbit = rabbits.get(rabbitIndex); + boolean found = foundRabbits.contains(rabbit.name()); + set(createRabbitItem(slot, rabbit, found)); + } + // Empty slots are left as AIR (no item) + } + + // Slot 47: Rabbit Locations + set(new GUIClickableItem(47) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + // TODO: Cycle through location filters + p.sendMessage("§7Rabbit Locations filter coming soon!"); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7Each rabbit has a specific location"); + lore.add("§7on a specific island where it can be"); + lore.add("§7found, just for fun."); + lore.add(""); + lore.add("§7The §9Hotspot §7of a Rabbit means that"); + lore.add("§7this season they have a §a50% §7higher"); + lore.add("§7chance to be found on this specific"); + lore.add("§7island."); + lore.add(""); + lore.add("§6Resident §7rabbits however, can §cONLY"); + lore.add("§7be found on their respective islands."); + lore.add(""); + lore.add("§7Currently selected: §aAll Rabbits"); + lore.add(""); + lore.add("§bRight-click to go backwards!"); + lore.add("§eClick to cycle!"); + + return ItemStackCreator.getStackHead("§9Rabbit Locations", LOCATION_TEXTURE, 1, lore); + } + }); + + // Slot 48: Go Back + set(new GUIClickableItem(48) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + GUIChocolateFactory.open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Factory"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + } + }); + + // Slot 49: Close + set(GUIClickableItem.getCloseItem(49)); + + // Slot 50: Sort + int finalTotalPages = totalPages; + set(new GUIClickableItem(50) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + boolean isRightClick = event.getClick() instanceof Click.Right; + SortType newSort = isRightClick + ? sortType.previous() + : sortType.next(); + new GUIHoppityCollection(1, newSort, filterType).open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add(""); + for (SortType type : SortType.values()) { + if (type == sortType) { + lore.add("§b▶ " + type.getDisplayName()); + } else { + lore.add("§7 " + type.getDisplayName()); + } + } + lore.add(""); + lore.add("§bRight-click to go backwards!"); + lore.add("§eClick to switch sort!"); + + return ItemStackCreator.getStack("§aSort", Material.HOPPER, 1, lore); + } + }); + + // Slot 51: Filter + set(new GUIClickableItem(51) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + boolean isRightClick = event.getClick() instanceof Click.Right; + FilterType newFilter = isRightClick + ? filterType.previous() + : filterType.next(); + new GUIHoppityCollection(1, sortType, newFilter).open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add(""); + for (FilterType type : FilterType.values()) { + if (type == filterType) { + lore.add("§a▶ " + type.getDisplayName()); + } else { + lore.add("§7 " + type.getDisplayName()); + } + } + lore.add(""); + lore.add("§bRight-click to go backwards!"); + lore.add("§eClick to switch!"); + + return ItemStackCreator.getStack("§aFilter", Material.ENDER_EYE, 1, lore); + } + }); + + // Slot 53: Next Page (if applicable) + if (currentPage < totalPages) { + set(new GUIClickableItem(53) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + new GUIHoppityCollection(currentPage + 1, sortType, filterType).open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§ePage " + (currentPage + 1)); + return ItemStackCreator.getStack("§aNext Page", Material.ARROW, 1, lore); + } + }); + } + + // Slot 45: Previous Page (if applicable) + if (currentPage > 1) { + set(new GUIClickableItem(45) { + @Override + public void run(InventoryPreClickEvent event, HypixelPlayer p) { + new GUIHoppityCollection(currentPage - 1, sortType, filterType).open((SkyBlockPlayer) p); + } + + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + lore.add("§ePage " + (currentPage - 1)); + return ItemStackCreator.getStack("§aPrevious Page", Material.ARROW, 1, lore); + } + }); + } + } + + private ItemStack.Builder createCollectionInfoItem(SkyBlockPlayer player) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + Set foundRabbits = data.getFoundRabbits(); + int found = foundRabbits.size(); + double percentage = (found / (double) TOTAL_RABBITS) * 100; + + // Calculate bonuses + int totalChocolate = 0; + double totalMultiplier = 0; + for (String rabbitName : foundRabbits) { + try { + ChocolateRabbit rabbit = ChocolateRabbit.valueOf(rabbitName); + totalChocolate += rabbit.getChocolateBonus(); + totalMultiplier += rabbit.getMultiplierBonus(); + } catch (IllegalArgumentException ignored) { + } + } + + List lore = new ArrayList<>(); + lore.add("§7Help §aHoppity §7find all of his §aChocolate"); + lore.add("§aRabbits §7during the §dHoppity's Hunt"); + lore.add("§7event!"); + lore.add(""); + lore.add("§7The more unique §aChocolate Rabbits"); + lore.add("§7that you find, the more your"); + lore.add("§6Chocolate Factory §7will produce!"); + lore.add(""); + lore.add("§7Finding duplicate Rabbits grants"); + lore.add("§a+10% §7extra §6Chocolate §7per duplicate,"); + lore.add("§7up to §a+100%§7!"); + lore.add(""); + lore.add("§7Rabbits Found: §e" + String.format("%.1f", percentage) + "§6%"); + lore.add(createProgressBar(percentage) + " §e" + found + "§6/§e" + TOTAL_RABBITS); + lore.add(""); + if (totalChocolate > 0) { + lore.add("§6+" + totalChocolate + " Chocolate per second"); + } + if (totalMultiplier > 0) { + lore.add("§6+" + String.format("%.3fx", totalMultiplier) + " Chocolate Multiplier"); + } + + return ItemStackCreator.getStackHead("§aHoppity's Collection", HOPPITY_TEXTURE, 1, lore); + } + + private GUIItem createRabbitItem(int slot, ChocolateRabbit rabbit, boolean found) { + return new GUIItem(slot) { + @Override + public ItemStack.Builder getItem(HypixelPlayer p) { + List lore = new ArrayList<>(); + + // Bonus info + if (rabbit.getRarity() == ChocolateRabbit.Rarity.LEGENDARY || + rabbit.getRarity() == ChocolateRabbit.Rarity.DIVINE || + rabbit.getRarity() == ChocolateRabbit.Rarity.MYTHIC) { + lore.add("§7Grants §6+" + String.format("%.2fx", rabbit.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } else { + lore.add("§7Grants §6+" + rabbit.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", rabbit.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§6Chocolate Factory§7."); + } + lore.add(""); + + // Obtain method if exists + if (rabbit.getObtainMethod() != null) { + lore.add("§7" + rabbit.getObtainMethod()); + lore.add(""); + } + + // Requirement if exists + if (rabbit.getRequirement() != null) { + lore.add("§c✖ §7Requirement"); + lore.add("§7" + rabbit.getRequirement()); + lore.add(""); + if (!found) { + lore.add("§8You cannot find this rabbit until you"); + lore.add("§8meet the requirement!"); + lore.add(""); + } + } + + // Found status + if (found) { + lore.add("§a§lFOUND"); + } else { + lore.add("§8You have not found this rabbit yet!"); + } + + // Location if exists (show as Resident label after found status) + if (rabbit.getLocation() != null) { + lore.add(rabbit.getResidentLabel()); + } + lore.add(""); + + // Rarity + lore.add(rabbit.getRarity().getFormattedName()); + + if (found) { + // Use a default rabbit head texture for found rabbits + return ItemStackCreator.getStackHead(rabbit.getFormattedName(), + "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692", 1, lore); + } else { + return ItemStackCreator.getStack(rabbit.getFormattedName(), Material.GRAY_DYE, 1, lore); + } + } + }; + } + + private List getFilteredAndSortedRabbits(Set foundRabbits) { + List rabbits = Arrays.stream(ChocolateRabbit.values()) + .filter(rabbit -> { + boolean found = foundRabbits.contains(rabbit.name()); + return switch (filterType) { + case NONE -> true; + case FOUND -> found; + case NOT_FOUND -> !found; + case HAS_REQUIREMENT -> rabbit.getRequirement() != null; + case NO_REQUIREMENT -> rabbit.getRequirement() == null; + }; + }) + .collect(Collectors.toList()); + + // Sort + switch (sortType) { + case A_TO_Z -> rabbits.sort(Comparator.comparing(ChocolateRabbit::getDisplayName)); + case Z_TO_A -> rabbits.sort(Comparator.comparing(ChocolateRabbit::getDisplayName).reversed()); + case HIGHEST_RARITY -> rabbits.sort(Comparator.comparing((ChocolateRabbit r) -> r.getRarity().ordinal()).reversed()); + case LOWEST_RARITY -> rabbits.sort(Comparator.comparing(r -> r.getRarity().ordinal())); + } + + return rabbits; + } + + private String createProgressBar(double progress) { + int filled = (int) (progress / 4); + int empty = 25 - filled; + + StringBuilder bar = new StringBuilder("§2§l§m"); + for (int i = 0; i < filled; i++) { + bar.append(" "); + } + bar.append("§f§l§m"); + for (int i = 0; i < empty; i++) { + bar.append(" "); + } + bar.append("§r"); + + return bar.toString(); + } + + @Override + public boolean allowHotkeying() { + return false; + } + + @Override + public void onClose(InventoryCloseEvent e, CloseReason reason) { + } + + @Override + public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { + } + + @Override + public void onBottomClick(InventoryPreClickEvent e) { + e.setCancelled(true); + } + + public enum SortType { + A_TO_Z("A to Z"), + Z_TO_A("Z to A"), + HIGHEST_RARITY("Highest Rarity"), + LOWEST_RARITY("Lowest Rarity"); + + private final String displayName; + + SortType(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } + + public SortType next() { + SortType[] values = values(); + return values[(ordinal() + 1) % values.length]; + } + + public SortType previous() { + SortType[] values = values(); + return values[(ordinal() - 1 + values.length) % values.length]; + } + } + + public enum FilterType { + NONE("None"), + FOUND("Rabbits Found"), + NOT_FOUND("Rabbits Not Found"), + HAS_REQUIREMENT("Has Requirement"), + NO_REQUIREMENT("No Requirement"); + + private final String displayName; + + FilterType(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } + + public FilterType next() { + FilterType[] values = values(); + return values[(ordinal() + 1) % values.length]; + } + + public FilterType previous() { + FilterType[] values = values(); + return values[(ordinal() - 1 + values.length) % values.length]; + } + } +} From 4629d3f62891c79bc843b36dfe4eeb0f8904e20c Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 14:14:53 -0300 Subject: [PATCH 05/13] Code Changes --- .../type/generic/entity/npc/HypixelNPC.java | 2 +- .../gui/v2/event/ActionInventoryOpen.java | 9 ++-- .../ChocolateFactoryProductionLoop.java | 39 --------------- .../chocolatefactory/ChocolateMilestone.java | 34 +------------ .../chocolatefactory/ChocolateRabbit.java | 40 ++-------------- .../ChocolateShopMilestone.java | 48 ++----------------- .../datapoints/DatapointChocolateFactory.java | 28 ++--------- 7 files changed, 19 insertions(+), 181 deletions(-) delete mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java 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 300dd8a06..f7416a81a 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 @@ -42,7 +42,7 @@ public abstract class HypixelNPC { public HypixelNPC(NPCConfiguration configuration) { this.parameters = configuration; String className = getClass().getSimpleName().replace("NPC", "").replace("Villager", ""); - this.name = parameters.chatName() != null ? parameters.chatName() : className.replaceAll("(?<=.)(?=\\p{Lu})", " "); + this.name = className.replaceAll("(?<=.)(?=\\p{Lu})", " "); this.dialogueController = new DialogueController(this); } diff --git a/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java b/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java index d80e652ae..0ebb95518 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java +++ b/type.generic/src/main/java/net/swofty/type/generic/gui/v2/event/ActionInventoryOpen.java @@ -15,12 +15,9 @@ public class ActionInventoryOpen implements HypixelEventClass { public void onPlayerInventoryOpen(InventoryOpenEvent event) { MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { HypixelPlayer player = (HypixelPlayer) event.getPlayer(); - ViewNavigator.find(player).ifPresent(navigator -> { - ViewSession session = navigator.getCurrentSession(); - if (session != null) { - session.onOpenEvent(event); - } - }); + ViewNavigator.find(player) + .map(ViewNavigator::getCurrentSession) + .ifPresent(session -> session.onOpenEvent(event)); }); } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java deleted file mode 100644 index 4b12b640a..000000000 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.swofty.type.skyblockgeneric.chocolatefactory; - -/** - * @deprecated Production is now calculated on-demand based on time elapsed since last update. - * This approach: - * - Works for offline production (calculates when player logs in or opens GUI) - * - Is more efficient (no constant ticking for all players) - * - Matches how Hypixel handles chocolate production - * - * Production is calculated in: - * - GUIChocolateFactory.setItems() - when GUI opens - * - GUIChocolateFactory.refreshItems() - while GUI is open - * - Any purchase/interaction that needs current chocolate amount - * - * The calculation uses lastUpdated timestamp to compute elapsed time and apply production rate. - */ -@Deprecated -public class ChocolateFactoryProductionLoop { - - /** - * @deprecated No longer needed. Production is calculated on-demand. - * This method is kept for backwards compatibility but does nothing. - */ - @Deprecated - public static void start() { - // Production is now calculated on-demand when: - // 1. Player opens Chocolate Factory GUI - // 2. GUI refreshes while open - // 3. Any chocolate-related action occurs - // - // This is handled by ChocolateFactoryHelper.updateProduction() - // which uses time-based calculation from lastUpdated timestamp. - // - // Benefits: - // - Works offline (production accumulates based on time) - // - More efficient (no server tick overhead) - // - Matches Hypixel's implementation - } -} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java index 8da1d6ad8..5bfbb072c 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java @@ -1,11 +1,13 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; +import lombok.Getter; import net.minestom.server.item.Material; /** * Represents chocolate factory milestones. * Each milestone unlocks a special rabbit when reaching a certain all-time chocolate threshold. */ +@Getter public enum ChocolateMilestone { // Common milestones (White) MILESTONE_1(1, 1_000L, "Almond Amaretto Rabbit", "f", 1, 0.002, @@ -86,38 +88,6 @@ public enum ChocolateMilestone { this.glassPaneMaterial = glassPaneMaterial; } - public int getNumber() { - return number; - } - - public long getRequiredChocolate() { - return requiredChocolate; - } - - public String getRabbitName() { - return rabbitName; - } - - public String getColorCode() { - return colorCode; - } - - public int getChocolateBonus() { - return chocolateBonus; - } - - public double getMultiplierBonus() { - return multiplierBonus; - } - - public String getTextureId() { - return textureId; - } - - public Material getGlassPaneMaterial() { - return glassPaneMaterial; - } - public String getRomanNumeral() { return toRoman(number); } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java index 999b9cd3e..54e15c33f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java @@ -1,9 +1,12 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; +import lombok.Getter; + /** * Represents all chocolate rabbits that can be collected in Hoppity's Collection. * Total: 513 rabbits across all rarities. */ +@Getter public enum ChocolateRabbit { // ==================== COMMON RABBITS (223) ==================== AARON("Aaron", Rarity.COMMON, null, null, null), @@ -545,26 +548,6 @@ public enum ChocolateRabbit { this.requirement = requirement; } - public String getDisplayName() { - return displayName; - } - - public Rarity getRarity() { - return rarity; - } - - public String getObtainMethod() { - return obtainMethod; - } - - public String getLocation() { - return location; - } - - public String getRequirement() { - return requirement; - } - public int getChocolateBonus() { return rarity.getChocolateBonus(); } @@ -639,6 +622,7 @@ public static int getRabbitCountByRarity(Rarity rarity) { return count; } + @Getter public enum Rarity { COMMON("f", "COMMON", 1, 0.002), UNCOMMON("a", "UNCOMMON", 2, 0.003), @@ -660,22 +644,6 @@ public enum Rarity { this.multiplierBonus = multiplierBonus; } - public String getColorCode() { - return colorCode; - } - - public String getDisplayName() { - return displayName; - } - - public int getChocolateBonus() { - return chocolateBonus; - } - - public double getMultiplierBonus() { - return multiplierBonus; - } - public String getFormattedName() { return "§" + colorCode + "§l" + displayName + " RABBIT"; } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java index aec815614..4bfe33234 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java @@ -1,11 +1,15 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; +import lombok.AllArgsConstructor; +import lombok.Getter; import net.minestom.server.item.Material; /** * Represents chocolate shop milestones. * Each milestone unlocks a special rabbit when spending a certain amount of chocolate in the shop. */ +@Getter +@AllArgsConstructor public enum ChocolateShopMilestone { // Common milestones (White) MILESTONE_1(1, 1_000L, "Alpha Rabbit", "f", 1, 0.002, @@ -74,50 +78,6 @@ public enum ChocolateShopMilestone { private final String textureId; private final Material glassPaneMaterial; - ChocolateShopMilestone(int number, long requiredSpent, String rabbitName, String colorCode, - int chocolateBonus, double multiplierBonus, String textureId, Material glassPaneMaterial) { - this.number = number; - this.requiredSpent = requiredSpent; - this.rabbitName = rabbitName; - this.colorCode = colorCode; - this.chocolateBonus = chocolateBonus; - this.multiplierBonus = multiplierBonus; - this.textureId = textureId; - this.glassPaneMaterial = glassPaneMaterial; - } - - public int getNumber() { - return number; - } - - public long getRequiredSpent() { - return requiredSpent; - } - - public String getRabbitName() { - return rabbitName; - } - - public String getColorCode() { - return colorCode; - } - - public int getChocolateBonus() { - return chocolateBonus; - } - - public double getMultiplierBonus() { - return multiplierBonus; - } - - public String getTextureId() { - return textureId; - } - - public Material getGlassPaneMaterial() { - return glassPaneMaterial; - } - public String getRomanNumeral() { return toRoman(number); } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java index 563885850..ef766ffdc 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java @@ -2,6 +2,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import net.swofty.commons.protocol.Serializer; import net.swofty.type.skyblockgeneric.data.SkyBlockDatapoint; @@ -152,12 +153,13 @@ public DatapointChocolateFactory(String key) { } @AllArgsConstructor + @NoArgsConstructor @Getter @Setter public static class ChocolateFactoryData { private long chocolate; private long chocolateAllTime; - private long lastUpdated; + private long lastUpdated = System.currentTimeMillis(); private double partialChocolate; // Accumulates fractional chocolate production // Upgrades @@ -171,38 +173,18 @@ public static class ChocolateFactoryData { private int coachJackrabbitLevel; // Employees (rabbit name -> employee data) - private Map employees; + private Map employees = new HashMap<>(); // Statistics private long totalClicks; private int totalTimeTowerUsages; // Collection - private Set foundRabbits; + private Set foundRabbits = new HashSet<>(); // Shop stats private long totalChocolateSpent; - public ChocolateFactoryData() { - this.chocolate = 0L; - this.chocolateAllTime = 0L; - this.lastUpdated = System.currentTimeMillis(); - this.partialChocolate = 0.0; - this.timeTowerLevel = 0; - this.timeTowerCharges = 0; - this.timeTowerLastUsed = 0L; - this.timeTowerActiveUntil = 0L; - this.rabbitBarnLevel = 0; - this.handBakedChocolateLevel = 0; - this.rabbitShrineLevel = 0; - this.coachJackrabbitLevel = 0; - this.employees = new HashMap<>(); - this.totalClicks = 0L; - this.totalTimeTowerUsages = 0; - this.foundRabbits = new HashSet<>(); - this.totalChocolateSpent = 0L; - } - /** * Adds chocolate and updates all-time total */ From a128ccf896738725b7bbfa00861e0275e2be7406 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 14:17:42 -0300 Subject: [PATCH 06/13] Fix --- .../java/net/swofty/type/generic/entity/npc/HypixelNPC.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 f7416a81a..3f6d7e574 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 @@ -42,7 +42,8 @@ public abstract class HypixelNPC { public HypixelNPC(NPCConfiguration configuration) { this.parameters = configuration; String className = getClass().getSimpleName().replace("NPC", "").replace("Villager", ""); - this.name = className.replaceAll("(?<=.)(?=\\p{Lu})", " "); + String defaultName = className.replaceAll("(?<=.)(?=\\p{Lu})", " "); + this.name = parameters.chatName() != null ? parameters.chatName() : defaultName; this.dialogueController = new DialogueController(this); } From e8c5524bd5e7cca127b6fe51b2a31f3bbb17c1c1 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 14:26:52 -0300 Subject: [PATCH 07/13] Fix --- .../command/commands/AdminMeCommand.java | 3 +- .../type/hub/npcs/ChocolateFactoryRank.java | 38 +++----- .../ChocolateFactoryHelper.java | 91 ++++++++++++------- .../chocolatefactory/ChocolateRabbit.java | 18 +--- .../gui/inventories/GUIChocolateShop.java | 2 +- .../skyblockgeneric/user/SkyBlockPlayer.java | 8 ++ 6 files changed, 85 insertions(+), 75 deletions(-) diff --git a/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java b/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java index d3974d8fe..618110742 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java +++ b/type.generic/src/main/java/net/swofty/type/generic/command/commands/AdminMeCommand.java @@ -25,8 +25,7 @@ public class AdminMeCommand extends HypixelCommand { "Swofty", "Foodzz", "Hamza_dev", - "ItzKatze", - "Vicente_1313" + "ItzKatze" ); @Override diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java index ad8197426..243d20f34 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java @@ -1,37 +1,29 @@ package net.swofty.type.hub.npcs; +import lombok.Getter; +import net.swofty.commons.ChatColor; + +@Getter public enum ChocolateFactoryRank { - UNEMPLOYED(0, "Unemployed", "§c"), - INTERN(1, "Intern", "§7"), - EMPLOYEE(20, "Employee", "§a"), - ASSISTANT(120, "Assistant", "§9"), - MANAGER(140, "Manager", "§5"), - DIRECTOR(180, "Director", "§6"), - EXECUTIVE(200, "Executive", "§d"), - BOARD_MEMBER(220, "Board Member", "§b"); + UNEMPLOYED(0, "Unemployed", ChatColor.RED), + INTERN(1, "Intern", ChatColor.GRAY), + EMPLOYEE(20, "Employee", ChatColor.GREEN), + ASSISTANT(120, "Assistant", ChatColor.BLUE), + MANAGER(140, "Manager", ChatColor.DARK_PURPLE), + DIRECTOR(180, "Director", ChatColor.GOLD), + EXECUTIVE(200, "Executive", ChatColor.LIGHT_PURPLE), + BOARD_MEMBER(220, "Board Member", ChatColor.AQUA); private final int level; private final String name; - private final String color; + private final ChatColor color; - ChocolateFactoryRank(int level, String name, String color) { + ChocolateFactoryRank(int level, String name, ChatColor color) { this.level = level; this.name = name; this.color = color; } - public int getLevel() { - return level; - } - - public String getName() { - return name; - } - - public String getColor() { - return color; - } - /** * Gets the formatted hologram line for this rank using the rank's base level. * Format: §7[Lvl X] §{color}RankName @@ -50,7 +42,7 @@ public String getHologramLine(int actualLevel) { if (this == UNEMPLOYED) { return color + name; } - return "§7[" + actualLevel + "] " + color + name; + return ChatColor.GRAY + "[" + actualLevel + "] " + color + name; } /** diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java index 72a4ee0f7..f419d3b0e 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -1,6 +1,5 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; -import net.swofty.type.skyblockgeneric.data.SkyBlockDataHandler; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; @@ -20,24 +19,14 @@ public class ChocolateFactoryHelper { * Gets the chocolate factory data for a player */ public static DatapointChocolateFactory.ChocolateFactoryData getData(SkyBlockPlayer player) { - return player.getSkyblockDataHandler() - .get(SkyBlockDataHandler.Data.CHOCOLATE_FACTORY, DatapointChocolateFactory.class) - .getValue(); - } - - /** - * Gets the chocolate factory datapoint for a player - */ - public static DatapointChocolateFactory getDatapoint(SkyBlockPlayer player) { - return player.getSkyblockDataHandler() - .get(SkyBlockDataHandler.Data.CHOCOLATE_FACTORY, DatapointChocolateFactory.class); + return player.getChocolateFactoryData(); } /** * Updates chocolate production for the player based on time elapsed */ public static void updateProduction(SkyBlockPlayer player) { - DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory datapoint = player.getChocolateFactoryDatapoint(); DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); data.updateChocolateFromProduction(); datapoint.setValue(data); @@ -47,7 +36,7 @@ public static void updateProduction(SkyBlockPlayer player) { * Handles a click on the chocolate cookie */ public static void handleClick(SkyBlockPlayer player) { - DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory datapoint = player.getChocolateFactoryDatapoint(); DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); data.click(); datapoint.setValue(data); @@ -413,32 +402,66 @@ public static void addTimeTowerCharge(SkyBlockPlayer player) { * Gets the prestige rank name based on level */ public static String getPrestigeRankName(int level) { - return switch (level) { - case 0 -> "Newcomer"; - case 1 -> "Apprentice"; - case 2 -> "Worker"; - case 3 -> "Journeyman"; - case 4 -> "Expert"; - case 5 -> "Master"; - case 6 -> "Grandmaster"; - default -> "Unknown"; - }; + return Prestige.fromLevel(level).getName(); } /** * Gets the prestige rank color based on level */ public static String getPrestigeRankColor(int level) { - return switch (level) { - case 0 -> "§7"; - case 1 -> "§a"; - case 2 -> "§9"; - case 3 -> "§5"; - case 4 -> "§6"; - case 5 -> "§d"; - case 6 -> "§b"; - default -> "§f"; - }; + return Prestige.fromLevel(level).getColor(); + } + + /** + * Gets the Prestige enum based on level + */ + public static Prestige getPrestige(int level) { + return Prestige.fromLevel(level); + } + + public enum Prestige { + NEWCOMER(0, "Newcomer", "§7"), + APPRENTICE(1, "Apprentice", "§a"), + WORKER(2, "Worker", "§9"), + JOURNEYMAN(3, "Journeyman", "§5"), + EXPERT(4, "Expert", "§6"), + MASTER(5, "Master", "§d"), + GRANDMASTER(6, "Grandmaster", "§b"); + + private final int level; + private final String name; + private final String color; + + Prestige(int level, String name, String color) { + this.level = level; + this.name = name; + this.color = color; + } + + public int getLevel() { + return level; + } + + public String getName() { + return name; + } + + public String getColor() { + return color; + } + + public String getFormattedName() { + return color + name; + } + + public static Prestige fromLevel(int level) { + for (Prestige prestige : values()) { + if (prestige.level == level) { + return prestige; + } + } + return NEWCOMER; + } } public enum UpgradeType { diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java index 54e15c33f..7381bc7a5 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateRabbit.java @@ -1,5 +1,6 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; +import lombok.AllArgsConstructor; import lombok.Getter; /** @@ -7,6 +8,7 @@ * Total: 513 rabbits across all rarities. */ @Getter +@AllArgsConstructor public enum ChocolateRabbit { // ==================== COMMON RABBITS (223) ==================== AARON("Aaron", Rarity.COMMON, null, null, null), @@ -540,14 +542,6 @@ public enum ChocolateRabbit { private final String location; private final String requirement; - ChocolateRabbit(String displayName, Rarity rarity, String obtainMethod, String location, String requirement) { - this.displayName = displayName; - this.rarity = rarity; - this.obtainMethod = obtainMethod; - this.location = location; - this.requirement = requirement; - } - public int getChocolateBonus() { return rarity.getChocolateBonus(); } @@ -623,6 +617,7 @@ public static int getRabbitCountByRarity(Rarity rarity) { } @Getter + @AllArgsConstructor public enum Rarity { COMMON("f", "COMMON", 1, 0.002), UNCOMMON("a", "UNCOMMON", 2, 0.003), @@ -637,13 +632,6 @@ public enum Rarity { private final int chocolateBonus; private final double multiplierBonus; - Rarity(String colorCode, String displayName, int chocolateBonus, double multiplierBonus) { - this.colorCode = colorCode; - this.displayName = displayName; - this.chocolateBonus = chocolateBonus; - this.multiplierBonus = multiplierBonus; - } - public String getFormattedName() { return "§" + colorCode + "§l" + displayName + " RABBIT"; } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java index 93a7d7d24..c53056acc 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java @@ -191,7 +191,7 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { if (data.getChocolate() >= cost) { data.removeChocolate(cost); data.addChocolateSpent(cost); - ChocolateFactoryHelper.getDatapoint(player).setValue(data); + player.getChocolateFactoryDatapoint().setValue(data); player.sendMessage("§aPurchased " + name + " §afor §6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate§a!"); // Refresh the GUI diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/user/SkyBlockPlayer.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/user/SkyBlockPlayer.java index 17aca8a03..fd092a3e9 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/user/SkyBlockPlayer.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/user/SkyBlockPlayer.java @@ -148,6 +148,14 @@ public DatapointArcheryPractice.ArcheryPracticeData getArcheryPracticeData() { return getSkyblockDataHandler().get(SkyBlockDataHandler.Data.ARCHERY_PRACTICE, DatapointArcheryPractice.class).getValue(); } + public DatapointChocolateFactory.ChocolateFactoryData getChocolateFactoryData() { + return getSkyblockDataHandler().get(SkyBlockDataHandler.Data.CHOCOLATE_FACTORY, DatapointChocolateFactory.class).getValue(); + } + + public DatapointChocolateFactory getChocolateFactoryDatapoint() { + return getSkyblockDataHandler().get(SkyBlockDataHandler.Data.CHOCOLATE_FACTORY, DatapointChocolateFactory.class); + } + public String getFullDisplayName(SkyBlockEmblems.SkyBlockEmblem displayEmblem, String levelColor) { DatapointSkyBlockExperience.PlayerSkyBlockExperience experience = getSkyBlockExperience(); From d7390bc3a098fcc6d112237094ba3a0a49b93136 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 14:34:43 -0300 Subject: [PATCH 08/13] StatefulView & Lombok --- .../ChocolateFactoryHelper.java | 2 + .../chocolatefactory/ChocolateMilestone.java | 14 +- .../GUIChocolateFactoryMilestones.java | 134 ++---- .../gui/inventories/GUIChocolateShop.java | 332 ++++++-------- .../GUIChocolateShopMilestones.java | 134 ++---- .../gui/inventories/GUIHoppityCollection.java | 429 +++++++----------- 6 files changed, 368 insertions(+), 677 deletions(-) diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java index f419d3b0e..4f998dd8f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -2,6 +2,7 @@ import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import lombok.experimental.UtilityClass; import java.text.DecimalFormat; import java.text.NumberFormat; @@ -11,6 +12,7 @@ * Helper class for Chocolate Factory operations. * Provides utility methods for production calculation, formatting, and player interactions. */ +@UtilityClass public class ChocolateFactoryHelper { private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance(Locale.US); private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.0"); diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java index 5bfbb072c..794080563 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java @@ -1,5 +1,6 @@ package net.swofty.type.skyblockgeneric.chocolatefactory; +import lombok.AllArgsConstructor; import lombok.Getter; import net.minestom.server.item.Material; @@ -8,6 +9,7 @@ * Each milestone unlocks a special rabbit when reaching a certain all-time chocolate threshold. */ @Getter +@AllArgsConstructor public enum ChocolateMilestone { // Common milestones (White) MILESTONE_1(1, 1_000L, "Almond Amaretto Rabbit", "f", 1, 0.002, @@ -76,18 +78,6 @@ public enum ChocolateMilestone { private final String textureId; private final Material glassPaneMaterial; - ChocolateMilestone(int number, long requiredChocolate, String rabbitName, String colorCode, - int chocolateBonus, double multiplierBonus, String textureId, Material glassPaneMaterial) { - this.number = number; - this.requiredChocolate = requiredChocolate; - this.rabbitName = rabbitName; - this.colorCode = colorCode; - this.chocolateBonus = chocolateBonus; - this.multiplierBonus = multiplierBonus; - this.textureId = textureId; - this.glassPaneMaterial = glassPaneMaterial; - } - public String getRomanNumeral() { return toRoman(number); } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java index 51d2a5979..394fc8e46 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java @@ -1,16 +1,11 @@ package net.swofty.type.skyblockgeneric.gui.inventories; -import net.minestom.server.event.inventory.InventoryCloseEvent; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; import net.swofty.type.generic.gui.inventory.ItemStackCreator; -import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; -import net.swofty.type.generic.gui.inventory.item.GUIItem; -import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.generic.gui.v2.*; +import net.swofty.type.generic.gui.v2.context.ViewContext; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateMilestone; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; @@ -19,96 +14,53 @@ import java.util.ArrayList; import java.util.List; -public class GUIChocolateFactoryMilestones extends HypixelInventoryGUI { - // Slot mapping for milestones (milestone number -> slot) - // Layout based on the original design +public class GUIChocolateFactoryMilestones implements StatefulView { private static final int[] MILESTONE_SLOTS = { - 27, // 1 - 18, // 2 - 9, // 3 - 0, // 4 - 1, // 5 - 2, // 6 - 11, // 7 - 20, // 8 - 29, // 9 - 30, // 10 - 31, // 11 - 22, // 12 - 13, // 13 - 4, // 14 - 5, // 15 - 6, // 16 - 15, // 17 - 24, // 18 - 33, // 19 - 34, // 20 - 35, // 21 - 26, // 22 - 17, // 23 - 8 // 24 + 27, 18, 9, 0, 1, 2, 11, 20, 29, 30, 31, 22, + 13, 4, 5, 6, 15, 24, 33, 34, 35, 26, 17, 8 }; - public GUIChocolateFactoryMilestones() { - super("Chocolate Factory Milestones", InventoryType.CHEST_6_ROW); + public record State() {} + + @Override + public State initialState() { + return new State(); } @Override - public void setItems(InventoryGUIOpenEvent e) { - // Fill with black glass panes - fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + public ViewConfiguration configuration() { + return ViewConfiguration.withString((state, ctx) -> "Chocolate Factory Milestones", InventoryType.CHEST_6_ROW); + } - SkyBlockPlayer player = (SkyBlockPlayer) e.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - long allTimeChocolate = data.getChocolateAllTime(); + @Override + public void layout(ViewLayout layout, State state, ViewContext ctx) { + Components.fill(layout); // Set milestone items for (ChocolateMilestone milestone : ChocolateMilestone.values()) { int slot = MILESTONE_SLOTS[milestone.getNumber() - 1]; - set(createMilestoneItem(slot, milestone, allTimeChocolate)); + layout.slot(slot, (s, c) -> { + SkyBlockPlayer player = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + long allTimeChocolate = data.getChocolateAllTime(); + + if (milestone.isUnlocked(allTimeChocolate)) { + return createUnlockedMilestoneItem(milestone); + } else { + return createLockedMilestoneItem(milestone, allTimeChocolate); + } + }); } // Go Back button (slot 48) - set(new GUIClickableItem(48) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - GUIChocolateFactory.open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Factory"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - } - }); + layout.slot(48, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Factory"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + }, (click, c) -> GUIChocolateFactory.open((SkyBlockPlayer) c.player())); // Close button (slot 49) - set(GUIClickableItem.getCloseItem(49)); - } - - private GUIItem createMilestoneItem(int slot, ChocolateMilestone milestone, long allTimeChocolate) { - boolean unlocked = milestone.isUnlocked(allTimeChocolate); - - if (unlocked) { - // Show player head when unlocked - return new GUIItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - return createUnlockedMilestoneItem(milestone); - } - }; - } else { - // Show colored glass pane when locked - return new GUIItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - return createLockedMilestoneItem(milestone, data.getChocolateAllTime()); - } - }; - } + Components.close(layout, 49); } private ItemStack.Builder createUnlockedMilestoneItem(ChocolateMilestone milestone) { @@ -179,7 +131,7 @@ private ItemStack.Builder createLockedMilestoneItem(ChocolateMilestone milestone } private String createProgressBar(double progress) { - int filled = (int) (progress / 4); // 25 characters total, each represents 4% + int filled = (int) (progress / 4); int empty = 25 - filled; StringBuilder bar = new StringBuilder("§3§l§m"); @@ -220,21 +172,7 @@ private String getOrdinal(int number) { return number + suffixes[number % 10]; } - @Override - public boolean allowHotkeying() { - return false; - } - - @Override - public void onClose(InventoryCloseEvent e, CloseReason reason) { - } - - @Override - public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { - } - - @Override - public void onBottomClick(InventoryPreClickEvent e) { - e.setCancelled(true); + public static void open(SkyBlockPlayer player) { + ViewNavigator.get(player).push(new GUIChocolateFactoryMilestones()); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java index c53056acc..de26e4dfe 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java @@ -1,16 +1,11 @@ package net.swofty.type.skyblockgeneric.gui.inventories; -import net.minestom.server.event.inventory.InventoryCloseEvent; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; import net.swofty.type.generic.gui.inventory.ItemStackCreator; -import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; -import net.swofty.type.generic.gui.inventory.item.GUIItem; -import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.generic.gui.v2.*; +import net.swofty.type.generic.gui.v2.context.ViewContext; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; @@ -18,226 +13,163 @@ import java.util.ArrayList; import java.util.List; -public class GUIChocolateShop extends HypixelInventoryGUI { +public class GUIChocolateShop implements StatefulView { private static final String SHOP_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; - public GUIChocolateShop() { - super("Chocolate Shop", InventoryType.CHEST_6_ROW); + public record State() {} + + @Override + public State initialState() { + return new State(); + } + + @Override + public ViewConfiguration configuration() { + return ViewConfiguration.withString((state, ctx) -> "Chocolate Shop", InventoryType.CHEST_6_ROW); } @Override - public void setItems(InventoryGUIOpenEvent e) { - // Fill with black glass panes - fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + public void layout(ViewLayout layout, State state, ViewContext ctx) { + Components.fill(layout); - SkyBlockPlayer player = (SkyBlockPlayer) e.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + SkyBlockPlayer player = (SkyBlockPlayer) ctx.player(); // Slot 4: Shop Info - set(new GUIItem(4) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer skyPlayer = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData playerData = ChocolateFactoryHelper.getData(skyPlayer); - - List lore = new ArrayList<>(); - lore.add("§7Spend your §6Chocolate §7on the world's"); - lore.add("§7most delectable treats!"); - lore.add(""); - lore.add("§7Your Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(playerData.getChocolate())); - lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(playerData.getTotalChocolateSpent())); - - return ItemStackCreator.getStackHead("§6Chocolate Shop", SHOP_TEXTURE, 1, lore); - } + layout.slot(4, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(p); + + List lore = new ArrayList<>(); + lore.add("§7Spend your §6Chocolate §7on the world's"); + lore.add("§7most delectable treats!"); + lore.add(""); + lore.add("§7Your Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(data.getChocolate())); + lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(data.getTotalChocolateSpent())); + + return ItemStackCreator.getStackHead("§6Chocolate Shop", SHOP_TEXTURE, 1, lore); }); // Row 2: Shop Items - // Slot 10: Chocolate Rabbit (consumable) - set(createShopItem(10, "§6Chocolate Rabbit", Material.PLAYER_HEAD, - "§7A delicious chocolate rabbit that", - "§7restores §c+50 Health §7when consumed.", - 1000, SHOP_TEXTURE)); - - // Slot 11: Chocolate Bar - set(createShopItem(11, "§6Chocolate Bar", Material.COOKIE, - "§7A tasty chocolate bar that", - "§7restores §c+25 Health §7when consumed.", - 250, null)); - - // Slot 12: Golden Chocolate - set(createShopItem(12, "§6Golden Chocolate", Material.GOLD_INGOT, - "§7Premium chocolate infused with gold.", - "§7Grants §6+100 Chocolate §7when used.", - 5000, null)); - - // Slot 13: Time Tower Charge - set(createShopItem(13, "§dTime Tower Charge", Material.CLOCK, - "§7Adds §a+1 charge §7to your", - "§dTime Tower§7.", - 50000, null)); - - // Slot 14: Rabbit Foot - set(createShopItem(14, "§aLucky Rabbit Foot", Material.RABBIT_FOOT, - "§7Increases your chance of finding", - "§7rare rabbits by §a+5% §7for 1 hour.", - 25000, null)); - - // Slot 15: Cocoa Beans Pack - set(createShopItem(15, "§6Cocoa Beans Pack", Material.COCOA_BEANS, - "§7A pack of premium cocoa beans.", - "§7Grants §6+500 Chocolate §7instantly.", - 2500, null)); - - // Slot 16: Chocolate Fountain - set(createShopItem(16, "§5Chocolate Fountain", Material.CAULDRON, - "§7A decorative chocolate fountain", - "§7for your island.", - 100000, null)); + createShopSlot(layout, 10, "§6Chocolate Rabbit", Material.PLAYER_HEAD, + "§7A delicious chocolate rabbit that", "§7restores §c+50 Health §7when consumed.", + 1000, SHOP_TEXTURE); + + createShopSlot(layout, 11, "§6Chocolate Bar", Material.COOKIE, + "§7A tasty chocolate bar that", "§7restores §c+25 Health §7when consumed.", + 250, null); + + createShopSlot(layout, 12, "§6Golden Chocolate", Material.GOLD_INGOT, + "§7Premium chocolate infused with gold.", "§7Grants §6+100 Chocolate §7when used.", + 5000, null); + + createShopSlot(layout, 13, "§dTime Tower Charge", Material.CLOCK, + "§7Adds §a+1 charge §7to your", "§dTime Tower§7.", + 50000, null); + + createShopSlot(layout, 14, "§aLucky Rabbit Foot", Material.RABBIT_FOOT, + "§7Increases your chance of finding", "§7rare rabbits by §a+5% §7for 1 hour.", + 25000, null); + + createShopSlot(layout, 15, "§6Cocoa Beans Pack", Material.COCOA_BEANS, + "§7A pack of premium cocoa beans.", "§7Grants §6+500 Chocolate §7instantly.", + 2500, null); + + createShopSlot(layout, 16, "§5Chocolate Fountain", Material.CAULDRON, + "§7A decorative chocolate fountain", "§7for your island.", + 100000, null); // Row 3: More Items - // Slot 19: Rabbit Cage - set(createShopItem(19, "§aRabbit Cage", Material.IRON_BARS, - "§7Expands your §aRabbit Barn §7by", - "§a+5 slots §7permanently.", - 75000, null)); - - // Slot 20: Chocolate Recipe - set(createShopItem(20, "§9Chocolate Recipe", Material.PAPER, - "§7Learn a new chocolate recipe", - "§7for your factory.", - 10000, null)); - - // Slot 21: Rabbit Whistle - set(createShopItem(21, "§dRabbit Whistle", Material.GOAT_HORN, - "§7Summons a random rabbit to", - "§7help in your factory for 1 hour.", - 30000, null)); - - // Slot 22: Chocolate Essence - set(createShopItem(22, "§5Chocolate Essence", Material.EXPERIENCE_BOTTLE, - "§7Concentrated chocolate power.", - "§7Grants §6+1,000 Chocolate §7instantly.", - 5000, null)); - - // Slot 23: Factory Blueprint - set(createShopItem(23, "§6Factory Blueprint", Material.MAP, - "§7Unlocks additional factory", - "§7customization options.", - 200000, null)); - - // Slot 24: Chocolate Crown - set(createShopItem(24, "§6§lChocolate Crown", Material.GOLDEN_HELMET, - "§7The ultimate symbol of chocolate", - "§7mastery. Purely cosmetic.", - 1000000, null)); - - // Slot 25: Mystery Egg - set(createShopItem(25, "§d§lMystery Egg", Material.DRAGON_EGG, - "§7Contains a random rabbit!", - "§7Could be any rarity.", - 500000, null)); + createShopSlot(layout, 19, "§aRabbit Cage", Material.IRON_BARS, + "§7Expands your §aRabbit Barn §7by", "§a+5 slots §7permanently.", + 75000, null); - // Slot 31: Shop Milestones - set(new GUIClickableItem(31) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - new GUIChocolateShopMilestones().open((SkyBlockPlayer) p); - } + createShopSlot(layout, 20, "§9Chocolate Recipe", Material.PAPER, + "§7Learn a new chocolate recipe", "§7for your factory.", + 10000, null); - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer skyPlayer = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData playerData = ChocolateFactoryHelper.getData(skyPlayer); + createShopSlot(layout, 21, "§dRabbit Whistle", Material.GOAT_HORN, + "§7Summons a random rabbit to", "§7help in your factory for 1 hour.", + 30000, null); - List lore = new ArrayList<>(); - lore.add("§7Spend §6Chocolate §7in the shop to"); - lore.add("§7unlock special §aChocolate Rabbits§7!"); - lore.add(""); - lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(playerData.getTotalChocolateSpent())); - lore.add(""); - lore.add("§eClick to view milestones!"); + createShopSlot(layout, 22, "§5Chocolate Essence", Material.EXPERIENCE_BOTTLE, + "§7Concentrated chocolate power.", "§7Grants §6+1,000 Chocolate §7instantly.", + 5000, null); - return ItemStackCreator.getStack("§6Chocolate Shop Milestones", Material.LADDER, 1, lore); - } - }); + createShopSlot(layout, 23, "§6Factory Blueprint", Material.MAP, + "§7Unlocks additional factory", "§7customization options.", + 200000, null); - // Slot 48: Go Back - set(new GUIClickableItem(48) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - GUIChocolateFactory.open((SkyBlockPlayer) p); - } + createShopSlot(layout, 24, "§6§lChocolate Crown", Material.GOLDEN_HELMET, + "§7The ultimate symbol of chocolate", "§7mastery. Purely cosmetic.", + 1000000, null); - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Factory"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - } - }); + createShopSlot(layout, 25, "§d§lMystery Egg", Material.DRAGON_EGG, + "§7Contains a random rabbit!", "§7Could be any rarity.", + 500000, null); - // Slot 49: Close - set(GUIClickableItem.getCloseItem(49)); - } + // Slot 31: Shop Milestones + layout.slot(31, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(p); - private GUIClickableItem createShopItem(int slot, String name, Material material, - String desc1, String desc2, long cost, String textureId) { - return new GUIClickableItem(slot) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - if (data.getChocolate() >= cost) { - data.removeChocolate(cost); - data.addChocolateSpent(cost); - player.getChocolateFactoryDatapoint().setValue(data); - player.sendMessage("§aPurchased " + name + " §afor §6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate§a!"); - - // Refresh the GUI - new GUIChocolateShop().open(player); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - } - } + List lore = new ArrayList<>(); + lore.add("§7Spend §6Chocolate §7in the shop to"); + lore.add("§7unlock special §aChocolate Rabbits§7!"); + lore.add(""); + lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(data.getTotalChocolateSpent())); + lore.add(""); + lore.add("§eClick to view milestones!"); - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - boolean canAfford = data.getChocolate() >= cost; - - List lore = new ArrayList<>(); - lore.add(desc1); - lore.add(desc2); - lore.add(""); - lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); - lore.add(""); - lore.add(canAfford ? "§eClick to purchase!" : "§cNot enough Chocolate!"); - - if (textureId != null && material == Material.PLAYER_HEAD) { - return ItemStackCreator.getStackHead(name, textureId, 1, lore); - } - return ItemStackCreator.getStack(name, material, 1, lore); - } - }; - } + return ItemStackCreator.getStack("§6Chocolate Shop Milestones", Material.LADDER, 1, lore); + }, (click, c) -> GUIChocolateShopMilestones.open((SkyBlockPlayer) c.player())); - @Override - public boolean allowHotkeying() { - return false; - } + // Slot 48: Go Back + layout.slot(48, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Factory"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + }, (click, c) -> GUIChocolateFactory.open((SkyBlockPlayer) c.player())); - @Override - public void onClose(InventoryCloseEvent e, CloseReason reason) { + // Slot 49: Close + Components.close(layout, 49); } - @Override - public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { + private void createShopSlot(ViewLayout layout, int slot, String name, Material material, + String desc1, String desc2, long cost, String textureId) { + layout.slot(slot, (s, c) -> { + SkyBlockPlayer player = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + boolean canAfford = data.getChocolate() >= cost; + + List lore = new ArrayList<>(); + lore.add(desc1); + lore.add(desc2); + lore.add(""); + lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add(""); + lore.add(canAfford ? "§eClick to purchase!" : "§cNot enough Chocolate!"); + + if (textureId != null && material == Material.PLAYER_HEAD) { + return ItemStackCreator.getStackHead(name, textureId, 1, lore); + } + return ItemStackCreator.getStack(name, material, 1, lore); + }, (click, c) -> { + SkyBlockPlayer player = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + if (data.getChocolate() >= cost) { + data.removeChocolate(cost); + data.addChocolateSpent(cost); + player.getChocolateFactoryDatapoint().setValue(data); + player.sendMessage("§aPurchased " + name + " §afor §6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate§a!"); + c.session(State.class).refresh(); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + } + }); } - @Override - public void onBottomClick(InventoryPreClickEvent e) { - e.setCancelled(true); + public static void open(SkyBlockPlayer player) { + ViewNavigator.get(player).push(new GUIChocolateShop()); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java index ea2e8e6fd..887c9210c 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java @@ -1,16 +1,11 @@ package net.swofty.type.skyblockgeneric.gui.inventories; -import net.minestom.server.event.inventory.InventoryCloseEvent; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.InventoryType; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; import net.swofty.type.generic.gui.inventory.ItemStackCreator; -import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; -import net.swofty.type.generic.gui.inventory.item.GUIItem; -import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.generic.gui.v2.*; +import net.swofty.type.generic.gui.v2.context.ViewContext; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateShopMilestone; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; @@ -19,96 +14,53 @@ import java.util.ArrayList; import java.util.List; -public class GUIChocolateShopMilestones extends HypixelInventoryGUI { - // Slot mapping for milestones (milestone number -> slot) - // Layout based on the original design (spiral pattern) +public class GUIChocolateShopMilestones implements StatefulView { private static final int[] MILESTONE_SLOTS = { - 27, // 1 - 18, // 2 - 9, // 3 - 0, // 4 - 1, // 5 - 2, // 6 - 11, // 7 - 20, // 8 - 29, // 9 - 30, // 10 - 31, // 11 - 22, // 12 - 13, // 13 - 4, // 14 - 5, // 15 - 6, // 16 - 15, // 17 - 24, // 18 - 33, // 19 - 34, // 20 - 35, // 21 - 26, // 22 - 17, // 23 - 8 // 24 + 27, 18, 9, 0, 1, 2, 11, 20, 29, 30, 31, 22, + 13, 4, 5, 6, 15, 24, 33, 34, 35, 26, 17, 8 }; - public GUIChocolateShopMilestones() { - super("Chocolate Shop Milestones", InventoryType.CHEST_6_ROW); + public record State() {} + + @Override + public State initialState() { + return new State(); } @Override - public void setItems(InventoryGUIOpenEvent e) { - // Fill with black glass panes - fill(ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); + public ViewConfiguration configuration() { + return ViewConfiguration.withString((state, ctx) -> "Chocolate Shop Milestones", InventoryType.CHEST_6_ROW); + } - SkyBlockPlayer player = (SkyBlockPlayer) e.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - long totalSpent = data.getTotalChocolateSpent(); + @Override + public void layout(ViewLayout layout, State state, ViewContext ctx) { + Components.fill(layout); // Set milestone items for (ChocolateShopMilestone milestone : ChocolateShopMilestone.values()) { int slot = MILESTONE_SLOTS[milestone.getNumber() - 1]; - set(createMilestoneItem(slot, milestone, totalSpent)); + layout.slot(slot, (s, c) -> { + SkyBlockPlayer player = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + long totalSpent = data.getTotalChocolateSpent(); + + if (milestone.isUnlocked(totalSpent)) { + return createUnlockedMilestoneItem(milestone); + } else { + return createLockedMilestoneItem(milestone, totalSpent); + } + }); } // Go Back button (slot 48) - set(new GUIClickableItem(48) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - new GUIChocolateShop().open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Shop"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - } - }); + layout.slot(48, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Shop"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + }, (click, c) -> GUIChocolateShop.open((SkyBlockPlayer) c.player())); // Close button (slot 49) - set(GUIClickableItem.getCloseItem(49)); - } - - private GUIItem createMilestoneItem(int slot, ChocolateShopMilestone milestone, long totalSpent) { - boolean unlocked = milestone.isUnlocked(totalSpent); - - if (unlocked) { - // Show player head when unlocked - return new GUIItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - return createUnlockedMilestoneItem(milestone); - } - }; - } else { - // Show colored glass pane when locked - return new GUIItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - SkyBlockPlayer player = (SkyBlockPlayer) p; - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - return createLockedMilestoneItem(milestone, data.getTotalChocolateSpent()); - } - }; - } + Components.close(layout, 49); } private ItemStack.Builder createUnlockedMilestoneItem(ChocolateShopMilestone milestone) { @@ -179,7 +131,7 @@ private ItemStack.Builder createLockedMilestoneItem(ChocolateShopMilestone miles } private String createProgressBar(double progress) { - int filled = (int) (progress / 4); // 25 characters total, each represents 4% + int filled = (int) (progress / 4); int empty = 25 - filled; StringBuilder bar = new StringBuilder("§3§l§m"); @@ -220,21 +172,7 @@ private String getOrdinal(int number) { return number + suffixes[number % 10]; } - @Override - public boolean allowHotkeying() { - return false; - } - - @Override - public void onClose(InventoryCloseEvent e, CloseReason reason) { - } - - @Override - public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { - } - - @Override - public void onBottomClick(InventoryPreClickEvent e) { - e.setCancelled(true); + public static void open(SkyBlockPlayer player) { + ViewNavigator.get(player).push(new GUIChocolateShopMilestones()); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java index a8ed76a1c..d60ac417e 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java @@ -1,17 +1,13 @@ package net.swofty.type.skyblockgeneric.gui.inventories; -import net.minestom.server.event.inventory.InventoryCloseEvent; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.inventory.click.Click; -import net.minestom.server.inventory.Inventory; +import lombok.Getter; import net.minestom.server.inventory.InventoryType; +import net.minestom.server.inventory.click.Click; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; import net.swofty.type.generic.gui.inventory.ItemStackCreator; -import net.swofty.type.generic.gui.inventory.item.GUIClickableItem; -import net.swofty.type.generic.gui.inventory.item.GUIItem; -import net.swofty.type.generic.user.HypixelPlayer; +import net.swofty.type.generic.gui.v2.*; +import net.swofty.type.generic.gui.v2.context.ViewContext; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateRabbit; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; @@ -20,11 +16,10 @@ import java.util.*; import java.util.stream.Collectors; -public class GUIHoppityCollection extends HypixelInventoryGUI { +public class GUIHoppityCollection implements StatefulView { private static final String HOPPITY_TEXTURE = "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692"; private static final String LOCATION_TEXTURE = "d7cc6687423d0570d556ac53e0676cb563bbdd9717cd8269bdebed6f6d4e7bf8"; - // Rabbit display slots (7 per row, 4 rows) private static final int[] RABBIT_SLOTS = { 10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25, @@ -33,225 +28,156 @@ public class GUIHoppityCollection extends HypixelInventoryGUI { }; private static final int RABBITS_PER_PAGE = RABBIT_SLOTS.length; - private static final int TOTAL_RABBITS = 512; // Total rabbits in Hypixel + private static final int TOTAL_RABBITS = 512; - private int currentPage; - private SortType sortType; - private FilterType filterType; + private static final int[] BORDER_SLOTS = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 17, 18, 26, 27, 35, 36, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53 + }; - public GUIHoppityCollection() { - this(1, SortType.A_TO_Z, FilterType.NONE); - } + public record State(int page, SortType sortType, FilterType filterType) {} - public GUIHoppityCollection(int page, SortType sortType, FilterType filterType) { - super(buildTitle(page, sortType, filterType), InventoryType.CHEST_6_ROW); - this.currentPage = page; - this.sortType = sortType; - this.filterType = filterType; + @Override + public State initialState() { + return new State(1, SortType.A_TO_Z, FilterType.NONE); } - private static String buildTitle(int page, SortType sortType, FilterType filterType) { - // Calculate total pages based on filtered rabbits (approximation for title) - int totalRabbits = ChocolateRabbit.values().length; - int totalPages = Math.max(1, (int) Math.ceil(totalRabbits / (double) RABBITS_PER_PAGE)); - return "(" + page + "/" + totalPages + ") Hoppity's Collection"; + @Override + public ViewConfiguration configuration() { + return ViewConfiguration.withString((state, ctx) -> { + int totalRabbits = ChocolateRabbit.values().length; + int totalPages = Math.max(1, (int) Math.ceil(totalRabbits / (double) RABBITS_PER_PAGE)); + return "(" + state.page() + "/" + totalPages + ") Hoppity's Collection"; + }, InventoryType.CHEST_6_ROW); } - // Border slots (top row, bottom row, and left/right edges) - private static final int[] BORDER_SLOTS = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, // Top row - 9, 17, // Row 2 edges - 18, 26, // Row 3 edges - 27, 35, // Row 4 edges - 36, 44, // Row 5 edges - 45, 46, 47, 48, 49, 50, 51, 52, 53 // Bottom row - }; - @Override - public void setItems(InventoryGUIOpenEvent e) { - SkyBlockPlayer player = (SkyBlockPlayer) e.player(); + public void layout(ViewLayout layout, State state, ViewContext ctx) { + SkyBlockPlayer player = (SkyBlockPlayer) ctx.player(); DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); Set foundRabbits = data.getFoundRabbits(); - // Get and filter rabbits - List rabbits = getFilteredAndSortedRabbits(foundRabbits); + List rabbits = getFilteredAndSortedRabbits(foundRabbits, state.sortType(), state.filterType()); int totalPages = Math.max(1, (int) Math.ceil(rabbits.size() / (double) RABBITS_PER_PAGE)); - // Only fill border with black glass panes + // Fill border for (int slot : BORDER_SLOTS) { - set(new GUIItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - return ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " "); - } - }); + layout.slot(slot, (s, c) -> ItemStackCreator.createNamedItemStack(Material.BLACK_STAINED_GLASS_PANE, " ")); } - // Slot 4: Hoppity's Collection info - set(new GUIItem(4) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - return createCollectionInfoItem((SkyBlockPlayer) p); - } - }); + // Slot 4: Collection info + layout.slot(4, (s, c) -> createCollectionInfoItem((SkyBlockPlayer) c.player())); - // Set rabbit items for current page (empty slots will be AIR by default) - int startIndex = (currentPage - 1) * RABBITS_PER_PAGE; + // Rabbit items + int startIndex = (state.page() - 1) * RABBITS_PER_PAGE; for (int i = 0; i < RABBIT_SLOTS.length; i++) { int rabbitIndex = startIndex + i; int slot = RABBIT_SLOTS[i]; if (rabbitIndex < rabbits.size()) { ChocolateRabbit rabbit = rabbits.get(rabbitIndex); - boolean found = foundRabbits.contains(rabbit.name()); - set(createRabbitItem(slot, rabbit, found)); + layout.slot(slot, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + boolean found = ChocolateFactoryHelper.getData(p).getFoundRabbits().contains(rabbit.name()); + return createRabbitItem(rabbit, found); + }); } - // Empty slots are left as AIR (no item) } // Slot 47: Rabbit Locations - set(new GUIClickableItem(47) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - // TODO: Cycle through location filters - p.sendMessage("§7Rabbit Locations filter coming soon!"); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7Each rabbit has a specific location"); - lore.add("§7on a specific island where it can be"); - lore.add("§7found, just for fun."); - lore.add(""); - lore.add("§7The §9Hotspot §7of a Rabbit means that"); - lore.add("§7this season they have a §a50% §7higher"); - lore.add("§7chance to be found on this specific"); - lore.add("§7island."); - lore.add(""); - lore.add("§6Resident §7rabbits however, can §cONLY"); - lore.add("§7be found on their respective islands."); - lore.add(""); - lore.add("§7Currently selected: §aAll Rabbits"); - lore.add(""); - lore.add("§bRight-click to go backwards!"); - lore.add("§eClick to cycle!"); - - return ItemStackCreator.getStackHead("§9Rabbit Locations", LOCATION_TEXTURE, 1, lore); - } - }); + layout.slot(47, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7Each rabbit has a specific location"); + lore.add("§7on a specific island where it can be"); + lore.add("§7found, just for fun."); + lore.add(""); + lore.add("§7The §9Hotspot §7of a Rabbit means that"); + lore.add("§7this season they have a §a50% §7higher"); + lore.add("§7chance to be found on this specific"); + lore.add("§7island."); + lore.add(""); + lore.add("§6Resident §7rabbits however, can §cONLY"); + lore.add("§7be found on their respective islands."); + lore.add(""); + lore.add("§7Currently selected: §aAll Rabbits"); + lore.add(""); + lore.add("§bRight-click to go backwards!"); + lore.add("§eClick to cycle!"); + + return ItemStackCreator.getStackHead("§9Rabbit Locations", LOCATION_TEXTURE, 1, lore); + }, (click, c) -> c.player().sendMessage("§7Rabbit Locations filter coming soon!")); // Slot 48: Go Back - set(new GUIClickableItem(48) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - GUIChocolateFactory.open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Factory"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - } - }); + layout.slot(48, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7To Chocolate Factory"); + return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); + }, (click, c) -> GUIChocolateFactory.open((SkyBlockPlayer) c.player())); // Slot 49: Close - set(GUIClickableItem.getCloseItem(49)); + Components.close(layout, 49); // Slot 50: Sort - int finalTotalPages = totalPages; - set(new GUIClickableItem(50) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - boolean isRightClick = event.getClick() instanceof Click.Right; - SortType newSort = isRightClick - ? sortType.previous() - : sortType.next(); - new GUIHoppityCollection(1, newSort, filterType).open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add(""); - for (SortType type : SortType.values()) { - if (type == sortType) { - lore.add("§b▶ " + type.getDisplayName()); - } else { - lore.add("§7 " + type.getDisplayName()); - } + layout.slot(50, (s, c) -> { + List lore = new ArrayList<>(); + lore.add(""); + for (SortType type : SortType.values()) { + if (type == s.sortType()) { + lore.add("§b▶ " + type.getDisplayName()); + } else { + lore.add("§7 " + type.getDisplayName()); } - lore.add(""); - lore.add("§bRight-click to go backwards!"); - lore.add("§eClick to switch sort!"); - - return ItemStackCreator.getStack("§aSort", Material.HOPPER, 1, lore); } + lore.add(""); + lore.add("§bRight-click to go backwards!"); + lore.add("§eClick to switch sort!"); + + return ItemStackCreator.getStack("§aSort", Material.HOPPER, 1, lore); + }, (click, c) -> { + boolean isRightClick = click.click() instanceof Click.Right; + SortType newSort = isRightClick ? state.sortType().previous() : state.sortType().next(); + c.session(State.class).setState(new State(1, newSort, state.filterType())); }); // Slot 51: Filter - set(new GUIClickableItem(51) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - boolean isRightClick = event.getClick() instanceof Click.Right; - FilterType newFilter = isRightClick - ? filterType.previous() - : filterType.next(); - new GUIHoppityCollection(1, sortType, newFilter).open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add(""); - for (FilterType type : FilterType.values()) { - if (type == filterType) { - lore.add("§a▶ " + type.getDisplayName()); - } else { - lore.add("§7 " + type.getDisplayName()); - } + layout.slot(51, (s, c) -> { + List lore = new ArrayList<>(); + lore.add(""); + for (FilterType type : FilterType.values()) { + if (type == s.filterType()) { + lore.add("§a▶ " + type.getDisplayName()); + } else { + lore.add("§7 " + type.getDisplayName()); } - lore.add(""); - lore.add("§bRight-click to go backwards!"); - lore.add("§eClick to switch!"); - - return ItemStackCreator.getStack("§aFilter", Material.ENDER_EYE, 1, lore); } + lore.add(""); + lore.add("§bRight-click to go backwards!"); + lore.add("§eClick to switch!"); + + return ItemStackCreator.getStack("§aFilter", Material.ENDER_EYE, 1, lore); + }, (click, c) -> { + boolean isRightClick = click.click() instanceof Click.Right; + FilterType newFilter = isRightClick ? state.filterType().previous() : state.filterType().next(); + c.session(State.class).setState(new State(1, state.sortType(), newFilter)); }); - // Slot 53: Next Page (if applicable) - if (currentPage < totalPages) { - set(new GUIClickableItem(53) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - new GUIHoppityCollection(currentPage + 1, sortType, filterType).open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§ePage " + (currentPage + 1)); - return ItemStackCreator.getStack("§aNext Page", Material.ARROW, 1, lore); - } - }); + // Slot 53: Next Page + if (state.page() < totalPages) { + layout.slot(53, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§ePage " + (s.page() + 1)); + return ItemStackCreator.getStack("§aNext Page", Material.ARROW, 1, lore); + }, (click, c) -> c.session(State.class).setState(new State(state.page() + 1, state.sortType(), state.filterType()))); } - // Slot 45: Previous Page (if applicable) - if (currentPage > 1) { - set(new GUIClickableItem(45) { - @Override - public void run(InventoryPreClickEvent event, HypixelPlayer p) { - new GUIHoppityCollection(currentPage - 1, sortType, filterType).open((SkyBlockPlayer) p); - } - - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - lore.add("§ePage " + (currentPage - 1)); - return ItemStackCreator.getStack("§aPrevious Page", Material.ARROW, 1, lore); - } - }); + // Slot 45: Previous Page + if (state.page() > 1) { + layout.slot(45, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§ePage " + (s.page() - 1)); + return ItemStackCreator.getStack("§aPrevious Page", Material.ARROW, 1, lore); + }, (click, c) -> c.session(State.class).setState(new State(state.page() - 1, state.sortType(), state.filterType()))); } } @@ -261,7 +187,6 @@ private ItemStack.Builder createCollectionInfoItem(SkyBlockPlayer player) { int found = foundRabbits.size(); double percentage = (found / (double) TOTAL_RABBITS) * 100; - // Calculate bonuses int totalChocolate = 0; double totalMultiplier = 0; for (String rabbitName : foundRabbits) { @@ -299,72 +224,59 @@ private ItemStack.Builder createCollectionInfoItem(SkyBlockPlayer player) { return ItemStackCreator.getStackHead("§aHoppity's Collection", HOPPITY_TEXTURE, 1, lore); } - private GUIItem createRabbitItem(int slot, ChocolateRabbit rabbit, boolean found) { - return new GUIItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer p) { - List lore = new ArrayList<>(); - - // Bonus info - if (rabbit.getRarity() == ChocolateRabbit.Rarity.LEGENDARY || - rabbit.getRarity() == ChocolateRabbit.Rarity.DIVINE || - rabbit.getRarity() == ChocolateRabbit.Rarity.MYTHIC) { - lore.add("§7Grants §6+" + String.format("%.2fx", rabbit.getMultiplierBonus()) + " Chocolate §7per second"); - lore.add("§7to your §6Chocolate Factory§7."); - } else { - lore.add("§7Grants §6+" + rabbit.getChocolateBonus() + " Chocolate §7and §6" + - String.format("%.3fx", rabbit.getMultiplierBonus())); - lore.add("§6Chocolate §7per second to your"); - lore.add("§6Chocolate Factory§7."); - } - lore.add(""); - - // Obtain method if exists - if (rabbit.getObtainMethod() != null) { - lore.add("§7" + rabbit.getObtainMethod()); - lore.add(""); - } + private ItemStack.Builder createRabbitItem(ChocolateRabbit rabbit, boolean found) { + List lore = new ArrayList<>(); - // Requirement if exists - if (rabbit.getRequirement() != null) { - lore.add("§c✖ §7Requirement"); - lore.add("§7" + rabbit.getRequirement()); - lore.add(""); - if (!found) { - lore.add("§8You cannot find this rabbit until you"); - lore.add("§8meet the requirement!"); - lore.add(""); - } - } + if (rabbit.getRarity() == ChocolateRabbit.Rarity.LEGENDARY || + rabbit.getRarity() == ChocolateRabbit.Rarity.DIVINE || + rabbit.getRarity() == ChocolateRabbit.Rarity.MYTHIC) { + lore.add("§7Grants §6+" + String.format("%.2fx", rabbit.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } else { + lore.add("§7Grants §6+" + rabbit.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", rabbit.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§6Chocolate Factory§7."); + } + lore.add(""); - // Found status - if (found) { - lore.add("§a§lFOUND"); - } else { - lore.add("§8You have not found this rabbit yet!"); - } + if (rabbit.getObtainMethod() != null) { + lore.add("§7" + rabbit.getObtainMethod()); + lore.add(""); + } - // Location if exists (show as Resident label after found status) - if (rabbit.getLocation() != null) { - lore.add(rabbit.getResidentLabel()); - } + if (rabbit.getRequirement() != null) { + lore.add("§c✖ §7Requirement"); + lore.add("§7" + rabbit.getRequirement()); + lore.add(""); + if (!found) { + lore.add("§8You cannot find this rabbit until you"); + lore.add("§8meet the requirement!"); lore.add(""); + } + } - // Rarity - lore.add(rabbit.getRarity().getFormattedName()); + if (found) { + lore.add("§a§lFOUND"); + } else { + lore.add("§8You have not found this rabbit yet!"); + } - if (found) { - // Use a default rabbit head texture for found rabbits - return ItemStackCreator.getStackHead(rabbit.getFormattedName(), - "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692", 1, lore); - } else { - return ItemStackCreator.getStack(rabbit.getFormattedName(), Material.GRAY_DYE, 1, lore); - } - } - }; + if (rabbit.getLocation() != null) { + lore.add(rabbit.getResidentLabel()); + } + lore.add(""); + lore.add(rabbit.getRarity().getFormattedName()); + + if (found) { + return ItemStackCreator.getStackHead(rabbit.getFormattedName(), + "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692", 1, lore); + } else { + return ItemStackCreator.getStack(rabbit.getFormattedName(), Material.GRAY_DYE, 1, lore); + } } - private List getFilteredAndSortedRabbits(Set foundRabbits) { + private List getFilteredAndSortedRabbits(Set foundRabbits, SortType sortType, FilterType filterType) { List rabbits = Arrays.stream(ChocolateRabbit.values()) .filter(rabbit -> { boolean found = foundRabbits.contains(rabbit.name()); @@ -378,7 +290,6 @@ private List getFilteredAndSortedRabbits(Set foundRabbi }) .collect(Collectors.toList()); - // Sort switch (sortType) { case A_TO_Z -> rabbits.sort(Comparator.comparing(ChocolateRabbit::getDisplayName)); case Z_TO_A -> rabbits.sort(Comparator.comparing(ChocolateRabbit::getDisplayName).reversed()); @@ -406,24 +317,11 @@ private String createProgressBar(double progress) { return bar.toString(); } - @Override - public boolean allowHotkeying() { - return false; - } - - @Override - public void onClose(InventoryCloseEvent e, CloseReason reason) { - } - - @Override - public void suddenlyQuit(Inventory inventory, HypixelPlayer player) { - } - - @Override - public void onBottomClick(InventoryPreClickEvent e) { - e.setCancelled(true); + public static void open(SkyBlockPlayer player) { + ViewNavigator.get(player).push(new GUIHoppityCollection()); } + @Getter public enum SortType { A_TO_Z("A to Z"), Z_TO_A("Z to A"), @@ -436,10 +334,6 @@ public enum SortType { this.displayName = displayName; } - public String getDisplayName() { - return displayName; - } - public SortType next() { SortType[] values = values(); return values[(ordinal() + 1) % values.length]; @@ -451,6 +345,7 @@ public SortType previous() { } } + @Getter public enum FilterType { NONE("None"), FOUND("Rabbits Found"), @@ -464,10 +359,6 @@ public enum FilterType { this.displayName = displayName; } - public String getDisplayName() { - return displayName; - } - public FilterType next() { FilterType[] values = values(); return values[(ordinal() + 1) % values.length]; From b4a3595e26091ea216c022d2414eff20efccd593 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 14:41:28 -0300 Subject: [PATCH 09/13] Fix --- .../chocolatefactory/ChocolateFactoryHelper.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java index 4f998dd8f..56d958afb 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -24,6 +24,13 @@ public static DatapointChocolateFactory.ChocolateFactoryData getData(SkyBlockPla return player.getChocolateFactoryData(); } + /** + * Gets the chocolate factory datapoint for a player + */ + public static DatapointChocolateFactory getDatapoint(SkyBlockPlayer player) { + return player.getChocolateFactoryDatapoint(); + } + /** * Updates chocolate production for the player based on time elapsed */ From cc8518d726c3a5f0c1db381557a11f86c4bdd05a Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 15:01:42 -0300 Subject: [PATCH 10/13] Fixes --- .../ChocolateFactoryProductionLoop.java | 23 ++++++++++ .../gui/inventories/GUIChocolateFactory.java | 43 +++++++------------ .../GUIChocolateFactoryMilestones.java | 10 +---- .../gui/inventories/GUIChocolateShop.java | 8 +--- .../GUIChocolateShopMilestones.java | 9 +--- .../gui/inventories/GUIHoppityCollection.java | 10 +---- 6 files changed, 44 insertions(+), 59 deletions(-) create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java new file mode 100644 index 000000000..cbc898b6a --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java @@ -0,0 +1,23 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.timer.Scheduler; +import net.minestom.server.timer.TaskSchedule; +import net.swofty.type.skyblockgeneric.SkyBlockGenericLoader; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +public class ChocolateFactoryProductionLoop { + + public static void start() { + Scheduler scheduler = MinecraftServer.getSchedulerManager(); + + scheduler.submitTask(() -> { + for (SkyBlockPlayer player : SkyBlockGenericLoader.getLoadedPlayers()) { + if (player.getSkyblockDataHandler() == null) continue; + + ChocolateFactoryHelper.updateProduction(player); + } + return TaskSchedule.tick(20); // Every second (20 ticks) + }); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java index 07ac103bb..9fd05b9fe 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -15,6 +15,8 @@ import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import net.swofty.commons.StringUtility; + import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -120,7 +122,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { lore.add(""); lore.add(prestigeLevel >= 6 ? "§aYou have reached max prestige!" : "§eClick to prestige!"); - return ItemStackCreator.getStack(prestigeColor + "Chocolate Factory " + toRoman(prestigeLevel + 1), Material.DROPPER, 1, lore); + return ItemStackCreator.getStack(prestigeColor + "Chocolate Factory " + StringUtility.getAsRomanNumeral(prestigeLevel + 1), Material.DROPPER, 1, lore); }); // Employee slots (28-34) @@ -196,7 +198,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { p.sendMessage("§cThe Time Tower is already at its maximum level!"); p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } else if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { - p.sendMessage("§7You upgraded to §dTime Tower " + toRoman(d.getTimeTowerLevel() + 1) + "§7!"); + p.sendMessage("§7You upgraded to §dTime Tower " + StringUtility.getAsRomanNumeral(d.getTimeTowerLevel() + 1) + "§7!"); p.playSound(UPGRADE_SOUND); } else { p.sendMessage("§cYou don't have enough Chocolate!"); @@ -289,7 +291,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { lore.add("§eClick to view!"); return ItemStackCreator.getStackHead("§aHoppity's Collection", HOPPITY_TEXTURE, 1, lore); - }, (click, c) -> new GUIHoppityCollection().open((SkyBlockPlayer) c.player())); + }, (click, c) -> ((SkyBlockPlayer) c.player()).openView(new GUIHoppityCollection())); // Slot 51: Rabbit Hitman layout.slot(51, (s, c) -> { @@ -323,7 +325,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { lore.add(""); lore.add("§eClick to view!"); return ItemStackCreator.getStack("§6Chocolate Factory Milestones", Material.LADDER, 1, lore); - }, (click, c) -> new GUIChocolateFactoryMilestones().open((SkyBlockPlayer) c.player())); + }, (click, c) -> ((SkyBlockPlayer) c.player()).openView(new GUIChocolateFactoryMilestones())); } private void setupEmployeeSlots(ViewLayout layout, ViewContext ctx) { @@ -479,7 +481,7 @@ private ItemStack.Builder createRabbitBarnItem(SkyBlockPlayer player) { lore.add("§aYour Rabbit Barn is at maximum capacity!"); } else { lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + toRoman(level + 2)); + lore.add("§a§lUPGRADE §8➜ §aRabbit Barn " + StringUtility.getAsRomanNumeral(level + 2)); lore.add(" §a+2 Capacity"); lore.add(""); lore.add("§7Cost"); @@ -488,7 +490,7 @@ private ItemStack.Builder createRabbitBarnItem(SkyBlockPlayer player) { lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); } - return ItemStackCreator.getStack("§aRabbit Barn " + toRoman(level + 1), Material.OAK_FENCE, 1, lore); + return ItemStackCreator.getStack("§aRabbit Barn " + StringUtility.getAsRomanNumeral(level + 1), Material.OAK_FENCE, 1, lore); } private ItemStack.Builder createHandBakedChocolateItem(SkyBlockPlayer player) { @@ -512,7 +514,7 @@ private ItemStack.Builder createHandBakedChocolateItem(SkyBlockPlayer player) { lore.add("§aamount of upgrades!"); } else { lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dHand-Baked Chocolate " + toRoman(level + 2)); + lore.add("§a§lUPGRADE §8➜ §dHand-Baked Chocolate " + StringUtility.getAsRomanNumeral(level + 2)); lore.add(" §6+1 Chocolate Per Click"); lore.add(""); lore.add("§7Cost"); @@ -521,7 +523,7 @@ private ItemStack.Builder createHandBakedChocolateItem(SkyBlockPlayer player) { lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); } - return ItemStackCreator.getStack("§dHand-Baked Chocolate " + toRoman(level + 1), Material.COOKIE, 1, lore); + return ItemStackCreator.getStack("§dHand-Baked Chocolate " + StringUtility.getAsRomanNumeral(level + 1), Material.COOKIE, 1, lore); } private ItemStack.Builder createTimeTowerItem(SkyBlockPlayer player) { @@ -561,7 +563,7 @@ private ItemStack.Builder createTimeTowerItem(SkyBlockPlayer player) { lore.add("§aThe Time Tower is maxed out!"); } else { lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dTime Tower " + toRoman(level + 2)); + lore.add("§a§lUPGRADE §8➜ §dTime Tower " + StringUtility.getAsRomanNumeral(level + 2)); lore.add(" §6+0.1x Production Multiplier"); lore.add(""); lore.add("§7Cost"); @@ -573,7 +575,7 @@ private ItemStack.Builder createTimeTowerItem(SkyBlockPlayer player) { lore.add("§dRight-click to activate!"); } - return ItemStackCreator.getStack("§dTime Tower " + toRoman(level + 1), Material.CLOCK, 1, lore); + return ItemStackCreator.getStack("§dTime Tower " + StringUtility.getAsRomanNumeral(level + 1), Material.CLOCK, 1, lore); } private ItemStack.Builder createRabbitShrineItem(SkyBlockPlayer player) { @@ -603,7 +605,7 @@ private ItemStack.Builder createRabbitShrineItem(SkyBlockPlayer player) { lore.add("§alevel!"); } else { lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dRabbit Shrine " + toRoman(level + 2)); + lore.add("§a§lUPGRADE §8➜ §dRabbit Shrine " + StringUtility.getAsRomanNumeral(level + 2)); lore.add(" §a+2% Rare Rabbit Odds"); lore.add(""); lore.add("§7Cost"); @@ -612,7 +614,7 @@ private ItemStack.Builder createRabbitShrineItem(SkyBlockPlayer player) { lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); } - return ItemStackCreator.getStack("§dRabbit Shrine " + toRoman(level + 1), Material.RABBIT_FOOT, 1, lore); + return ItemStackCreator.getStack("§dRabbit Shrine " + StringUtility.getAsRomanNumeral(level + 1), Material.RABBIT_FOOT, 1, lore); } private ItemStack.Builder createCoachJackrabbitItem(SkyBlockPlayer player) { @@ -644,7 +646,7 @@ private ItemStack.Builder createCoachJackrabbitItem(SkyBlockPlayer player) { lore.add("§ayou all that he can teach!"); } else { lore.add("§8§m-----------------"); - lore.add("§a§lUPGRADE §8➜ §dCoach Jackrabbit " + toRoman(level + 2)); + lore.add("§a§lUPGRADE §8➜ §dCoach Jackrabbit " + StringUtility.getAsRomanNumeral(level + 2)); lore.add(" §6+0.01x Production Multiplier"); lore.add(""); lore.add("§7Cost"); @@ -653,7 +655,7 @@ private ItemStack.Builder createCoachJackrabbitItem(SkyBlockPlayer player) { lore.add(canAfford ? "§eClick to upgrade!" : "§cNot enough chocolate!"); } - return ItemStackCreator.getStackHead("§dCoach Jackrabbit " + toRoman(level + 1), COACH_JACKRABBIT_TEXTURE, 1, lore); + return ItemStackCreator.getStackHead("§dCoach Jackrabbit " + StringUtility.getAsRomanNumeral(level + 1), COACH_JACKRABBIT_TEXTURE, 1, lore); } private ItemStack.Builder createProductionInfoItem(SkyBlockPlayer player) { @@ -728,19 +730,6 @@ private String getEmployeeRankColor(int level) { return "c"; } - private String toRoman(int number) { - if (number <= 0) return "I"; - String[] thousands = {"", "M", "MM", "MMM"}; - String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; - String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; - String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; - - return thousands[number / 1000] + - hundreds[(number % 1000) / 100] + - tens[(number % 100) / 10] + - ones[number % 10]; - } - /** * Opens the Chocolate Factory GUI for a player with auto-refresh every second. */ diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java index 394fc8e46..317900418 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java @@ -53,11 +53,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { } // Go Back button (slot 48) - layout.slot(48, (s, c) -> { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Factory"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - }, (click, c) -> GUIChocolateFactory.open((SkyBlockPlayer) c.player())); + Components.back(layout, 48, ctx); // Close button (slot 49) Components.close(layout, 49); @@ -171,8 +167,4 @@ private String getOrdinal(int number) { } return number + suffixes[number % 10]; } - - public static void open(SkyBlockPlayer player) { - ViewNavigator.get(player).push(new GUIChocolateFactoryMilestones()); - } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java index de26e4dfe..db9c8b09f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java @@ -121,14 +121,10 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { lore.add("§eClick to view milestones!"); return ItemStackCreator.getStack("§6Chocolate Shop Milestones", Material.LADDER, 1, lore); - }, (click, c) -> GUIChocolateShopMilestones.open((SkyBlockPlayer) c.player())); + }, (click, c) -> ((SkyBlockPlayer) c.player()).openView(new GUIChocolateShopMilestones())); // Slot 48: Go Back - layout.slot(48, (s, c) -> { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Factory"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - }, (click, c) -> GUIChocolateFactory.open((SkyBlockPlayer) c.player())); + Components.back(layout, 48, ctx); // Slot 49: Close Components.close(layout, 49); diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java index 887c9210c..cfac31475 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShopMilestones.java @@ -53,11 +53,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { } // Go Back button (slot 48) - layout.slot(48, (s, c) -> { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Shop"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - }, (click, c) -> GUIChocolateShop.open((SkyBlockPlayer) c.player())); + Components.back(layout, 48, ctx); // Close button (slot 49) Components.close(layout, 49); @@ -172,7 +168,4 @@ private String getOrdinal(int number) { return number + suffixes[number % 10]; } - public static void open(SkyBlockPlayer player) { - ViewNavigator.get(player).push(new GUIChocolateShopMilestones()); - } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java index d60ac417e..14a84917f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java @@ -109,11 +109,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { }, (click, c) -> c.player().sendMessage("§7Rabbit Locations filter coming soon!")); // Slot 48: Go Back - layout.slot(48, (s, c) -> { - List lore = new ArrayList<>(); - lore.add("§7To Chocolate Factory"); - return ItemStackCreator.getStack("§aGo Back", Material.ARROW, 1, lore); - }, (click, c) -> GUIChocolateFactory.open((SkyBlockPlayer) c.player())); + Components.back(layout, 48, ctx); // Slot 49: Close Components.close(layout, 49); @@ -317,10 +313,6 @@ private String createProgressBar(double progress) { return bar.toString(); } - public static void open(SkyBlockPlayer player) { - ViewNavigator.get(player).push(new GUIHoppityCollection()); - } - @Getter public enum SortType { A_TO_Z("A to Z"), From 09e6a641ec04464b0340c0316097a422abffa82a Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 15:11:45 -0300 Subject: [PATCH 11/13] Fixes --- .../SkyBlockGenericLoader.java | 4 ++-- .../ChocolateFactoryProductionLoop.java | 23 ------------------- .../gui/inventories/GUIChocolateFactory.java | 1 + 3 files changed, 3 insertions(+), 25 deletions(-) delete mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java index 86d444114..065510a33 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/SkyBlockGenericLoader.java @@ -80,7 +80,7 @@ import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; import net.swofty.type.skyblockgeneric.user.SkyBlockScoreboard; import net.swofty.type.skyblockgeneric.user.StashReminder; -import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryProductionLoop; + import net.swofty.type.generic.user.categories.CustomGroups; import net.swofty.type.skyblockgeneric.user.fairysouls.FairySoul; import net.swofty.type.skyblockgeneric.user.fairysouls.FairySoulZone; @@ -327,7 +327,7 @@ public void initialize(MinecraftServer server) { // Start repeaters SkyBlockScoreboard.start(); StashReminder.start(MinecraftServer.getSchedulerManager()); - ChocolateFactoryProductionLoop.start(); + PlayerHolograms.updateAll(MinecraftServer.getSchedulerManager()); /** diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java deleted file mode 100644 index cbc898b6a..000000000 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryProductionLoop.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.swofty.type.skyblockgeneric.chocolatefactory; - -import net.minestom.server.MinecraftServer; -import net.minestom.server.timer.Scheduler; -import net.minestom.server.timer.TaskSchedule; -import net.swofty.type.skyblockgeneric.SkyBlockGenericLoader; -import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; - -public class ChocolateFactoryProductionLoop { - - public static void start() { - Scheduler scheduler = MinecraftServer.getSchedulerManager(); - - scheduler.submitTask(() -> { - for (SkyBlockPlayer player : SkyBlockGenericLoader.getLoadedPlayers()) { - if (player.getSkyblockDataHandler() == null) continue; - - ChocolateFactoryHelper.updateProduction(player); - } - return TaskSchedule.tick(20); // Every second (20 ticks) - }); - } -} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java index 9fd05b9fe..29ce608f0 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -75,6 +75,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { Components.fill(layout); SkyBlockPlayer player = (SkyBlockPlayer) ctx.player(); + ChocolateFactoryHelper.updateProduction(player); DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); // Slot 13: Chocolate cookie (clickable) From 038a322bea3ceac0bd3445ff91098ce61292a192 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 5 Feb 2026 16:39:21 -0300 Subject: [PATCH 12/13] Hoppity's Egg Hunt --- .../net/swofty/type/hub/TypeHubLoader.java | 7 + .../type/hub/hoppity/HoppityEggEntity.java | 54 ++ .../hub/hoppity/HoppityHuntEntityManager.java | 51 ++ .../type/hub/npcs/ChocolateFactoryRank.java | 8 +- .../ChocolateFactoryHelper.java | 22 +- .../chocolatefactory/HoppityEggLocations.java | 30 + .../chocolatefactory/HoppityEggType.java | 24 + .../chocolatefactory/HoppityHuntManager.java | 158 +++++ .../commands/HoppityHuntCommand.java | 42 ++ .../datapoints/DatapointChocolateFactory.java | 50 +- .../gui/inventories/GUIChocolateFactory.java | 7 +- .../gui/inventories/GUIChocolateShop.java | 583 +++++++++++++++--- .../gui/inventories/GUIHoppityCollection.java | 7 +- 13 files changed, 913 insertions(+), 130 deletions(-) create mode 100644 type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java create mode 100644 type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java create mode 100644 type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java diff --git a/type.hub/src/main/java/net/swofty/type/hub/TypeHubLoader.java b/type.hub/src/main/java/net/swofty/type/hub/TypeHubLoader.java index 3fefd2471..570d8c91c 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/TypeHubLoader.java +++ b/type.hub/src/main/java/net/swofty/type/hub/TypeHubLoader.java @@ -24,6 +24,8 @@ import net.swofty.type.generic.tab.TablistManager; import net.swofty.type.generic.tab.TablistModule; import net.swofty.type.hub.darkauction.DarkAuctionDisplay; +import net.swofty.type.hub.hoppity.HoppityHuntEntityManager; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityHuntManager; import net.swofty.type.hub.runes.RuneEntityImpl; import net.swofty.type.hub.tab.HubServerModule; import net.swofty.type.hub.util.HubMap; @@ -125,6 +127,11 @@ public void afterInitialize(MinecraftServer server) { // Place forest trees ForestTreePlacement.placeTrees(HypixelConst.getInstanceContainer()); + // Register Hoppity Hunt entity manager + HoppityHuntEntityManager hoppityEntityManager = new HoppityHuntEntityManager(); + HoppityHuntManager.setOnHuntStartCallback(hoppityEntityManager::spawnAllEggs); + HoppityHuntManager.setOnHuntStopCallback(hoppityEntityManager::despawnAllEggs); + HubMap hubMap = new HubMap(); hubMap.placeItemFrames(HypixelConst.getInstanceContainer()); } diff --git a/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java new file mode 100644 index 000000000..ed8473316 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java @@ -0,0 +1,54 @@ +package net.swofty.type.hub.hoppity; + +import net.minestom.server.component.DataComponents; +import net.minestom.server.entity.EntityCreature; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.PlayerSkin; +import net.minestom.server.entity.metadata.other.ArmorStandMeta; +import net.minestom.server.instance.Instance; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.network.player.ResolvableProfile; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityEggLocations; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityEggType; +import org.json.JSONObject; + +import java.util.Base64; +import java.util.concurrent.ThreadLocalRandom; + +public class HoppityEggEntity extends EntityCreature { + + private final HoppityEggLocations location; + private final HoppityEggType eggType; + + public HoppityEggEntity(HoppityEggLocations location, HoppityEggType eggType) { + super(EntityType.ARMOR_STAND); + this.location = location; + this.eggType = eggType; + + setInvisible(true); + ArmorStandMeta meta = (ArmorStandMeta) getEntityMeta(); + meta.setCustomNameVisible(false); + meta.setHasNoGravity(true); + meta.setSmall(true); + meta.setHasNoBasePlate(true); + meta.setNotifyAboutChanges(false); + + String texturesEncoded = encodeTexture(eggType.getTextureHash()); + setHelmet(ItemStack.builder(Material.PLAYER_HEAD) + .set(DataComponents.PROFILE, new ResolvableProfile(new PlayerSkin(texturesEncoded, null))) + .build()); + } + + public void spawn(Instance instance) { + float randomYaw = ThreadLocalRandom.current().nextFloat() * 360f; + setInstance(instance, location.getPosition().sub(0, 1.46875, 0).withYaw(randomYaw)); + } + + private static String encodeTexture(String textureHash) { + JSONObject json = new JSONObject(); + json.put("textures", new JSONObject().put("SKIN", + new JSONObject().put("url", "http://textures.minecraft.net/texture/" + textureHash))); + return Base64.getEncoder().encodeToString(json.toString().getBytes()); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java new file mode 100644 index 000000000..e18a3a304 --- /dev/null +++ b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java @@ -0,0 +1,51 @@ +package net.swofty.type.hub.hoppity; + +import net.swofty.type.generic.HypixelConst; +import net.swofty.type.generic.entity.InteractionEntity; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityEggLocations; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityEggType; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityHuntManager; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class HoppityHuntEntityManager { + + private final List spawnedEggs = new ArrayList<>(); + private final List spawnedInteractions = new ArrayList<>(); + + public void spawnAllEggs() { + Map locationEggTypes = HoppityHuntManager.getInstance().getLocationEggTypes(); + + for (Map.Entry entry : locationEggTypes.entrySet()) { + HoppityEggLocations location = entry.getKey(); + HoppityEggType eggType = entry.getValue(); + + HoppityEggEntity eggEntity = new HoppityEggEntity(location, eggType); + eggEntity.spawn(HypixelConst.getInstanceContainer()); + spawnedEggs.add(eggEntity); + + InteractionEntity interaction = new InteractionEntity(1.0f, 1.0f, (player, event) -> { + if (player instanceof SkyBlockPlayer skyBlockPlayer) { + HoppityHuntManager.getInstance().claimEgg(skyBlockPlayer, location); + } + }); + interaction.setInstance(HypixelConst.getInstanceContainer(), location.getPosition()); + spawnedInteractions.add(interaction); + } + } + + public void despawnAllEggs() { + for (HoppityEggEntity egg : spawnedEggs) { + egg.remove(); + } + spawnedEggs.clear(); + + for (InteractionEntity interaction : spawnedInteractions) { + interaction.remove(); + } + spawnedInteractions.clear(); + } +} diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java index 243d20f34..170359e9d 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java @@ -1,9 +1,11 @@ package net.swofty.type.hub.npcs; +import lombok.AllArgsConstructor; import lombok.Getter; import net.swofty.commons.ChatColor; @Getter +@AllArgsConstructor public enum ChocolateFactoryRank { UNEMPLOYED(0, "Unemployed", ChatColor.RED), INTERN(1, "Intern", ChatColor.GRAY), @@ -18,12 +20,6 @@ public enum ChocolateFactoryRank { private final String name; private final ChatColor color; - ChocolateFactoryRank(int level, String name, ChatColor color) { - this.level = level; - this.name = name; - this.color = color; - } - /** * Gets the formatted hologram line for this rank using the rank's base level. * Format: §7[Lvl X] §{color}RankName diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java index 56d958afb..96ccb8257 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -2,6 +2,8 @@ import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.experimental.UtilityClass; import java.text.DecimalFormat; @@ -428,6 +430,8 @@ public static Prestige getPrestige(int level) { return Prestige.fromLevel(level); } + @Getter + @AllArgsConstructor public enum Prestige { NEWCOMER(0, "Newcomer", "§7"), APPRENTICE(1, "Apprentice", "§a"), @@ -441,24 +445,6 @@ public enum Prestige { private final String name; private final String color; - Prestige(int level, String name, String color) { - this.level = level; - this.name = name; - this.color = color; - } - - public int getLevel() { - return level; - } - - public String getName() { - return name; - } - - public String getColor() { - return color; - } - public String getFormattedName() { return color + name; } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java new file mode 100644 index 000000000..e574f1abd --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java @@ -0,0 +1,30 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.minestom.server.coordinate.Pos; + +@Getter +@AllArgsConstructor +public enum HoppityEggLocations { + BAZAAR_ALLEY(new Pos(-40, 70, -74), "§din the §eBazaar Alley"), + BLACKSMITH(new Pos(-38, 70, -135), "§dbehind the §bBlacksmith §dbuilding"), + COLOSSEUM(new Pos(160, 97, -71), "§datop the §bColosseum"), + WHEAT_MINION(new Pos(44, 71, -137), "§dnear the Wheat Minion"), + FARMHOUSE(new Pos(18, 70, -76), "§dnext to the §bFarmhouse"), + FISHERMANS_HUT(new Pos(169, 72, 36), "§dat the §bFisherman's Hut"), + LUMBER_PILES(new Pos(-153, 74, -40), "§dnear the lumber piles"), + EMERALD_ALTAR(new Pos(-127, 73, -126), "§don the emerald altar"), + MOUNTAIN_PATH(new Pos(-1, 144, 51), "§don the §bMountain §dpath"), + MOUNTAIN_PEAK(new Pos(-38, 193, 35), "§don the second highest §bMountain §dpeak"), + RUINS(new Pos(-186, 87, 81), "§dwithin the §bRuins"), + UNINCORPORATED(new Pos(-7, 70, 188), "§din §cUnincorporated Territory"), + CRANE(new Pos(-61, 80, -38), "§don the crane"), + DARK_AUCTION(new Pos(125, 74, 168), "§dnear the §5Dark Auction"), + EMERALD_TREEHOUSE(new Pos(147, 113, 117), "§dwithin the emerald treehouse"), + WITHER_CAGE(new Pos(161, 71, 157), "§dwithin the wither cage"), + WIZARD_TOWER(new Pos(35, 72, 79), "§dat the base of the Wizard Tower"); + + private final Pos position; + private final String locationMessage; +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java new file mode 100644 index 000000000..1e9ab05c9 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java @@ -0,0 +1,24 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum HoppityEggType { + BREAKFAST("Chocolate Breakfast Egg", "6", "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"), + LUNCH("Chocolate Lunch Egg", "9", "e5e36165819fd2850f98552edcd763ff986313119283c126ace0c4cc495e76a8"), + DINNER("Chocolate Dinner Egg", "a", "7ae6d2d31d8167bcaf95293b68a4acd872d66e751db5a34f2cbc6766a0356d0a"), + BRUNCH("Chocolate Brunch Egg", "6", "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"), + DEJEUNER("Chocolate Déjeuner Egg", "9", "e5e36165819fd2850f98552edcd763ff986313119283c126ace0c4cc495e76a8"), + SUPPER("Chocolate Supper Egg", "a", "7ae6d2d31d8167bcaf95293b68a4acd872d66e751db5a34f2cbc6766a0356d0a"), + HITMAN("Hitman Egg", "c", "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"); + + private final String displayName; + private final String colorCode; + private final String textureHash; + + public String getFormattedName() { + return "§" + colorCode + displayName; + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java new file mode 100644 index 000000000..d3de0f6fb --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java @@ -0,0 +1,158 @@ +package net.swofty.type.skyblockgeneric.chocolatefactory; + +import lombok.Getter; +import lombok.Setter; +import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; +import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +public class HoppityHuntManager { + @Getter + private static final HoppityHuntManager instance = new HoppityHuntManager(); + + @Setter + private static Runnable onHuntStartCallback; + @Setter + private static Runnable onHuntStopCallback; + + @Getter + private boolean active; + @Getter + private final Map locationEggTypes = new HashMap<>(); + + private static final NavigableMap RARITY_WEIGHTS = new TreeMap<>(); + + static { + double cumulative = 0; + cumulative += 608; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.COMMON); + cumulative += 250; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.UNCOMMON); + cumulative += 100; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.RARE); + cumulative += 30; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.EPIC); + cumulative += 10; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.LEGENDARY); + cumulative += 2; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.MYTHIC); + cumulative += 0.5; + RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.DIVINE); + } + + private static final Map DUPLICATE_MULTIPLIERS = Map.of( + ChocolateRabbit.Rarity.COMMON, 150, + ChocolateRabbit.Rarity.UNCOMMON, 300, + ChocolateRabbit.Rarity.RARE, 750, + ChocolateRabbit.Rarity.EPIC, 1500, + ChocolateRabbit.Rarity.LEGENDARY, 4000, + ChocolateRabbit.Rarity.MYTHIC, 8000, + ChocolateRabbit.Rarity.DIVINE, 15000 + ); + + private HoppityHuntManager() {} + + public void startHunt() { + if (active) return; + + active = true; + locationEggTypes.clear(); + + HoppityEggType[] eggTypes = { + HoppityEggType.BREAKFAST, HoppityEggType.LUNCH, HoppityEggType.DINNER, + HoppityEggType.BRUNCH, HoppityEggType.DEJEUNER, HoppityEggType.SUPPER + }; + + HoppityEggLocations[] locations = HoppityEggLocations.values(); + for (int i = 0; i < locations.length; i++) { + locationEggTypes.put(locations[i], eggTypes[i % eggTypes.length]); + } + + if (onHuntStartCallback != null) { + onHuntStartCallback.run(); + } + } + + public void stopHunt() { + if (!active) return; + + active = false; + + if (onHuntStopCallback != null) { + onHuntStopCallback.run(); + } + + locationEggTypes.clear(); + } + + public void claimEgg(SkyBlockPlayer player, HoppityEggLocations location) { + if (!active) return; + + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + if (data.hasClaimedEgg(location.name())) { + player.sendMessage("§cYou have already claimed this egg!"); + return; + } + + HoppityEggType eggType = locationEggTypes.get(location); + if (eggType == null) return; + + data.claimEgg(location.name()); + + ChocolateRabbit.Rarity rarity = rollRarity(); + ChocolateRabbit rabbit = rollRabbit(rarity); + + player.sendMessage("§d§lHOPPITY'S HUNT §r" + eggType.getFormattedName() + " §r" + location.getLocationMessage()); + + if (rabbit != null && !data.hasFoundRabbit(rabbit.name())) { + data.addFoundRabbit(rabbit.name()); + player.sendMessage("§d§lNEW RABBIT! §r§" + rarity.getColorCode() + rabbit.getDisplayName() + " §7(" + rarity.getFormattedName() + "§7)"); + player.sendMessage("§7+§6" + rarity.getChocolateBonus() + " Chocolate §7per second"); + } else { + long duplicateChocolate = getDuplicateChocolate(rarity, data); + data.addChocolate(duplicateChocolate); + String rabbitName = rabbit != null ? rabbit.getDisplayName() : "Unknown"; + player.sendMessage("§d§lDUPLICATE RABBIT! §r§" + rarity.getColorCode() + rabbitName); + player.sendMessage("§7+§6" + formatNumber(duplicateChocolate) + " Chocolate"); + } + + ChocolateFactoryHelper.getDatapoint(player).setValue(data); + } + + private ChocolateRabbit.Rarity rollRarity() { + double totalWeight = RARITY_WEIGHTS.lastKey(); + double roll = ThreadLocalRandom.current().nextDouble() * totalWeight; + return RARITY_WEIGHTS.higherEntry(roll).getValue(); + } + + private ChocolateRabbit rollRabbit(ChocolateRabbit.Rarity rarity) { + List eligible = new ArrayList<>(); + for (ChocolateRabbit rabbit : ChocolateRabbit.values()) { + if (rabbit.getRarity() != rarity) continue; + if (rabbit.getObtainMethod() != null) continue; + if (rabbit.getLocation() != null && !rabbit.getLocation().equals("Hub")) continue; + eligible.add(rabbit); + } + + if (eligible.isEmpty()) return null; + return eligible.get(ThreadLocalRandom.current().nextInt(eligible.size())); + } + + private long getDuplicateChocolate(ChocolateRabbit.Rarity rarity, DatapointChocolateFactory.ChocolateFactoryData data) { + int multiplier = DUPLICATE_MULTIPLIERS.getOrDefault(rarity, 150); + double cps = data.getChocolatePerSecond(); + return Math.max(multiplier, (long) (cps * multiplier)); + } + + private String formatNumber(long number) { + if (number >= 1_000_000) { + return String.format("%.1fM", number / 1_000_000.0); + } else if (number >= 1_000) { + return String.format("%,d", number); + } + return String.valueOf(number); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java new file mode 100644 index 000000000..d9d13c593 --- /dev/null +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java @@ -0,0 +1,42 @@ +package net.swofty.type.skyblockgeneric.commands; + +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.swofty.type.generic.command.CommandParameters; +import net.swofty.type.generic.command.HypixelCommand; +import net.swofty.type.generic.user.categories.Rank; +import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityHuntManager; + +@CommandParameters(description = "Manages the Hoppity Hunt event", + usage = "/hoppityhunt ", + permission = Rank.STAFF, + allowsConsole = false) +public class HoppityHuntCommand extends HypixelCommand { + @Override + public void registerUsage(MinestomCommand command) { + command.addSyntax((sender, context) -> { + if (!permissionCheck(sender)) return; + + HoppityHuntManager manager = HoppityHuntManager.getInstance(); + if (manager.isActive()) { + sender.sendMessage("§cHoppity's Hunt is already active!"); + return; + } + + manager.startHunt(); + sender.sendMessage("§aHoppity's Hunt has been started! §e17 eggs §ahave been spawned."); + }, ArgumentType.Literal("start")); + + command.addSyntax((sender, context) -> { + if (!permissionCheck(sender)) return; + + HoppityHuntManager manager = HoppityHuntManager.getInstance(); + if (!manager.isActive()) { + sender.sendMessage("§cHoppity's Hunt is not currently active!"); + return; + } + + manager.stopHunt(); + sender.sendMessage("§aHoppity's Hunt has been stopped! All eggs have been removed."); + }, ArgumentType.Literal("stop")); + } +} diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java index ef766ffdc..21760793a 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java @@ -60,6 +60,14 @@ public String serialize(ChocolateFactoryData value) { // Shop stats json.put("totalChocolateSpent", value.totalChocolateSpent); + // Hoppity Hunt + JSONArray claimedEggsArray = new JSONArray(); + for (String egg : value.claimedEggs) { + claimedEggsArray.put(egg); + } + json.put("claimedEggs", claimedEggsArray); + json.put("totalEggsFound", value.totalEggsFound); + return json.toString(); } @@ -87,6 +95,14 @@ public ChocolateFactoryData deserialize(String json) { } } + Set claimedEggs = new HashSet<>(); + if (jsonObject.has("claimedEggs")) { + JSONArray claimedEggsArray = jsonObject.getJSONArray("claimedEggs"); + for (int i = 0; i < claimedEggsArray.length(); i++) { + claimedEggs.add(claimedEggsArray.getString(i)); + } + } + return new ChocolateFactoryData( jsonObject.optLong("chocolate", 0L), jsonObject.optLong("chocolateAllTime", 0L), @@ -104,7 +120,9 @@ public ChocolateFactoryData deserialize(String json) { jsonObject.optLong("totalClicks", 0L), jsonObject.optInt("totalTimeTowerUsages", 0), foundRabbits, - jsonObject.optLong("totalChocolateSpent", 0L) + jsonObject.optLong("totalChocolateSpent", 0L), + claimedEggs, + jsonObject.optInt("totalEggsFound", 0) ); } @@ -135,7 +153,9 @@ public ChocolateFactoryData clone(ChocolateFactoryData value) { value.totalClicks, value.totalTimeTowerUsages, new HashSet<>(value.foundRabbits), - value.totalChocolateSpent + value.totalChocolateSpent, + new HashSet<>(value.claimedEggs), + value.totalEggsFound ); } }; @@ -185,6 +205,10 @@ public static class ChocolateFactoryData { // Shop stats private long totalChocolateSpent; + // Hoppity Hunt + private Set claimedEggs = new HashSet<>(); + private int totalEggsFound; + /** * Adds chocolate and updates all-time total */ @@ -372,6 +396,28 @@ public int getFoundRabbitCount() { public void addChocolateSpent(long amount) { this.totalChocolateSpent += amount; } + + /** + * Claims an egg location for the current hunt + */ + public void claimEgg(String eggLocationId) { + claimedEggs.add(eggLocationId); + totalEggsFound++; + } + + /** + * Checks if an egg location has been claimed in the current hunt + */ + public boolean hasClaimedEgg(String eggLocationId) { + return claimedEggs.contains(eggLocationId); + } + + /** + * Resets claimed eggs for a new hunt + */ + public void resetClaimedEggs() { + claimedEggs.clear(); + } } @AllArgsConstructor diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java index 29ce608f0..aa083afd8 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -70,6 +70,11 @@ public void onOpen(State state, ViewContext ctx) { ChocolateFactoryHelper.updateProduction((SkyBlockPlayer) ctx.player()); } + @Override + public void onRefresh(State state, ViewContext ctx) { + ChocolateFactoryHelper.updateProduction((SkyBlockPlayer) ctx.player()); + } + @Override public void layout(ViewLayout layout, State state, ViewContext ctx) { Components.fill(layout); @@ -735,6 +740,6 @@ private String getEmployeeRankColor(int level) { * Opens the Chocolate Factory GUI for a player with auto-refresh every second. */ public static void open(SkyBlockPlayer player) { - ViewNavigator.get(player).push(new GUIChocolateFactory()).refreshEvery(Duration.ofSeconds(1)); + player.openView(new GUIChocolateFactory()).refreshEvery(Duration.ofSeconds(1)); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java index db9c8b09f..31a6da94f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java @@ -14,7 +14,22 @@ import java.util.List; public class GUIChocolateShop implements StatefulView { - private static final String SHOP_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; + // Texture IDs (extracted from skull texture URLs) + private static final String SUPREME_CHOCOLATE_BAR_TEXTURE = "254b7f3f2a6f0d1c2c054678128ec2322619aefff0f450c390d6a41b5950302e"; + private static final String EGGLOCATOR_TEXTURE = "14a7ff9a10fdae14446499ef3bc1df13b7888d6cd2e311ccab51b8352c6093b4"; + private static final String NIBBLE_CHOCOLATE_STICK_TEXTURE = "888188d62908af6e114f73a109e15ac7f1faded39abd6a2054034ec5cc70c727"; + private static final String SMOOTH_CHOCOLATE_BAR_TEXTURE = "a9372efd2ca1a6c6dfc066f1ec83f9456575c3850a0e7d01109c4f1af300ba8"; + private static final String RICH_CHOCOLATE_CHUNK_TEXTURE = "6f942717364c0fecf7ad11bac8cd98dd7ad4dbd72e3d3ce2b57eb48713824ff"; + private static final String GANACHE_CHOCOLATE_SLAB_TEXTURE = "f89512331edfdc27cb7d4e80f3e0db460d05caf66c7c1c42e0e712130a9b690"; + private static final String PRESTIGE_CHOCOLATE_REALM_TEXTURE = "af19ceeabf2ecb020610b8aabc9299264fa670048c010c9699ce687fc9bf351e"; + private static final String DARK_CACAO_TRUFFLE_TEXTURE = "db9db373cadbec1912a9ab386d31ceb3e0cd4d6a64f222426588a3b2eb31ed29"; + private static final String CHOCOLATE_DYE_TEXTURE = "a15e7208539306f65d68df9be6c3124c48027e307739fc8dc35526febd643c21"; + private static final String BARN_SKIN_TEXTURE = "af90da40c557af4ac01d39b6733e204c74ae9fee8c2bc40be1fd4f28f837d52"; + private static final String CHOCOLATE_SYRINGE_TEXTURE = "7dcb67a72c01f3ca75da846f957ffed6417f0c45ad814fb3e340c317cf316718"; + private static final String CHOCO_RABBIT_MINION_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; + private static final String ZORROS_CAPE_TEXTURE = "81f7226a927558d069a6ae343b4e089fbd60fc6037190097c7713208e988faae"; + private static final String FISH_CHOCOLAT_TEXTURE = "422b0e5faa97ca109cd45f1fba2d84ca2b9b601de50b47f4add2d835aa360f78"; + private static final String HOT_CHOCOLATE_MIXIN_TEXTURE = "4fde9c68bc5a89f01a5e5203eecc5367d494d55a47c81e6b1d689a0c4488b6e"; public record State() {} @@ -32,140 +47,508 @@ public ViewConfiguration configuration() { public void layout(ViewLayout layout, State state, ViewContext ctx) { Components.fill(layout); - SkyBlockPlayer player = (SkyBlockPlayer) ctx.player(); - - // Slot 4: Shop Info - layout.slot(4, (s, c) -> { - SkyBlockPlayer p = (SkyBlockPlayer) c.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(p); - + // Slot 10: Supreme Chocolate Bar + layout.slot(10, (s, c) -> { List lore = new ArrayList<>(); - lore.add("§7Spend your §6Chocolate §7on the world's"); - lore.add("§7most delectable treats!"); + lore.add("§7Bring §63,000 §7of these to §5Carrolyn §7in"); + lore.add("§5Scarleton §7on the §cCrimson Isle §7to"); + lore.add("§7permanently gain §c+5\u2764 Health §7and"); + lore.add("§6+12\u2618 Cocoa Beans Fortune§7."); lore.add(""); - lore.add("§7Your Chocolate: §6" + ChocolateFactoryHelper.formatChocolate(data.getChocolate())); - lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(data.getTotalChocolateSpent())); - - return ItemStackCreator.getStackHead("§6Chocolate Shop", SHOP_TEXTURE, 1, lore); - }); - - // Row 2: Shop Items - createShopSlot(layout, 10, "§6Chocolate Rabbit", Material.PLAYER_HEAD, - "§7A delicious chocolate rabbit that", "§7restores §c+50 Health §7when consumed.", - 1000, SHOP_TEXTURE); - - createShopSlot(layout, 11, "§6Chocolate Bar", Material.COOKIE, - "§7A tasty chocolate bar that", "§7restores §c+25 Health §7when consumed.", - 250, null); + lore.add("§a§lUNCOMMON"); + lore.add(""); + addCostLore(lore, (SkyBlockPlayer) c.player(), 2_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§6500 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + lore.add("§eRight-click for more trading options!"); + return ItemStackCreator.getStackHead("§aSupreme Chocolate Bar", SUPREME_CHOCOLATE_BAR_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 2_000_000L, "§aSupreme Chocolate Bar", 0, c)); - createShopSlot(layout, 12, "§6Golden Chocolate", Material.GOLD_INGOT, - "§7Premium chocolate infused with gold.", "§7Grants §6+100 Chocolate §7when used.", - 5000, null); + // Slot 11: Egglocator + layout.slot(11, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7Uses the magic of §aHoppity §7to"); + lore.add("§7uncover hidden §aChocolate Rabbit"); + lore.add("§aEggs§7."); + lore.add(""); + lore.add("§6Ability: Egglocator §e§lRIGHT CLICK"); + lore.add("§7Points towards the nearest unclaimed"); + lore.add("§aChocolate Rabbit Egg§7!"); + lore.add("§8Cooldown: §a5s"); + lore.add(""); + lore.add("§7Only works during §dHoppity's Hunt§7."); + lore.add(""); + lore.add("§f§lCOMMON"); + lore.add(""); + addCostLore(lore, (SkyBlockPlayer) c.player(), 7_500_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§fEgglocator", EGGLOCATOR_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 7_500_000L, "§fEgglocator", 0, c)); - createShopSlot(layout, 13, "§dTime Tower Charge", Material.CLOCK, - "§7Adds §a+1 charge §7to your", "§dTime Tower§7.", - 50000, null); + // Slot 12: Nibble Chocolate Stick + layout.slot(12, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 1) { + return createLockedItem("§cChocolate Factory II."); + } + List lore = new ArrayList<>(); + lore.add("§7Grants §a+2% §7chance to find a"); + lore.add("§aChocolate Rabbit §7that you haven't"); + lore.add("§7found yet and grants §6+10 Chocolate"); + lore.add("§7per second."); + lore.add(""); + lore.add("§8§oA delightful treat from the Factory."); + lore.add("§8§oIts crisp taste sparks joy with"); + lore.add("§8§oevery bite."); + lore.add(""); + lore.add("§f§lCOMMON ACCESSORY"); + lore.add(""); + addCostLore(lore, p, 250_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§fNibble Chocolate Stick", NIBBLE_CHOCOLATE_STICK_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 250_000_000L, "§fNibble Chocolate Stick", 1, c)); - createShopSlot(layout, 14, "§aLucky Rabbit Foot", Material.RABBIT_FOOT, - "§7Increases your chance of finding", "§7rare rabbits by §a+5% §7for 1 hour.", - 25000, null); + // Slot 13: Smooth Chocolate Bar + layout.slot(13, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 1) { + return createLockedItem("§cChocolate Factory II."); + } + List lore = new ArrayList<>(); + lore.add("§7Grants §a+4% §7chance to find a"); + lore.add("§aChocolate Rabbit §7that you haven't"); + lore.add("§7found yet and grants §6+20 Chocolate"); + lore.add("§7per second."); + lore.add(""); + lore.add("§8§oCrafted in the Factory, its"); + lore.add("§8§osmoothness melts hearts and tastes"); + lore.add("§8§olike a sweet escape."); + lore.add(""); + lore.add("§a§lUNCOMMON ACCESSORY"); + lore.add(""); + addCostLore(lore, p, 1_000_000_000L); + lore.add("§fNibble Chocolate Stick"); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§aSmooth Chocolate Bar", SMOOTH_CHOCOLATE_BAR_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 1_000_000_000L, "§aSmooth Chocolate Bar", 1, c)); - createShopSlot(layout, 15, "§6Cocoa Beans Pack", Material.COCOA_BEANS, - "§7A pack of premium cocoa beans.", "§7Grants §6+500 Chocolate §7instantly.", - 2500, null); + // Slot 14: Rich Chocolate Chunk + layout.slot(14, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 2) { + return createLockedItem("§cChocolate Factory III."); + } + List lore = new ArrayList<>(); + lore.add("§7Grants §a+6% §7chance to find a"); + lore.add("§aChocolate Rabbit §7that you haven't"); + lore.add("§7found yet and grants §6+30 Chocolate"); + lore.add("§7per second."); + lore.add(""); + lore.add("§8§oFrom the Factory's secret"); + lore.add("§8§oreserves, its rich flavor is a deep"); + lore.add("§8§odive into indulgence."); + lore.add(""); + lore.add("§9§lRARE ACCESSORY"); + lore.add(""); + addCostLore(lore, p, 2_000_000_000L); + lore.add("§aSmooth Chocolate Bar"); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§9Rich Chocolate Chunk", RICH_CHOCOLATE_CHUNK_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 2_000_000_000L, "§9Rich Chocolate Chunk", 2, c)); - createShopSlot(layout, 16, "§5Chocolate Fountain", Material.CAULDRON, - "§7A decorative chocolate fountain", "§7for your island.", - 100000, null); + // Slot 15: Ganache Chocolate Slab + layout.slot(15, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 2) { + return createLockedItem("§cChocolate Factory III."); + } + List lore = new ArrayList<>(); + lore.add("§7Grants §a+8% §7chance to find a"); + lore.add("§aChocolate Rabbit §7that you haven't"); + lore.add("§7found yet and grants §6+40 Chocolate"); + lore.add("§7per second."); + lore.add(""); + lore.add("§8§oA Factory masterpiece - its divine"); + lore.add("§8§otaste transcends reality, offering a"); + lore.add("§8§oheavenly escape."); + lore.add(""); + lore.add("§5§lEPIC ACCESSORY"); + lore.add(""); + addCostLore(lore, p, 3_000_000_000L); + lore.add("§9Rich Chocolate Chunk"); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§5Ganache Chocolate Slab", GANACHE_CHOCOLATE_SLAB_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 3_000_000_000L, "§5Ganache Chocolate Slab", 2, c)); - // Row 3: More Items - createShopSlot(layout, 19, "§aRabbit Cage", Material.IRON_BARS, - "§7Expands your §aRabbit Barn §7by", "§a+5 slots §7permanently.", - 75000, null); + // Slot 16: Prestige Chocolate Realm + layout.slot(16, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 3) { + return createLockedItem("§cChocolate Factory IV."); + } + List lore = new ArrayList<>(); + lore.add("§7Grants §a+10% §7chance to find a"); + lore.add("§aChocolate Rabbit §7that you haven't"); + lore.add("§7found yet and grants §6+50 Chocolate"); + lore.add("§7per second."); + lore.add(""); + lore.add("§8§oThe Factory's pinnacle creation - its"); + lore.add("§8§oepic taste shatters expectations,"); + lore.add("§8§ooffering a taste of utopia."); + lore.add(""); + lore.add("§6§lLEGENDARY ACCESSORY"); + lore.add(""); + addCostLore(lore, p, 4_500_000_000L); + lore.add("§5Ganache Chocolate Slab"); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§6Prestige Chocolate Realm", PRESTIGE_CHOCOLATE_REALM_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 4_500_000_000L, "§6Prestige Chocolate Realm", 3, c)); - createShopSlot(layout, 20, "§9Chocolate Recipe", Material.PAPER, - "§7Learn a new chocolate recipe", "§7for your factory.", - 10000, null); + // Slot 19: Dark Cacao Truffle + layout.slot(19, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 3) { + return createLockedItem("§cChocolate Factory IV."); + } + List lore = new ArrayList<>(); + lore.add("§7Consume to boost your §6\u2618 Global"); + lore.add("§6Fortune §7for §a60m§7."); + lore.add(""); + lore.add("§7Keep this item in your inventory to"); + lore.add("§7increase the bonus up to §6+30§6\u2618"); + lore.add("§6Global Fortune§7, at which point the"); + lore.add("§7item §c§oevolves§7!"); + lore.add(""); + lore.add("§7Current Bonus: §6+0§6\u2618 Global Fortune"); + lore.add(""); + lore.add("§9§lRARE"); + lore.add(""); + addCostLore(lore, p, 2_500_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§62 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§9Dark Cacao Truffle", DARK_CACAO_TRUFFLE_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 2_500_000_000L, "§9Dark Cacao Truffle", 3, c)); - createShopSlot(layout, 21, "§dRabbit Whistle", Material.GOAT_HORN, - "§7Summons a random rabbit to", "§7help in your factory for 1 hour.", - 30000, null); + // Slot 20: Chocolate Dye + layout.slot(20, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 5) { + return createLockedItem("§cChocolate Factory VI."); + } + List lore = new ArrayList<>(); + lore.add("§8Combinable in Anvil"); + lore.add(""); + lore.add("§7Changes the color of an armor piece"); + lore.add("§7to §6#7B3F00§7!"); + lore.add(""); + lore.add("§5§lEPIC DYE"); + lore.add(""); + addCostLore(lore, p, 40_000_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§6Chocolate Dye", CHOCOLATE_DYE_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 40_000_000_000L, "§6Chocolate Dye", 5, c)); - createShopSlot(layout, 22, "§5Chocolate Essence", Material.EXPERIENCE_BOTTLE, - "§7Concentrated chocolate power.", "§7Grants §6+1,000 Chocolate §7instantly.", - 5000, null); + // Slot 21: Chocolate Factory Barn Skin + layout.slot(21, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 4) { + return createLockedItem("§cChocolate Factory V."); + } + List lore = new ArrayList<>(); + lore.add("§7Consume this item to unlock the"); + lore.add("§6Chocolate Factory Barn Skin §7on §aThe"); + lore.add("§aGarden§7!"); + lore.add(""); + lore.add("§eClick to consume!"); + lore.add(""); + lore.add("§6§lLEGENDARY COSMETIC"); + lore.add(""); + addCostLore(lore, p, 7_000_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§6Chocolate Factory Barn Skin", BARN_SKIN_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 7_000_000_000L, "§6Chocolate Factory Barn Skin", 4, c)); - createShopSlot(layout, 23, "§6Factory Blueprint", Material.MAP, - "§7Unlocks additional factory", "§7customization options.", - 200000, null); + // Slot 22: Chocolate Syringe + layout.slot(22, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 4) { + return createLockedItem("§cChocolate Factory V."); + } + List lore = new ArrayList<>(); + lore.add("§7Use at §bKat §7to upgrade §eRabbit Pets §7to"); + lore.add("§dMythic §7rarity."); + lore.add(""); + lore.add("§d§lMYTHIC"); + lore.add(""); + addCostLore(lore, p, 10_000_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§dChocolate Syringe", CHOCOLATE_SYRINGE_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 10_000_000_000L, "§dChocolate Syringe", 4, c)); - createShopSlot(layout, 24, "§6§lChocolate Crown", Material.GOLDEN_HELMET, - "§7The ultimate symbol of chocolate", "§7mastery. Purely cosmetic.", - 1000000, null); + // Slot 23: Choco Rabbit Minion Skin + layout.slot(23, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 4) { + return createLockedItem("§cChocolate Factory V."); + } + List lore = new ArrayList<>(); + lore.add("§7This Minion skin changes your"); + lore.add("§7minion's appearance to a §eChoco"); + lore.add("§eRabbit§7."); + lore.add(""); + lore.add("§7You can place this item in any minion"); + lore.add("§7of your choice!"); + lore.add(""); + lore.add("§5§lEPIC COSMETIC"); + lore.add(""); + addCostLore(lore, p, 2_500_000_000L); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§62 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§5Choco Rabbit Minion Skin", CHOCO_RABBIT_MINION_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 2_500_000_000L, "§5Choco Rabbit Minion Skin", 4, c)); - createShopSlot(layout, 25, "§d§lMystery Egg", Material.DRAGON_EGG, - "§7Contains a random rabbit!", "§7Could be any rarity.", - 500000, null); + // Slot 24: Zorro's Cape + layout.slot(24, (s, c) -> { + List lore = new ArrayList<>(); + lore.add("§7Strength: §c+10"); + lore.add("§7Ferocity: §c+2"); + lore.add("§7Farming Fortune: §6+10"); + lore.add("§7Farming Wisdom: §3+1"); + lore.add(""); + lore.add("§7The stats of this Cape §adouble "); + lore.add("§7during §eJacob's Farming Contest§7."); + lore.add("§7Additionally, you have a §a20% §7chance"); + lore.add("§7to obtain an extra medal from"); + lore.add("§7contests."); + lore.add(""); + lore.add("§8§oNot all Rabbits wear capes."); + lore.add(""); + lore.add("§8This item can be reforged!"); + lore.add("§4\u2763 §cRequires §dZorro §cin Hoppity's Collection§c."); + lore.add("§6§lLEGENDARY CLOAK"); + lore.add(""); + addCostLore(lore, (SkyBlockPlayer) c.player(), 20_000_000_000L); + lore.add(""); + lore.add("§cNot unlocked!"); + return ItemStackCreator.getStackHead("§6Zorro's Cape", ZORROS_CAPE_TEXTURE, 1, lore); + }); - // Slot 31: Shop Milestones - layout.slot(31, (s, c) -> { + // Slot 25: Fish Chocolat à la Vapeur + layout.slot(25, (s, c) -> { SkyBlockPlayer p = (SkyBlockPlayer) c.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(p); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 5) { + return createLockedItem("§cChocolate Factory VI."); + } + List lore = new ArrayList<>(); + lore.add("§7Give this dish to §aHoppity §7to obtain his"); + lore.add("§aAbiphone Contact§7."); + lore.add(""); + lore.add("§8§oSavory fish with a chocolate twist."); + lore.add("§8§oMwah! C'est magnifique, no?"); + lore.add(""); + lore.add("§5§lEPIC"); + lore.add(""); + addCostLore(lore, p, 50_000_000_000L); + lore.add("§cRabbit the Fish"); + lore.add(""); + lore.add("§7Annual Stock §8Year 471"); + lore.add("§61 §7remaining"); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§5Fish Chocolat \u00e0 la Vapeur", FISH_CHOCOLAT_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 50_000_000_000L, "§5Fish Chocolat \u00e0 la Vapeur", 5, c)); + // Slot 28: Hot Chocolate Mixin + layout.slot(28, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 5) { + List lore = new ArrayList<>(); + lore.add("§8Brewing Ingredient"); + lore.add(""); + lore.add("§7Mixins provide a buff that can be"); + lore.add("§7added to §cGod Potions §7in a brewing"); + lore.add("§7stand and lasts for the full duration."); + lore.add(""); + lore.add("§7Gain §d+15\u2663 Pet Luck §7and §6+0.05x"); + lore.add("§6Chocolate §7per second."); + lore.add(""); + lore.add("§7Duration: §a36h 0m"); + lore.add(""); + lore.add("§7The duration of Mixins can be stacked!"); + lore.add(""); + lore.add("§eRight-click to consume!§8"); + lore.add("§8(Requires active Booster Cookie)"); + lore.add(""); + lore.add("§4\u2763 §cRequires §cChocolate Factory VI§c."); + lore.add("§9§lRARE"); + lore.add(""); + addCostLore(lore, p, 1_500_000_000L); + lore.add(""); + lore.add("§cNot unlocked!"); + return ItemStackCreator.getStackHead("§9Hot Chocolate Mixin", HOT_CHOCOLATE_MIXIN_TEXTURE, 1, lore); + } List lore = new ArrayList<>(); - lore.add("§7Spend §6Chocolate §7in the shop to"); - lore.add("§7unlock special §aChocolate Rabbits§7!"); + lore.add("§8Brewing Ingredient"); lore.add(""); - lore.add("§7Total Spent: §6" + ChocolateFactoryHelper.formatChocolate(data.getTotalChocolateSpent())); + lore.add("§7Mixins provide a buff that can be"); + lore.add("§7added to §cGod Potions §7in a brewing"); + lore.add("§7stand and lasts for the full duration."); lore.add(""); - lore.add("§eClick to view milestones!"); + lore.add("§7Gain §d+15\u2663 Pet Luck §7and §6+0.05x"); + lore.add("§6Chocolate §7per second."); + lore.add(""); + lore.add("§7Duration: §a36h 0m"); + lore.add(""); + lore.add("§7The duration of Mixins can be stacked!"); + lore.add(""); + lore.add("§eRight-click to consume!§8"); + lore.add("§8(Requires active Booster Cookie)"); + lore.add(""); + lore.add("§9§lRARE"); + lore.add(""); + addCostLore(lore, p, 1_500_000_000L); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStackHead("§9Hot Chocolate Mixin", HOT_CHOCOLATE_MIXIN_TEXTURE, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 1_500_000_000L, "§9Hot Chocolate Mixin", 5, c)); - return ItemStackCreator.getStack("§6Chocolate Shop Milestones", Material.LADDER, 1, lore); - }, (click, c) -> ((SkyBlockPlayer) c.player()).openView(new GUIChocolateShopMilestones())); + // Slot 29: Chocolate Fortune + layout.slot(29, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + int prestige = ChocolateFactoryHelper.getData(p).getPrestigeLevel(); + if (prestige < 5) { + List lore = new ArrayList<>(); + lore.add("§7Permanently gain §6+1\u2618 Cocoa Beans"); + lore.add("§6Fortune §7per tier."); + lore.add(""); + addCostLore(lore, p, 2_000_000_000L); + lore.add(""); + lore.add("§cChocolate Factory VI."); + return ItemStackCreator.getStack("§eChocolate Fortune", Material.COCOA_BEANS, 1, lore); + } + List lore = new ArrayList<>(); + lore.add("§7Permanently gain §6+1\u2618 Cocoa Beans"); + lore.add("§6Fortune §7per tier."); + lore.add(""); + addCostLore(lore, p, 2_000_000_000L); + lore.add(""); + lore.add("§eClick to trade!"); + return ItemStackCreator.getStack("§eChocolate Fortune", Material.COCOA_BEANS, 1, lore); + }, (click, c) -> handlePurchase((SkyBlockPlayer) c.player(), 2_000_000_000L, "§eChocolate Fortune", 5, c)); // Slot 48: Go Back Components.back(layout, 48, ctx); // Slot 49: Close Components.close(layout, 49); - } - private void createShopSlot(ViewLayout layout, int slot, String name, Material material, - String desc1, String desc2, long cost, String textureId) { - layout.slot(slot, (s, c) -> { - SkyBlockPlayer player = (SkyBlockPlayer) c.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - boolean canAfford = data.getChocolate() >= cost; + // Slot 50: Chocolate Shop Milestones + layout.slot(50, (s, c) -> { + SkyBlockPlayer p = (SkyBlockPlayer) c.player(); + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(p); List lore = new ArrayList<>(); - lore.add(desc1); - lore.add(desc2); + lore.add("§7Unlock special §aChocolate Rabbits §7by"); + lore.add("§7spending §6Chocolate §7in the §6Chocolate"); + lore.add("§6Shop§7."); lore.add(""); - lore.add("§7Cost: " + (canAfford ? "§6" : "§c") + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + lore.add("§7Chocolate Spent: §6" + ChocolateFactoryHelper.formatChocolate(data.getTotalChocolateSpent())); lore.add(""); - lore.add(canAfford ? "§eClick to purchase!" : "§cNot enough Chocolate!"); + lore.add("§eClick to view!"); + return ItemStackCreator.getStack("§6Chocolate Shop Milestones", Material.LADDER, 1, lore); + }, (click, c) -> ((SkyBlockPlayer) c.player()).openView(new GUIChocolateShopMilestones())); + } - if (textureId != null && material == Material.PLAYER_HEAD) { - return ItemStackCreator.getStackHead(name, textureId, 1, lore); - } - return ItemStackCreator.getStack(name, material, 1, lore); - }, (click, c) -> { - SkyBlockPlayer player = (SkyBlockPlayer) c.player(); - DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); - - if (data.getChocolate() >= cost) { - data.removeChocolate(cost); - data.addChocolateSpent(cost); - player.getChocolateFactoryDatapoint().setValue(data); - player.sendMessage("§aPurchased " + name + " §afor §6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate§a!"); - c.session(State.class).refresh(); - } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - } - }); + private void addCostLore(List lore, SkyBlockPlayer player, long cost) { + lore.add("§7Cost"); + lore.add("§6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate"); + } + + private ItemStack.Builder createLockedItem(String requirement) { + List lore = new ArrayList<>(); + lore.add("§7???"); + lore.add(""); + lore.add(requirement); + return ItemStackCreator.getStack("§c???", Material.GRAY_DYE, 1, lore); + } + + private void handlePurchase(SkyBlockPlayer player, long cost, String itemName, int requiredPrestige, ViewContext c) { + DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); + + if (data.getPrestigeLevel() < requiredPrestige) { + player.sendMessage("§cYou don't meet the requirements for this item!"); + return; + } + + if (data.getChocolate() >= cost) { + data.removeChocolate(cost); + data.addChocolateSpent(cost); + player.getChocolateFactoryDatapoint().setValue(data); + player.sendMessage("§aPurchased " + itemName + " §afor §6" + ChocolateFactoryHelper.formatChocolate(cost) + " Chocolate§a!"); + c.session(State.class).refresh(); + } else { + player.sendMessage("§cYou don't have enough Chocolate!"); + } } public static void open(SkyBlockPlayer player) { - ViewNavigator.get(player).push(new GUIChocolateShop()); + player.openView(new GUIChocolateShop()); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java index 14a84917f..b916d4c84 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java @@ -82,6 +82,8 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { boolean found = ChocolateFactoryHelper.getData(p).getFoundRabbits().contains(rabbit.name()); return createRabbitItem(rabbit, found); }); + } else { + layout.slot(slot, (s, c) -> ItemStack.AIR.builder()); } } @@ -252,13 +254,12 @@ private ItemStack.Builder createRabbitItem(ChocolateRabbit rabbit, boolean found } } - if (found) { - lore.add("§a§lFOUND"); - } else { + if (!found) { lore.add("§8You have not found this rabbit yet!"); } if (rabbit.getLocation() != null) { + lore.add(""); lore.add(rabbit.getResidentLabel()); } lore.add(""); From 11f228fc2fa8531cb447586c921ede1b9d7d8171 Mon Sep 17 00:00:00 2001 From: Vicente Date: Fri, 6 Feb 2026 11:19:14 -0300 Subject: [PATCH 13/13] Fixed Stuff --- .../type/hub/hoppity/HoppityEggEntity.java | 13 +- .../hub/hoppity/HoppityHuntEntityManager.java | 9 +- .../type/hub/npcs/ChocolateFactoryRank.java | 3 +- .../type/hub/npcs/NPCCoachJackrabbit.java | 4 +- .../ChocolateFactoryHelper.java | 120 ++++++---- .../chocolatefactory/ChocolateMilestone.java | 32 ++- .../ChocolateShopMilestone.java | 32 ++- .../chocolatefactory/HoppityEggLocations.java | 4 + .../chocolatefactory/HoppityEggType.java | 26 +- .../chocolatefactory/HoppityHuntManager.java | 157 +++++++----- .../commands/ChocolateFactoryCommand.java | 4 +- .../commands/HoppityHuntCommand.java | 13 +- .../datapoints/DatapointChocolateFactory.java | 226 ++++++++++-------- .../gui/inventories/GUIChocolateFactory.java | 90 +++---- .../GUIChocolateFactoryMilestones.java | 50 ++-- .../gui/inventories/GUIChocolateShop.java | 3 - .../gui/inventories/GUIHoppityCollection.java | 30 +-- 17 files changed, 463 insertions(+), 353 deletions(-) diff --git a/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java index ed8473316..c6c55ee1b 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java +++ b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityEggEntity.java @@ -13,10 +13,15 @@ import net.swofty.type.skyblockgeneric.chocolatefactory.HoppityEggType; import org.json.JSONObject; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.concurrent.ThreadLocalRandom; public class HoppityEggEntity extends EntityCreature { + private static final float MAX_YAW = 360f; + private static final double Y_SPAWN_OFFSET = 1.46875; + private static final String TEXTURE_URL_PREFIX = "http://textures.minecraft.net/texture/"; + private final HoppityEggLocations location; private final HoppityEggType eggType; @@ -41,14 +46,14 @@ public HoppityEggEntity(HoppityEggLocations location, HoppityEggType eggType) { } public void spawn(Instance instance) { - float randomYaw = ThreadLocalRandom.current().nextFloat() * 360f; - setInstance(instance, location.getPosition().sub(0, 1.46875, 0).withYaw(randomYaw)); + float randomYaw = ThreadLocalRandom.current().nextFloat() * MAX_YAW; + setInstance(instance, location.getPosition().sub(0, Y_SPAWN_OFFSET, 0).withYaw(randomYaw)); } private static String encodeTexture(String textureHash) { JSONObject json = new JSONObject(); json.put("textures", new JSONObject().put("SKIN", - new JSONObject().put("url", "http://textures.minecraft.net/texture/" + textureHash))); - return Base64.getEncoder().encodeToString(json.toString().getBytes()); + new JSONObject().put("url", TEXTURE_URL_PREFIX + textureHash))); + return Base64.getEncoder().encodeToString(json.toString().getBytes(StandardCharsets.UTF_8)); } } diff --git a/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java index e18a3a304..cddc69821 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java +++ b/type.hub/src/main/java/net/swofty/type/hub/hoppity/HoppityHuntEntityManager.java @@ -12,12 +12,15 @@ import java.util.Map; public class HoppityHuntEntityManager { + private static final float EGG_INTERACTION_WIDTH = 1.0f; + private static final float EGG_INTERACTION_HEIGHT = 1.0f; private final List spawnedEggs = new ArrayList<>(); private final List spawnedInteractions = new ArrayList<>(); public void spawnAllEggs() { - Map locationEggTypes = HoppityHuntManager.getInstance().getLocationEggTypes(); + HoppityHuntManager huntManager = HoppityHuntManager.getInstance(); + Map locationEggTypes = huntManager.getLocationEggTypes(); for (Map.Entry entry : locationEggTypes.entrySet()) { HoppityEggLocations location = entry.getKey(); @@ -27,9 +30,9 @@ public void spawnAllEggs() { eggEntity.spawn(HypixelConst.getInstanceContainer()); spawnedEggs.add(eggEntity); - InteractionEntity interaction = new InteractionEntity(1.0f, 1.0f, (player, event) -> { + InteractionEntity interaction = new InteractionEntity(EGG_INTERACTION_WIDTH, EGG_INTERACTION_HEIGHT, (player, event) -> { if (player instanceof SkyBlockPlayer skyBlockPlayer) { - HoppityHuntManager.getInstance().claimEgg(skyBlockPlayer, location); + huntManager.claimEgg(skyBlockPlayer, location); } }); interaction.setInstance(HypixelConst.getInstanceContainer(), location.getPosition()); diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java index 170359e9d..15f442cba 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/ChocolateFactoryRank.java @@ -19,6 +19,7 @@ public enum ChocolateFactoryRank { private final int level; private final String name; private final ChatColor color; + private static final ChocolateFactoryRank[] RANKS = values(); /** * Gets the formatted hologram line for this rank using the rank's base level. @@ -55,7 +56,7 @@ public String getChatName(String npcName) { */ public static ChocolateFactoryRank fromLevel(int level) { ChocolateFactoryRank result = UNEMPLOYED; - for (ChocolateFactoryRank rank : values()) { + for (ChocolateFactoryRank rank : RANKS) { if (level >= rank.level) { result = rank; } diff --git a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java index 5eabebc76..e8737883d 100644 --- a/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java +++ b/type.hub/src/main/java/net/swofty/type/hub/npcs/NPCCoachJackrabbit.java @@ -8,6 +8,8 @@ import net.swofty.type.skyblockgeneric.gui.inventories.GUIChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import java.time.Duration; + public class NPCCoachJackrabbit extends HypixelNPC { public NPCCoachJackrabbit() { @@ -47,7 +49,7 @@ public String chatName() { @Override public void onClick(NPCInteractEvent e) { if (isInDialogue(e.player())) return; - new GUIChocolateFactory().open((SkyBlockPlayer) e.player()); + ((SkyBlockPlayer) e.player()).openView(new GUIChocolateFactory()).refreshEvery(Duration.ofSeconds(1)); } @Override diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java index 96ccb8257..f9b93304d 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateFactoryHelper.java @@ -9,6 +9,7 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Locale; +import java.util.function.Consumer; /** * Helper class for Chocolate Factory operations. @@ -16,6 +17,22 @@ */ @UtilityClass public class ChocolateFactoryHelper { + private static final int SECONDS_PER_HOUR = 3600; + + private static final int HAND_BAKED_MAX_INTERNAL_LEVEL = 9; + private static final long HAND_BAKED_COST_STEP = 500L; + + private static final long RABBIT_BARN_BASE_COST = 5000L; + private static final double RABBIT_BARN_COST_GROWTH = 1.05; + + private static final String RABBIT_BRO_NAME = "Rabbit Bro"; + private static final int RABBIT_BRO_SPECIAL_MAX_LEVEL = 10; + private static final int RABBIT_BRO_BASE_COST = 30; + private static final int RABBIT_BRO_CF_SCALING = 20; + private static final int EMPLOYEE_BASE_COST = 216; + private static final double EMPLOYEE_CF_SCALING = 144.0; + private static final double EMPLOYEE_COST_GROWTH = 1.05; + private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance(Locale.US); private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,##0.0"); @@ -37,20 +54,14 @@ public static DatapointChocolateFactory getDatapoint(SkyBlockPlayer player) { * Updates chocolate production for the player based on time elapsed */ public static void updateProduction(SkyBlockPlayer player) { - DatapointChocolateFactory datapoint = player.getChocolateFactoryDatapoint(); - DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); - data.updateChocolateFromProduction(); - datapoint.setValue(data); + mutateData(player, DatapointChocolateFactory.ChocolateFactoryData::updateChocolateFromProduction); } /** * Handles a click on the chocolate cookie */ public static void handleClick(SkyBlockPlayer player) { - DatapointChocolateFactory datapoint = player.getChocolateFactoryDatapoint(); - DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); - data.click(); - datapoint.setValue(data); + mutateData(player, DatapointChocolateFactory.ChocolateFactoryData::click); } /** @@ -71,7 +82,7 @@ public static String formatProductionPerSecond(double production) { * Formats production per hour for display */ public static String formatProductionPerHour(double productionPerSecond) { - double perHour = productionPerSecond * 3600; + double perHour = productionPerSecond * SECONDS_PER_HOUR; return formatChocolate((long) perHour) + "/h"; } @@ -82,8 +93,8 @@ public static String formatProductionPerHour(double productionPerSecond) { * Max internal level is 9 (display level 10) */ public static long getHandBakedChocolateCost(int currentLevel) { - if (currentLevel >= 9) return 0; // Already maxed at level 10 - return 500L * (currentLevel + 1); + if (currentLevel >= HAND_BAKED_MAX_INTERNAL_LEVEL) return 0; // Already maxed at level 10 + return HAND_BAKED_COST_STEP * (currentLevel + 1); } /** @@ -98,7 +109,7 @@ public static long getRabbitBarnCost(int currentLevel) { return 0; } // Cost formula: 5000 × 1.05^(currentLevel-1), rounded - return Math.round(5000 * Math.pow(1.05, currentLevel - 1)); + return Math.round(RABBIT_BARN_BASE_COST * Math.pow(RABBIT_BARN_COST_GROWTH, currentLevel - 1)); } /** @@ -215,16 +226,7 @@ public static long getCoachJackrabbitCost(int currentLevel) { * Gets the employee index (1-7) for cost calculation */ public static int getEmployeeIndex(String employeeName) { - return switch (employeeName) { - case "Rabbit Bro" -> 1; - case "Rabbit Cousin" -> 2; - case "Rabbit Sis" -> 3; - case "Rabbit Daddy" -> 4; - case "Rabbit Granny" -> 5; - case "Rabbit Uncle" -> 6; - case "Rabbit Dog" -> 7; - default -> 1; - }; + return getEmployeeType(employeeName).getIndex(); } /** @@ -233,18 +235,19 @@ public static int getEmployeeIndex(String employeeName) { * For all other cases: base_cost × 1.05^L where base_cost = (216 + 144 × CF) × i² */ public static long getEmployeeCost(String employeeName, int targetLevel, int chocolateFactoryLevel) { - int employeeIndex = getEmployeeIndex(employeeName); + EmployeeType employeeType = getEmployeeType(employeeName); + int employeeIndex = employeeType.getIndex(); int cf = chocolateFactoryLevel; // CF level 1-6 // Rabbit Bro's first 10 levels use special formula - if (employeeName.equals("Rabbit Bro") && targetLevel <= 10) { + if (RABBIT_BRO_NAME.equals(employeeName) && targetLevel <= RABBIT_BRO_SPECIAL_MAX_LEVEL) { double multiplier = getRabbitBroMultiplier(targetLevel); - return (long) ((30 + 20 * cf) * multiplier); + return (long) ((RABBIT_BRO_BASE_COST + RABBIT_BRO_CF_SCALING * cf) * multiplier); } // All other cases: base_cost × 1.05^L - double baseCost = (216 + 144.0 * cf) * (employeeIndex * employeeIndex); - return (long) (baseCost * Math.pow(1.05, targetLevel)); + double baseCost = (EMPLOYEE_BASE_COST + EMPLOYEE_CF_SCALING * cf) * (employeeIndex * employeeIndex); + return (long) (baseCost * Math.pow(EMPLOYEE_COST_GROWTH, targetLevel)); } /** @@ -286,16 +289,7 @@ private static double getRabbitBroMultiplier(int level) { * This is the chocolate per second gained per employee level */ public static double getEmployeeBaseProduction(String employeeName) { - return switch (employeeName) { - case "Rabbit Bro" -> 1.0; // +1/level - case "Rabbit Cousin" -> 2.0; // +2/level - case "Rabbit Sis" -> 3.0; // +3/level - case "Rabbit Daddy" -> 4.0; // +4/level - case "Rabbit Granny" -> 5.0; // +5/level - case "Rabbit Uncle" -> 6.0; // +6/level - case "Rabbit Dog" -> 7.0; // +7/level - default -> 1.0; - }; + return getEmployeeType(employeeName).getBaseProductionPerLevel(); } /** @@ -303,16 +297,7 @@ public static double getEmployeeBaseProduction(String employeeName) { * Returns null if no prerequisite (Rabbit Bro) */ public static String getEmployeePrerequisite(String employeeName) { - return switch (employeeName) { - case "Rabbit Bro" -> null; // No prerequisite - case "Rabbit Cousin" -> "Rabbit Bro"; - case "Rabbit Sis" -> "Rabbit Cousin"; - case "Rabbit Daddy" -> "Rabbit Sis"; - case "Rabbit Granny" -> "Rabbit Daddy"; - case "Rabbit Uncle" -> "Rabbit Granny"; - case "Rabbit Dog" -> "Rabbit Uncle"; - default -> null; - }; + return getEmployeeType(employeeName).getPrerequisiteEmployee(); } /** @@ -403,10 +388,7 @@ public static boolean activateTimeTower(SkyBlockPlayer player) { * Adds a Time Tower charge to the player */ public static void addTimeTowerCharge(SkyBlockPlayer player) { - DatapointChocolateFactory datapoint = getDatapoint(player); - DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); - data.setTimeTowerCharges(data.getTimeTowerCharges() + 1); - datapoint.setValue(data); + mutateData(player, data -> data.setTimeTowerCharges(data.getTimeTowerCharges() + 1)); } /** @@ -430,6 +412,42 @@ public static Prestige getPrestige(int level) { return Prestige.fromLevel(level); } + private static void mutateData(SkyBlockPlayer player, Consumer mutator) { + DatapointChocolateFactory datapoint = getDatapoint(player); + DatapointChocolateFactory.ChocolateFactoryData data = datapoint.getValue(); + mutator.accept(data); + datapoint.setValue(data); + } + + private static EmployeeType getEmployeeType(String employeeName) { + return switch (employeeName) { + case "Rabbit Bro" -> EmployeeType.RABBIT_BRO; + case "Rabbit Cousin" -> EmployeeType.RABBIT_COUSIN; + case "Rabbit Sis" -> EmployeeType.RABBIT_SIS; + case "Rabbit Daddy" -> EmployeeType.RABBIT_DADDY; + case "Rabbit Granny" -> EmployeeType.RABBIT_GRANNY; + case "Rabbit Uncle" -> EmployeeType.RABBIT_UNCLE; + case "Rabbit Dog" -> EmployeeType.RABBIT_DOG; + default -> EmployeeType.RABBIT_BRO; + }; + } + + @Getter + @AllArgsConstructor + private enum EmployeeType { + RABBIT_BRO(1, 1.0, null), + RABBIT_COUSIN(2, 2.0, RABBIT_BRO_NAME), + RABBIT_SIS(3, 3.0, "Rabbit Cousin"), + RABBIT_DADDY(4, 4.0, "Rabbit Sis"), + RABBIT_GRANNY(5, 5.0, "Rabbit Daddy"), + RABBIT_UNCLE(6, 6.0, "Rabbit Granny"), + RABBIT_DOG(7, 7.0, "Rabbit Uncle"); + + private final int index; + private final double baseProductionPerLevel; + private final String prerequisiteEmployee; + } + @Getter @AllArgsConstructor public enum Prestige { diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java index 794080563..e4180b0ba 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateMilestone.java @@ -3,6 +3,10 @@ import lombok.AllArgsConstructor; import lombok.Getter; import net.minestom.server.item.Material; +import net.swofty.commons.StringUtility; + +import java.util.HashMap; +import java.util.Map; /** * Represents chocolate factory milestones. @@ -78,8 +82,10 @@ public enum ChocolateMilestone { private final String textureId; private final Material glassPaneMaterial; + private static final Map MILESTONES_BY_NUMBER = createMilestonesByNumber(); + public String getRomanNumeral() { - return toRoman(number); + return StringUtility.getAsRomanNumeral(number); } public String getFormattedRequirement() { @@ -96,24 +102,14 @@ public double getProgress(long allTimeChocolate) { } public static ChocolateMilestone fromNumber(int number) { - for (ChocolateMilestone milestone : values()) { - if (milestone.number == number) { - return milestone; - } - } - return null; + return MILESTONES_BY_NUMBER.get(number); } - private static String toRoman(int number) { - if (number <= 0) return "I"; - String[] thousands = {"", "M", "MM", "MMM"}; - String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; - String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; - String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; - - return thousands[number / 1000] + - hundreds[(number % 1000) / 100] + - tens[(number % 100) / 10] + - ones[number % 10]; + private static Map createMilestonesByNumber() { + Map milestonesByNumber = new HashMap<>(); + for (ChocolateMilestone milestone : values()) { + milestonesByNumber.put(milestone.number, milestone); + } + return milestonesByNumber; } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java index 4bfe33234..a569d4ec2 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/ChocolateShopMilestone.java @@ -3,6 +3,10 @@ import lombok.AllArgsConstructor; import lombok.Getter; import net.minestom.server.item.Material; +import net.swofty.commons.StringUtility; + +import java.util.HashMap; +import java.util.Map; /** * Represents chocolate shop milestones. @@ -78,8 +82,10 @@ public enum ChocolateShopMilestone { private final String textureId; private final Material glassPaneMaterial; + private static final Map MILESTONES_BY_NUMBER = createMilestonesByNumber(); + public String getRomanNumeral() { - return toRoman(number); + return StringUtility.getAsRomanNumeral(number); } public String getFormattedRequirement() { @@ -96,24 +102,14 @@ public double getProgress(long totalSpent) { } public static ChocolateShopMilestone fromNumber(int number) { - for (ChocolateShopMilestone milestone : values()) { - if (milestone.number == number) { - return milestone; - } - } - return null; + return MILESTONES_BY_NUMBER.get(number); } - private static String toRoman(int number) { - if (number <= 0) return "I"; - String[] thousands = {"", "M", "MM", "MMM"}; - String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; - String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; - String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; - - return thousands[number / 1000] + - hundreds[(number % 1000) / 100] + - tens[(number % 100) / 10] + - ones[number % 10]; + private static Map createMilestonesByNumber() { + Map milestonesByNumber = new HashMap<>(); + for (ChocolateShopMilestone milestone : values()) { + milestonesByNumber.put(milestone.number, milestone); + } + return milestonesByNumber; } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java index e574f1abd..f9efab97f 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggLocations.java @@ -27,4 +27,8 @@ public enum HoppityEggLocations { private final Pos position; private final String locationMessage; + + public static int totalLocations() { + return values().length; + } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java index 1e9ab05c9..939ba2918 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityEggType.java @@ -6,13 +6,13 @@ @Getter @AllArgsConstructor public enum HoppityEggType { - BREAKFAST("Chocolate Breakfast Egg", "6", "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"), - LUNCH("Chocolate Lunch Egg", "9", "e5e36165819fd2850f98552edcd763ff986313119283c126ace0c4cc495e76a8"), - DINNER("Chocolate Dinner Egg", "a", "7ae6d2d31d8167bcaf95293b68a4acd872d66e751db5a34f2cbc6766a0356d0a"), - BRUNCH("Chocolate Brunch Egg", "6", "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"), - DEJEUNER("Chocolate Déjeuner Egg", "9", "e5e36165819fd2850f98552edcd763ff986313119283c126ace0c4cc495e76a8"), - SUPPER("Chocolate Supper Egg", "a", "7ae6d2d31d8167bcaf95293b68a4acd872d66e751db5a34f2cbc6766a0356d0a"), - HITMAN("Hitman Egg", "c", "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"); + BREAKFAST("Chocolate Breakfast Egg", "6", breakfastTexture()), + LUNCH("Chocolate Lunch Egg", "9", lunchTexture()), + DINNER("Chocolate Dinner Egg", "a", dinnerTexture()), + BRUNCH("Chocolate Brunch Egg", "6", breakfastTexture()), + DEJEUNER("Chocolate Déjeuner Egg", "9", lunchTexture()), + SUPPER("Chocolate Supper Egg", "a", dinnerTexture()), + HITMAN("Hitman Egg", "c", breakfastTexture()); private final String displayName; private final String colorCode; @@ -21,4 +21,16 @@ public enum HoppityEggType { public String getFormattedName() { return "§" + colorCode + displayName; } + + private static String breakfastTexture() { + return "a49333d85b8a315d0336eb2df37d8a714ca24c51b8c6074f1b5b927deb516c24"; + } + + private static String lunchTexture() { + return "e5e36165819fd2850f98552edcd763ff986313119283c126ace0c4cc495e76a8"; + } + + private static String dinnerTexture() { + return "7ae6d2d31d8167bcaf95293b68a4acd872d66e751db5a34f2cbc6766a0356d0a"; + } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java index d3de0f6fb..982899ce7 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/chocolatefactory/HoppityHuntManager.java @@ -5,10 +5,29 @@ import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; import java.util.concurrent.ThreadLocalRandom; public class HoppityHuntManager { + private static final String HUB_LOCATION = "Hub"; + private static final String EGG_ALREADY_CLAIMED_MESSAGE = "§cYou have already claimed this egg!"; + private static final String NEW_RABBIT_MESSAGE_PREFIX = "§d§lNEW RABBIT! §r§"; + private static final String DUPLICATE_RABBIT_MESSAGE_PREFIX = "§d§lDUPLICATE RABBIT! §r§"; + private static final String HUNT_MESSAGE_PREFIX = "§d§lHOPPITY'S HUNT §r"; + private static final int DEFAULT_DUPLICATE_MULTIPLIER = 150; + private static final double ONE_MILLION = 1_000_000.0; + private static final int ONE_THOUSAND = 1_000; + + private static final HoppityEggType[] HUNT_EGG_TYPES = { + HoppityEggType.BREAKFAST, HoppityEggType.LUNCH, HoppityEggType.DINNER, + HoppityEggType.BRUNCH, HoppityEggType.DEJEUNER, HoppityEggType.SUPPER + }; + @Getter private static final HoppityHuntManager instance = new HoppityHuntManager(); @@ -20,59 +39,27 @@ public class HoppityHuntManager { @Getter private boolean active; @Getter - private final Map locationEggTypes = new HashMap<>(); + private final Map locationEggTypes = new EnumMap<>(HoppityEggLocations.class); - private static final NavigableMap RARITY_WEIGHTS = new TreeMap<>(); + private static final NavigableMap RARITY_WEIGHTS = createRarityWeights(); + private static final Map DUPLICATE_MULTIPLIERS = createDuplicateMultipliers(); + private static final Map> ELIGIBLE_RABBITS = createEligibleRabbits(); - static { - double cumulative = 0; - cumulative += 608; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.COMMON); - cumulative += 250; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.UNCOMMON); - cumulative += 100; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.RARE); - cumulative += 30; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.EPIC); - cumulative += 10; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.LEGENDARY); - cumulative += 2; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.MYTHIC); - cumulative += 0.5; - RARITY_WEIGHTS.put(cumulative, ChocolateRabbit.Rarity.DIVINE); + private HoppityHuntManager() { } - private static final Map DUPLICATE_MULTIPLIERS = Map.of( - ChocolateRabbit.Rarity.COMMON, 150, - ChocolateRabbit.Rarity.UNCOMMON, 300, - ChocolateRabbit.Rarity.RARE, 750, - ChocolateRabbit.Rarity.EPIC, 1500, - ChocolateRabbit.Rarity.LEGENDARY, 4000, - ChocolateRabbit.Rarity.MYTHIC, 8000, - ChocolateRabbit.Rarity.DIVINE, 15000 - ); - - private HoppityHuntManager() {} - public void startHunt() { if (active) return; active = true; locationEggTypes.clear(); - HoppityEggType[] eggTypes = { - HoppityEggType.BREAKFAST, HoppityEggType.LUNCH, HoppityEggType.DINNER, - HoppityEggType.BRUNCH, HoppityEggType.DEJEUNER, HoppityEggType.SUPPER - }; - HoppityEggLocations[] locations = HoppityEggLocations.values(); for (int i = 0; i < locations.length; i++) { - locationEggTypes.put(locations[i], eggTypes[i % eggTypes.length]); + locationEggTypes.put(locations[i], HUNT_EGG_TYPES[i % HUNT_EGG_TYPES.length]); } - if (onHuntStartCallback != null) { - onHuntStartCallback.run(); - } + runCallback(onHuntStartCallback); } public void stopHunt() { @@ -80,9 +67,7 @@ public void stopHunt() { active = false; - if (onHuntStopCallback != null) { - onHuntStopCallback.run(); - } + runCallback(onHuntStopCallback); locationEggTypes.clear(); } @@ -93,7 +78,7 @@ public void claimEgg(SkyBlockPlayer player, HoppityEggLocations location) { DatapointChocolateFactory.ChocolateFactoryData data = ChocolateFactoryHelper.getData(player); if (data.hasClaimedEgg(location.name())) { - player.sendMessage("§cYou have already claimed this egg!"); + player.sendMessage(EGG_ALREADY_CLAIMED_MESSAGE); return; } @@ -105,17 +90,17 @@ public void claimEgg(SkyBlockPlayer player, HoppityEggLocations location) { ChocolateRabbit.Rarity rarity = rollRarity(); ChocolateRabbit rabbit = rollRabbit(rarity); - player.sendMessage("§d§lHOPPITY'S HUNT §r" + eggType.getFormattedName() + " §r" + location.getLocationMessage()); + player.sendMessage(HUNT_MESSAGE_PREFIX + eggType.getFormattedName() + " §r" + location.getLocationMessage()); if (rabbit != null && !data.hasFoundRabbit(rabbit.name())) { data.addFoundRabbit(rabbit.name()); - player.sendMessage("§d§lNEW RABBIT! §r§" + rarity.getColorCode() + rabbit.getDisplayName() + " §7(" + rarity.getFormattedName() + "§7)"); + player.sendMessage(NEW_RABBIT_MESSAGE_PREFIX + rarity.getColorCode() + rabbit.getDisplayName() + " §7(" + rarity.getFormattedName() + "§7)"); player.sendMessage("§7+§6" + rarity.getChocolateBonus() + " Chocolate §7per second"); } else { long duplicateChocolate = getDuplicateChocolate(rarity, data); data.addChocolate(duplicateChocolate); String rabbitName = rabbit != null ? rabbit.getDisplayName() : "Unknown"; - player.sendMessage("§d§lDUPLICATE RABBIT! §r§" + rarity.getColorCode() + rabbitName); + player.sendMessage(DUPLICATE_RABBIT_MESSAGE_PREFIX + rarity.getColorCode() + rabbitName); player.sendMessage("§7+§6" + formatNumber(duplicateChocolate) + " Chocolate"); } @@ -129,30 +114,84 @@ private ChocolateRabbit.Rarity rollRarity() { } private ChocolateRabbit rollRabbit(ChocolateRabbit.Rarity rarity) { - List eligible = new ArrayList<>(); - for (ChocolateRabbit rabbit : ChocolateRabbit.values()) { - if (rabbit.getRarity() != rarity) continue; - if (rabbit.getObtainMethod() != null) continue; - if (rabbit.getLocation() != null && !rabbit.getLocation().equals("Hub")) continue; - eligible.add(rabbit); - } - + List eligible = ELIGIBLE_RABBITS.getOrDefault(rarity, List.of()); if (eligible.isEmpty()) return null; return eligible.get(ThreadLocalRandom.current().nextInt(eligible.size())); } private long getDuplicateChocolate(ChocolateRabbit.Rarity rarity, DatapointChocolateFactory.ChocolateFactoryData data) { - int multiplier = DUPLICATE_MULTIPLIERS.getOrDefault(rarity, 150); + int multiplier = DUPLICATE_MULTIPLIERS.getOrDefault(rarity, DEFAULT_DUPLICATE_MULTIPLIER); double cps = data.getChocolatePerSecond(); return Math.max(multiplier, (long) (cps * multiplier)); } private String formatNumber(long number) { - if (number >= 1_000_000) { - return String.format("%.1fM", number / 1_000_000.0); - } else if (number >= 1_000) { + if (number >= ONE_MILLION) { + return String.format("%.1fM", number / ONE_MILLION); + } + if (number >= ONE_THOUSAND) { return String.format("%,d", number); } return String.valueOf(number); } + + private static NavigableMap createRarityWeights() { + NavigableMap weights = new TreeMap<>(); + double cumulative = 0; + cumulative += 608; + weights.put(cumulative, ChocolateRabbit.Rarity.COMMON); + cumulative += 250; + weights.put(cumulative, ChocolateRabbit.Rarity.UNCOMMON); + cumulative += 100; + weights.put(cumulative, ChocolateRabbit.Rarity.RARE); + cumulative += 30; + weights.put(cumulative, ChocolateRabbit.Rarity.EPIC); + cumulative += 10; + weights.put(cumulative, ChocolateRabbit.Rarity.LEGENDARY); + cumulative += 2; + weights.put(cumulative, ChocolateRabbit.Rarity.MYTHIC); + cumulative += 0.5; + weights.put(cumulative, ChocolateRabbit.Rarity.DIVINE); + return weights; + } + + private static Map createDuplicateMultipliers() { + Map multipliers = new EnumMap<>(ChocolateRabbit.Rarity.class); + multipliers.put(ChocolateRabbit.Rarity.COMMON, 150); + multipliers.put(ChocolateRabbit.Rarity.UNCOMMON, 300); + multipliers.put(ChocolateRabbit.Rarity.RARE, 750); + multipliers.put(ChocolateRabbit.Rarity.EPIC, 1500); + multipliers.put(ChocolateRabbit.Rarity.LEGENDARY, 4000); + multipliers.put(ChocolateRabbit.Rarity.MYTHIC, 8000); + multipliers.put(ChocolateRabbit.Rarity.DIVINE, 15000); + return multipliers; + } + + private static Map> createEligibleRabbits() { + Map> rabbitsByRarity = new EnumMap<>(ChocolateRabbit.Rarity.class); + for (ChocolateRabbit.Rarity rarity : ChocolateRabbit.Rarity.values()) { + rabbitsByRarity.put(rarity, new ArrayList<>()); + } + + for (ChocolateRabbit rabbit : ChocolateRabbit.values()) { + if (rabbit.getObtainMethod() != null) { + continue; + } + + String rabbitLocation = rabbit.getLocation(); + if (rabbitLocation != null && !HUB_LOCATION.equals(rabbitLocation)) { + continue; + } + + rabbitsByRarity.get(rabbit.getRarity()).add(rabbit); + } + + return rabbitsByRarity; + } + + private void runCallback(Runnable callback) { + if (callback != null) { + callback.run(); + } + } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java index 49e16f5e2..b55d5ccd1 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/ChocolateFactoryCommand.java @@ -6,6 +6,8 @@ import net.swofty.type.skyblockgeneric.gui.inventories.GUIChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; +import java.time.Duration; + @CommandParameters(aliases = "cf factory", description = "Opens the Chocolate Factory menu", usage = "/chocolatefactory", @@ -18,7 +20,7 @@ public void registerUsage(MinestomCommand command) { if (!permissionCheck(sender)) return; SkyBlockPlayer player = (SkyBlockPlayer) sender; - new GUIChocolateFactory().open(player); + player.openView(new GUIChocolateFactory()).refreshEvery(Duration.ofSeconds(1)); }); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java index d9d13c593..16d69dda6 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/HoppityHuntCommand.java @@ -11,6 +11,11 @@ permission = Rank.STAFF, allowsConsole = false) public class HoppityHuntCommand extends HypixelCommand { + private static final String HUNT_ALREADY_ACTIVE_MESSAGE = "§cHoppity's Hunt is already active!"; + private static final String HUNT_NOT_ACTIVE_MESSAGE = "§cHoppity's Hunt is not currently active!"; + private static final String HUNT_STARTED_MESSAGE = "§aHoppity's Hunt has been started! §e17 eggs §ahave been spawned."; + private static final String HUNT_STOPPED_MESSAGE = "§aHoppity's Hunt has been stopped! All eggs have been removed."; + @Override public void registerUsage(MinestomCommand command) { command.addSyntax((sender, context) -> { @@ -18,12 +23,12 @@ public void registerUsage(MinestomCommand command) { HoppityHuntManager manager = HoppityHuntManager.getInstance(); if (manager.isActive()) { - sender.sendMessage("§cHoppity's Hunt is already active!"); + sender.sendMessage(HUNT_ALREADY_ACTIVE_MESSAGE); return; } manager.startHunt(); - sender.sendMessage("§aHoppity's Hunt has been started! §e17 eggs §ahave been spawned."); + sender.sendMessage(HUNT_STARTED_MESSAGE); }, ArgumentType.Literal("start")); command.addSyntax((sender, context) -> { @@ -31,12 +36,12 @@ public void registerUsage(MinestomCommand command) { HoppityHuntManager manager = HoppityHuntManager.getInstance(); if (!manager.isActive()) { - sender.sendMessage("§cHoppity's Hunt is not currently active!"); + sender.sendMessage(HUNT_NOT_ACTIVE_MESSAGE); return; } manager.stopHunt(); - sender.sendMessage("§aHoppity's Hunt has been stopped! All eggs have been removed."); + sender.sendMessage(HUNT_STOPPED_MESSAGE); }, ArgumentType.Literal("stop")); } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java index 21760793a..6569bd210 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/data/datapoints/DatapointChocolateFactory.java @@ -6,9 +6,8 @@ import lombok.Setter; import net.swofty.commons.protocol.Serializer; import net.swofty.type.skyblockgeneric.data.SkyBlockDatapoint; -import org.json.JSONObject; - import org.json.JSONArray; +import org.json.JSONObject; import java.util.HashMap; import java.util.HashSet; @@ -21,87 +20,47 @@ public class DatapointChocolateFactory extends SkyBlockDatapoint entry : value.employees.entrySet()) { - JSONObject employeeJson = new JSONObject(); - employeeJson.put("level", entry.getValue().level); - employeeJson.put("baseProduction", entry.getValue().baseProduction); - employeesJson.put(entry.getKey(), employeeJson); - } - json.put("employees", employeesJson); + json.put("timeTowerLevel", value.getTimeTowerLevel()); + json.put("timeTowerCharges", value.getTimeTowerCharges()); + json.put("timeTowerLastUsed", value.getTimeTowerLastUsed()); + json.put("timeTowerActiveUntil", value.getTimeTowerActiveUntil()); + json.put("rabbitBarnLevel", value.getRabbitBarnLevel()); + json.put("handBakedChocolateLevel", value.getHandBakedChocolateLevel()); + json.put("rabbitShrineLevel", value.getRabbitShrineLevel()); + json.put("coachJackrabbitLevel", value.getCoachJackrabbitLevel()); + json.put("employees", serializeEmployees(value.getEmployees())); // Production stats - json.put("totalClicks", value.totalClicks); - json.put("totalTimeTowerUsages", value.totalTimeTowerUsages); - - // Found rabbits - JSONArray foundRabbitsArray = new JSONArray(); - for (String rabbit : value.foundRabbits) { - foundRabbitsArray.put(rabbit); - } - json.put("foundRabbits", foundRabbitsArray); + json.put("totalClicks", value.getTotalClicks()); + json.put("totalTimeTowerUsages", value.getTotalTimeTowerUsages()); + json.put("foundRabbits", serializeStringSet(value.getFoundRabbits())); // Shop stats - json.put("totalChocolateSpent", value.totalChocolateSpent); + json.put("totalChocolateSpent", value.getTotalChocolateSpent()); // Hoppity Hunt - JSONArray claimedEggsArray = new JSONArray(); - for (String egg : value.claimedEggs) { - claimedEggsArray.put(egg); - } - json.put("claimedEggs", claimedEggsArray); - json.put("totalEggsFound", value.totalEggsFound); + json.put("claimedEggs", serializeStringSet(value.getClaimedEggs())); + json.put("totalEggsFound", value.getTotalEggsFound()); return json.toString(); } @Override public ChocolateFactoryData deserialize(String json) { - JSONObject jsonObject = new JSONObject(json); - - Map employees = new HashMap<>(); - if (jsonObject.has("employees")) { - JSONObject employeesJson = jsonObject.getJSONObject("employees"); - for (String key : employeesJson.keySet()) { - JSONObject employeeJson = employeesJson.getJSONObject(key); - employees.put(key, new EmployeeData( - employeeJson.getInt("level"), - employeeJson.getDouble("baseProduction") - )); - } + if (json == null || json.isEmpty()) { + return new ChocolateFactoryData(); } - Set foundRabbits = new HashSet<>(); - if (jsonObject.has("foundRabbits")) { - JSONArray foundRabbitsArray = jsonObject.getJSONArray("foundRabbits"); - for (int i = 0; i < foundRabbitsArray.length(); i++) { - foundRabbits.add(foundRabbitsArray.getString(i)); - } - } - - Set claimedEggs = new HashSet<>(); - if (jsonObject.has("claimedEggs")) { - JSONArray claimedEggsArray = jsonObject.getJSONArray("claimedEggs"); - for (int i = 0; i < claimedEggsArray.length(); i++) { - claimedEggs.add(claimedEggsArray.getString(i)); - } - } + JSONObject jsonObject = new JSONObject(json); + Map employees = deserializeEmployees(jsonObject.optJSONObject("employees")); + Set foundRabbits = deserializeStringSet(jsonObject.optJSONArray("foundRabbits")); + Set claimedEggs = deserializeStringSet(jsonObject.optJSONArray("claimedEggs")); return new ChocolateFactoryData( jsonObject.optLong("chocolate", 0L), @@ -128,36 +87,90 @@ public ChocolateFactoryData deserialize(String json) { @Override public ChocolateFactoryData clone(ChocolateFactoryData value) { - Map clonedEmployees = new HashMap<>(); - for (Map.Entry entry : value.employees.entrySet()) { - clonedEmployees.put(entry.getKey(), new EmployeeData( - entry.getValue().level, - entry.getValue().baseProduction - )); - } + Map clonedEmployees = cloneEmployees(value.getEmployees()); return new ChocolateFactoryData( - value.chocolate, - value.chocolateAllTime, - value.lastUpdated, - value.partialChocolate, - value.timeTowerLevel, - value.timeTowerCharges, - value.timeTowerLastUsed, - value.timeTowerActiveUntil, - value.rabbitBarnLevel, - value.handBakedChocolateLevel, - value.rabbitShrineLevel, - value.coachJackrabbitLevel, + value.getChocolate(), + value.getChocolateAllTime(), + value.getLastUpdated(), + value.getPartialChocolate(), + value.getTimeTowerLevel(), + value.getTimeTowerCharges(), + value.getTimeTowerLastUsed(), + value.getTimeTowerActiveUntil(), + value.getRabbitBarnLevel(), + value.getHandBakedChocolateLevel(), + value.getRabbitShrineLevel(), + value.getCoachJackrabbitLevel(), clonedEmployees, - value.totalClicks, - value.totalTimeTowerUsages, - new HashSet<>(value.foundRabbits), - value.totalChocolateSpent, - new HashSet<>(value.claimedEggs), - value.totalEggsFound + value.getTotalClicks(), + value.getTotalTimeTowerUsages(), + new HashSet<>(value.getFoundRabbits()), + value.getTotalChocolateSpent(), + new HashSet<>(value.getClaimedEggs()), + value.getTotalEggsFound() ); } + + private JSONObject serializeEmployees(Map employees) { + JSONObject employeesJson = new JSONObject(); + for (Map.Entry entry : employees.entrySet()) { + JSONObject employeeJson = new JSONObject(); + EmployeeData employee = entry.getValue(); + employeeJson.put("level", employee.getLevel()); + employeeJson.put("baseProduction", employee.getBaseProduction()); + employeesJson.put(entry.getKey(), employeeJson); + } + return employeesJson; + } + + private Map deserializeEmployees(JSONObject employeesJson) { + Map employees = new HashMap<>(); + if (employeesJson == null) { + return employees; + } + + for (String key : employeesJson.keySet()) { + JSONObject employeeJson = employeesJson.getJSONObject(key); + employees.put(key, new EmployeeData( + employeeJson.optInt("level", 0), + employeeJson.optDouble("baseProduction", 0.0) + )); + } + return employees; + } + + private JSONArray serializeStringSet(Set values) { + JSONArray array = new JSONArray(); + for (String value : values) { + array.put(value); + } + return array; + } + + private Set deserializeStringSet(JSONArray array) { + Set values = new HashSet<>(); + if (array == null) { + return values; + } + + for (int i = 0; i < array.length(); i++) { + values.add(array.getString(i)); + } + return values; + } + + private Map cloneEmployees(Map employees) { + Map clonedEmployees = new HashMap<>(); + for (Map.Entry entry : employees.entrySet()) { + EmployeeData employee = entry.getValue(); + clonedEmployees.put(entry.getKey(), new EmployeeData( + employee.getLevel(), + employee.getBaseProduction() + )); + } + return clonedEmployees; + } }; public DatapointChocolateFactory(String key, ChocolateFactoryData value, Serializer serializer) { @@ -177,6 +190,15 @@ public DatapointChocolateFactory(String key) { @Getter @Setter public static class ChocolateFactoryData { + private static final int BASE_CLICK_POWER = 1; + private static final int BASE_RABBIT_SLOTS = 3; + private static final double BASE_MULTIPLIER = 1.0; + private static final double SHRINE_MULTIPLIER_PER_LEVEL = 0.1; + private static final double TIME_TOWER_MULTIPLIER_PER_LEVEL = 0.1; + private static final double COACH_MULTIPLIER_PER_LEVEL = 0.01; + private static final long TIME_TOWER_DURATION_MS = 60L * 60L * 1000L; + private static final double MILLIS_PER_SECOND = 1000.0; + private long chocolate; private long chocolateAllTime; private long lastUpdated = System.currentTimeMillis(); @@ -235,14 +257,14 @@ public boolean removeChocolate(long amount) { * Base is 1, increases with Hand-Baked Chocolate upgrade */ public int getClickPower() { - return 1 + handBakedChocolateLevel; + return BASE_CLICK_POWER + handBakedChocolateLevel; } /** * Gets the maximum number of rabbit slots based on Rabbit Barn level */ public int getMaxRabbitSlots() { - return 3 + rabbitBarnLevel; + return BASE_RABBIT_SLOTS + rabbitBarnLevel; } /** @@ -250,7 +272,7 @@ public int getMaxRabbitSlots() { * Base is 1.0, increases by 0.1 per level */ public double getShrineMultiplier() { - return 1.0 + (rabbitShrineLevel * 0.1); + return BASE_MULTIPLIER + (rabbitShrineLevel * SHRINE_MULTIPLIER_PER_LEVEL); } /** @@ -259,9 +281,9 @@ public double getShrineMultiplier() { */ public double getTimeTowerMultiplier() { if (System.currentTimeMillis() < timeTowerActiveUntil) { - return 1.0 + (timeTowerLevel * 0.1); + return BASE_MULTIPLIER + (timeTowerLevel * TIME_TOWER_MULTIPLIER_PER_LEVEL); } - return 1.0; + return BASE_MULTIPLIER; } /** @@ -276,9 +298,10 @@ public boolean isTimeTowerActive() { */ public boolean activateTimeTower() { if (timeTowerCharges > 0 && !isTimeTowerActive()) { + long now = System.currentTimeMillis(); timeTowerCharges--; - timeTowerLastUsed = System.currentTimeMillis(); - timeTowerActiveUntil = System.currentTimeMillis() + (60 * 60 * 1000); // 1 hour + timeTowerLastUsed = now; + timeTowerActiveUntil = now + TIME_TOWER_DURATION_MS; // 1 hour totalTimeTowerUsages++; return true; } @@ -290,7 +313,7 @@ public boolean activateTimeTower() { * Base is 1.0, increases by 0.01 per level */ public double getCoachMultiplier() { - return 1.0 + (coachJackrabbitLevel * 0.01); + return BASE_MULTIPLIER + (coachJackrabbitLevel * COACH_MULTIPLIER_PER_LEVEL); } /** @@ -317,7 +340,7 @@ public double getChocolatePerSecond() { */ public void updateChocolateFromProduction() { long now = System.currentTimeMillis(); - double secondsPassed = (now - lastUpdated) / 1000.0; + double secondsPassed = (now - lastUpdated) / MILLIS_PER_SECOND; if (secondsPassed > 0) { // Accumulate fractional production @@ -421,6 +444,7 @@ public void resetClaimedEggs() { } @AllArgsConstructor + @NoArgsConstructor @Getter @Setter public static class EmployeeData { diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java index aa083afd8..f84362db7 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactory.java @@ -6,22 +6,32 @@ import net.minestom.server.inventory.click.Click; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import net.swofty.commons.StringUtility; import net.swofty.type.generic.gui.inventory.ItemStackCreator; import net.swofty.type.generic.gui.v2.*; -import net.swofty.type.generic.gui.v2.context.ClickContext; import net.swofty.type.generic.gui.v2.context.ViewContext; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateFactoryHelper; import net.swofty.type.skyblockgeneric.chocolatefactory.ChocolateRabbit; import net.swofty.type.skyblockgeneric.data.datapoints.DatapointChocolateFactory; import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer; -import net.swofty.commons.StringUtility; - -import java.time.Duration; import java.util.ArrayList; import java.util.List; public class GUIChocolateFactory implements StatefulView { + private static final int RABBIT_BARN_MAX_LEVEL = 247; + private static final int RABBIT_BARN_EXTRA_CAPACITY = 2; + private static final int HAND_BAKED_MAX_LEVEL = 10; + private static final int TIME_TOWER_MAX_LEVEL = 15; + private static final int RABBIT_SHRINE_MAX_LEVEL = 20; + private static final int COACH_JACKRABBIT_MAX_LEVEL = 20; + private static final int EMPLOYEE_MAX_LEVEL = 220; + private static final int TOTAL_RABBITS = ChocolateRabbit.values().length; + private static final long MILLIS_PER_MINUTE = 60_000L; + private static final long MILLIS_PER_SECOND = 1_000L; + + private static final String NOT_ENOUGH_CHOCOLATE_MESSAGE = "§cYou don't have enough Chocolate!"; + // Texture IDs private static final String CHOCOLATE_TEXTURE = "9a815398e7da89b1bc08f646cafc8e7b813da0be0eec0cce6d3eff5207801026"; private static final String HOPPITY_TEXTURE = "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692"; @@ -132,14 +142,14 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { }); // Employee slots (28-34) - setupEmployeeSlots(layout, ctx); + setupEmployeeSlots(layout); // Slot 35: Rabbit Barn layout.slot(35, (s, c) -> createRabbitBarnItem((SkyBlockPlayer) c.player()), (click, c) -> { SkyBlockPlayer p = (SkyBlockPlayer) c.player(); DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); - if (d.getRabbitBarnLevel() >= 247) { + if (d.getRabbitBarnLevel() >= RABBIT_BARN_MAX_LEVEL) { p.sendMessage("§cYour Rabbit Barn is already at maximum capacity!"); p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); return; @@ -147,11 +157,10 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.RABBIT_BARN)) { d = ChocolateFactoryHelper.getData(p); - p.sendMessage("§7Your §aRabbit Barn §7capacity has been increased to §a" + (d.getMaxRabbitSlots() + 2) + " Rabbits§7!"); + p.sendMessage("§7Your §aRabbit Barn §7capacity has been increased to §a" + (d.getMaxRabbitSlots() + RABBIT_BARN_EXTRA_CAPACITY) + " Rabbits§7!"); p.playSound(UPGRADE_SOUND); } else { - p.sendMessage("§cYou don't have enough Chocolate!"); - p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + sendNotEnoughChocolateFeedback(p); } c.session(State.class).refresh(); }); @@ -161,7 +170,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { SkyBlockPlayer p = (SkyBlockPlayer) c.player(); DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); - if (d.getHandBakedChocolateLevel() >= 10) { + if (d.getHandBakedChocolateLevel() >= HAND_BAKED_MAX_LEVEL) { p.sendMessage("§cYou only have so many fingers! You can't click any faster!"); p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); return; @@ -172,8 +181,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { p.sendMessage("§7You will now produce §6+" + d.getClickPower() + " Chocolate §7per click!"); p.playSound(UPGRADE_SOUND); } else { - p.sendMessage("§cYou don't have enough Chocolate!"); - p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + sendNotEnoughChocolateFeedback(p); } c.session(State.class).refresh(); }); @@ -200,15 +208,14 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { } // Left-click to upgrade - if (d.getTimeTowerLevel() >= 15) { + if (d.getTimeTowerLevel() >= TIME_TOWER_MAX_LEVEL) { p.sendMessage("§cThe Time Tower is already at its maximum level!"); p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } else if (ChocolateFactoryHelper.purchaseUpgrade(p, ChocolateFactoryHelper.UpgradeType.TIME_TOWER)) { p.sendMessage("§7You upgraded to §dTime Tower " + StringUtility.getAsRomanNumeral(d.getTimeTowerLevel() + 1) + "§7!"); p.playSound(UPGRADE_SOUND); } else { - p.sendMessage("§cYou don't have enough Chocolate!"); - p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + sendNotEnoughChocolateFeedback(p); } c.session(State.class).refresh(); }); @@ -224,7 +231,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { return; } - if (d.getRabbitShrineLevel() >= 20) { + if (d.getRabbitShrineLevel() >= RABBIT_SHRINE_MAX_LEVEL) { p.sendMessage("§cYour Rabbit Shrine is already at its maximum level!"); p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); return; @@ -234,8 +241,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { p.sendMessage("§aUpgraded Rabbit Shrine!"); p.playSound(UPGRADE_SOUND); } else { - p.sendMessage("§cYou don't have enough Chocolate!"); - p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + sendNotEnoughChocolateFeedback(p); } c.session(State.class).refresh(); }); @@ -251,7 +257,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { return; } - if (d.getCoachJackrabbitLevel() >= 20) { + if (d.getCoachJackrabbitLevel() >= COACH_JACKRABBIT_MAX_LEVEL) { p.sendMessage("§cCoach Jackrabbit has already taught you all that he can teach!"); p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); return; @@ -261,8 +267,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { p.sendMessage("§aUpgraded Coach Jackrabbit!"); p.playSound(UPGRADE_SOUND); } else { - p.sendMessage("§cYou don't have enough Chocolate!"); - p.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + sendNotEnoughChocolateFeedback(p); } c.session(State.class).refresh(); }); @@ -279,8 +284,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { DatapointChocolateFactory.ChocolateFactoryData d = ChocolateFactoryHelper.getData(p); int rabbitsFound = d.getFoundRabbitCount(); - int totalRabbits = 512; - double percentage = (rabbitsFound / (double) totalRabbits) * 100; + double percentage = (rabbitsFound / (double) TOTAL_RABBITS) * 100; List lore = new ArrayList<>(); lore.add("§7Help §aHoppity §7find all of his §aChocolate"); @@ -292,7 +296,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { lore.add("§7§6Chocolate Factory §7will produce!"); lore.add(""); lore.add("§7Rabbits Found: §e" + String.format("%.1f", percentage) + "§6%"); - lore.add("§2§l§m §f§l§m §r §e" + rabbitsFound + "§6/§e" + totalRabbits); + lore.add("§2§l§m §f§l§m §r §e" + rabbitsFound + "§6/§e" + TOTAL_RABBITS); lore.add(""); lore.add("§eClick to view!"); @@ -334,7 +338,7 @@ public void layout(ViewLayout layout, State state, ViewContext ctx) { }, (click, c) -> ((SkyBlockPlayer) c.player()).openView(new GUIChocolateFactoryMilestones())); } - private void setupEmployeeSlots(ViewLayout layout, ViewContext ctx) { + private void setupEmployeeSlots(ViewLayout layout) { for (int i = 0; i < EMPLOYEE_SLOTS.length; i++) { int slot = EMPLOYEE_SLOTS[i]; String employeeName = EMPLOYEE_NAMES[i]; @@ -361,7 +365,7 @@ private void handleEmployeeClick(SkyBlockPlayer player, String employeeName) { DatapointChocolateFactory.EmployeeData existingEmployee = data.getEmployees().get(employeeName); - if (existingEmployee != null && existingEmployee.getLevel() >= 220) { + if (existingEmployee != null && existingEmployee.getLevel() >= EMPLOYEE_MAX_LEVEL) { player.sendMessage("§b" + employeeName + " §ccannot ascend the corporate ladder any further!"); player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); return; @@ -386,8 +390,7 @@ private void handleEmployeeClick(SkyBlockPlayer player, String employeeName) { player.sendMessage(rankColor + employeeName + " §7has been promoted to §7[" + level + "§7] " + rankColor + rank + "§7!"); player.playSound(UPGRADE_SOUND); } else { - player.sendMessage("§cYou don't have enough Chocolate!"); - player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); + sendNotEnoughChocolateFeedback(player); } } @@ -445,7 +448,7 @@ private ItemStack.Builder createEmployeeItem(SkyBlockPlayer player, String emplo lore.add("§7produce §6+" + String.format("%.0f", currentProduction) + " Chocolate §7per second!"); lore.add(""); - if (level >= 220) { + if (level >= EMPLOYEE_MAX_LEVEL) { lore.add("§7§" + rankColor + employeeName + " §ahas climbed as far as the"); lore.add("§acorporate ladder will allow!"); } else { @@ -481,8 +484,8 @@ private ItemStack.Builder createRabbitBarnItem(SkyBlockPlayer player) { lore.add("§7rabbits than your barn can hold,"); lore.add("§7they will be §ccrushed§7."); lore.add(""); - lore.add("§7Your Barn: §a" + data.getEmployeeCount() + "§7/§a" + (data.getMaxRabbitSlots() + 2) + " Rabbits"); - if (level >= 247) { + lore.add("§7Your Barn: §a" + data.getEmployeeCount() + "§7/§a" + (data.getMaxRabbitSlots() + RABBIT_BARN_EXTRA_CAPACITY) + " Rabbits"); + if (level >= RABBIT_BARN_MAX_LEVEL) { lore.add(""); lore.add("§aYour Rabbit Barn is at maximum capacity!"); } else { @@ -515,7 +518,7 @@ private ItemStack.Builder createHandBakedChocolateItem(SkyBlockPlayer player) { lore.add(""); lore.add("§7Chocolate Per Click: §6+" + data.getClickPower() + " Chocolate"); lore.add(""); - if (level >= 10) { + if (level >= HAND_BAKED_MAX_LEVEL) { lore.add("§aYou have reached the maximum"); lore.add("§aamount of upgrades!"); } else { @@ -555,8 +558,8 @@ private ItemStack.Builder createTimeTowerItem(SkyBlockPlayer player) { lore.add(""); if (isActive) { long remaining = data.getTimeTowerActiveUntil() - System.currentTimeMillis(); - long minutes = remaining / 60000; - long seconds = (remaining % 60000) / 1000; + long minutes = remaining / MILLIS_PER_MINUTE; + long seconds = (remaining % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND; lore.add("§7Status: §a§lACTIVE"); lore.add("§7Time Remaining: §a" + minutes + "m " + seconds + "s"); } else { @@ -565,7 +568,7 @@ private ItemStack.Builder createTimeTowerItem(SkyBlockPlayer player) { lore.add(""); lore.add("§7Charges: §a" + data.getTimeTowerCharges() + "§7/§a3"); lore.add(""); - if (level >= 15) { + if (level >= TIME_TOWER_MAX_LEVEL) { lore.add("§aThe Time Tower is maxed out!"); } else { lore.add("§8§m-----------------"); @@ -606,7 +609,7 @@ private ItemStack.Builder createRabbitShrineItem(SkyBlockPlayer player) { lore.add("§7higher rarity during §dHoppity's Hunt"); lore.add("§d§7by §a" + oddsBonus + "%§7."); lore.add(""); - if (level >= 20) { + if (level >= RABBIT_SHRINE_MAX_LEVEL) { lore.add("§aYour Rabbit Shrine is at its maximum"); lore.add("§alevel!"); } else { @@ -647,7 +650,7 @@ private ItemStack.Builder createCoachJackrabbitItem(SkyBlockPlayer player) { lore.add("§7your full potential by granting §6+" + String.format("%.2fx", multiplierBonus)); lore.add("§6Chocolate §7per second!"); lore.add(""); - if (level >= 20) { + if (level >= COACH_JACKRABBIT_MAX_LEVEL) { lore.add("§aCoach Jackrabbit has already taught"); lore.add("§ayou all that he can teach!"); } else { @@ -715,7 +718,7 @@ private ItemStack.Builder createProductionInfoItem(SkyBlockPlayer player) { } private String getEmployeeRank(int level) { - if (level >= 220) return "Board Member"; + if (level >= EMPLOYEE_MAX_LEVEL) return "Board Member"; if (level >= 200) return "Executive"; if (level >= 180) return "Director"; if (level >= 140) return "Manager"; @@ -726,7 +729,7 @@ private String getEmployeeRank(int level) { } private String getEmployeeRankColor(int level) { - if (level >= 220) return "b"; + if (level >= EMPLOYEE_MAX_LEVEL) return "b"; if (level >= 200) return "d"; if (level >= 180) return "6"; if (level >= 140) return "5"; @@ -736,10 +739,9 @@ private String getEmployeeRankColor(int level) { return "c"; } - /** - * Opens the Chocolate Factory GUI for a player with auto-refresh every second. - */ - public static void open(SkyBlockPlayer player) { - player.openView(new GUIChocolateFactory()).refreshEvery(Duration.ofSeconds(1)); + private void sendNotEnoughChocolateFeedback(SkyBlockPlayer player) { + player.sendMessage(NOT_ENOUGH_CHOCOLATE_MESSAGE); + player.playSound(NOT_ENOUGH_CHOCOLATE_SOUND); } + } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java index 317900418..73e24ffe3 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateFactoryMilestones.java @@ -15,6 +15,9 @@ import java.util.List; public class GUIChocolateFactoryMilestones implements StatefulView { + private static final int PROGRESS_BAR_SEGMENTS = 25; + private static final double PERCENT_PER_SEGMENT = 100.0 / PROGRESS_BAR_SEGMENTS; + private static final int[] MILESTONE_SLOTS = { 27, 18, 9, 0, 1, 2, 11, 20, 29, 30, 31, 22, 13, 4, 5, 6, 15, 24, 33, 34, 35, 26, 17, 8 @@ -69,21 +72,12 @@ private ItemStack.Builder createUnlockedMilestoneItem(ChocolateMilestone milesto lore.add("§7Milestone " + milestone.getRomanNumeral() + " Reward"); lore.add("§" + milestone.getColorCode() + milestone.getRabbitName()); lore.add(""); - - if (milestone.getChocolateBonus() > 0) { - lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + - String.format("%.3fx", milestone.getMultiplierBonus())); - lore.add("§6Chocolate §7per second to your"); - lore.add("§7§6Chocolate Factory§7."); - } else { - lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); - lore.add("§7to your §6Chocolate Factory§7."); - } + addRewardLore(lore, milestone); lore.add(""); lore.add("§a§lUNLOCKED"); return ItemStackCreator.getStackHead( - "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Chocolate Milestone", + getMilestoneName(milestone), milestone.getTextureId(), 1, lore @@ -105,21 +99,12 @@ private ItemStack.Builder createLockedMilestoneItem(ChocolateMilestone milestone lore.add("§7Milestone " + milestone.getRomanNumeral() + " Reward"); lore.add("§" + milestone.getColorCode() + milestone.getRabbitName()); lore.add(""); - - if (milestone.getChocolateBonus() > 0) { - lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + - String.format("%.3fx", milestone.getMultiplierBonus())); - lore.add("§6Chocolate §7per second to your"); - lore.add("§7§6Chocolate Factory§7."); - } else { - lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); - lore.add("§7to your §6Chocolate Factory§7."); - } + addRewardLore(lore, milestone); lore.add(""); lore.add("§cRequires " + formattedReq + " all-time Chocolate!"); return ItemStackCreator.getStack( - "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Chocolate Milestone", + getMilestoneName(milestone), milestone.getGlassPaneMaterial(), 1, lore @@ -127,8 +112,8 @@ private ItemStack.Builder createLockedMilestoneItem(ChocolateMilestone milestone } private String createProgressBar(double progress) { - int filled = (int) (progress / 4); - int empty = 25 - filled; + int filled = (int) (progress / PERCENT_PER_SEGMENT); + int empty = PROGRESS_BAR_SEGMENTS - filled; StringBuilder bar = new StringBuilder("§3§l§m"); for (int i = 0; i < filled; i++) { @@ -167,4 +152,21 @@ private String getOrdinal(int number) { } return number + suffixes[number % 10]; } + + private String getMilestoneName(ChocolateMilestone milestone) { + return "§" + milestone.getColorCode() + getOrdinal(milestone.getNumber()) + " Chocolate Milestone"; + } + + private void addRewardLore(List lore, ChocolateMilestone milestone) { + if (milestone.getChocolateBonus() > 0) { + lore.add("§7Grants §6+" + milestone.getChocolateBonus() + " Chocolate §7and §6" + + String.format("%.3fx", milestone.getMultiplierBonus())); + lore.add("§6Chocolate §7per second to your"); + lore.add("§7§6Chocolate Factory§7."); + return; + } + + lore.add("§7Grants §6+" + String.format("%.2fx", milestone.getMultiplierBonus()) + " Chocolate §7per second"); + lore.add("§7to your §6Chocolate Factory§7."); + } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java index 31a6da94f..ef91160b5 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIChocolateShop.java @@ -548,7 +548,4 @@ private void handlePurchase(SkyBlockPlayer player, long cost, String itemName, i } } - public static void open(SkyBlockPlayer player) { - player.openView(new GUIChocolateShop()); - } } diff --git a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java index b916d4c84..16ef917eb 100644 --- a/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java +++ b/type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/gui/inventories/GUIHoppityCollection.java @@ -19,6 +19,7 @@ public class GUIHoppityCollection implements StatefulView { private static final String HOPPITY_TEXTURE = "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692"; private static final String LOCATION_TEXTURE = "d7cc6687423d0570d556ac53e0676cb563bbdd9717cd8269bdebed6f6d4e7bf8"; + private static final String FOUND_RABBIT_TEXTURE = HOPPITY_TEXTURE; private static final int[] RABBIT_SLOTS = { 10, 11, 12, 13, 14, 15, 16, @@ -28,7 +29,9 @@ public class GUIHoppityCollection implements StatefulView layout, State state, ViewContext ctx) { if (rabbitIndex < rabbits.size()) { ChocolateRabbit rabbit = rabbits.get(rabbitIndex); layout.slot(slot, (s, c) -> { - SkyBlockPlayer p = (SkyBlockPlayer) c.player(); - boolean found = ChocolateFactoryHelper.getData(p).getFoundRabbits().contains(rabbit.name()); + boolean found = foundRabbits.contains(rabbit.name()); return createRabbitItem(rabbit, found); }); } else { @@ -267,7 +269,7 @@ private ItemStack.Builder createRabbitItem(ChocolateRabbit rabbit, boolean found if (found) { return ItemStackCreator.getStackHead(rabbit.getFormattedName(), - "b79e7f3341b672d9de6564cbaca052a6a723ea466a2e66af35ba1ba855f0d692", 1, lore); + FOUND_RABBIT_TEXTURE, 1, lore); } else { return ItemStackCreator.getStack(rabbit.getFormattedName(), Material.GRAY_DYE, 1, lore); } @@ -298,8 +300,8 @@ private List getFilteredAndSortedRabbits(Set foundRabbi } private String createProgressBar(double progress) { - int filled = (int) (progress / 4); - int empty = 25 - filled; + int filled = (int) (progress / PERCENT_PER_PROGRESS_SEGMENT); + int empty = PROGRESS_BAR_SEGMENTS - filled; StringBuilder bar = new StringBuilder("§2§l§m"); for (int i = 0; i < filled; i++) { @@ -328,13 +330,13 @@ public enum SortType { } public SortType next() { - SortType[] values = values(); - return values[(ordinal() + 1) % values.length]; + SortType[] all = values(); + return all[(ordinal() + 1) % all.length]; } public SortType previous() { - SortType[] values = values(); - return values[(ordinal() - 1 + values.length) % values.length]; + SortType[] all = values(); + return all[(ordinal() - 1 + all.length) % all.length]; } } @@ -353,13 +355,13 @@ public enum FilterType { } public FilterType next() { - FilterType[] values = values(); - return values[(ordinal() + 1) % values.length]; + FilterType[] all = values(); + return all[(ordinal() + 1) % all.length]; } public FilterType previous() { - FilterType[] values = values(); - return values[(ordinal() - 1 + values.length) % values.length]; + FilterType[] all = values(); + return all[(ordinal() - 1 + all.length) % all.length]; } } }