diff --git a/api/src/main/java/com/alttd/cometskyblock/api/challenges/ChallengeDifficulty.java b/api/src/main/java/com/alttd/cometskyblock/api/challenges/ChallengeDifficulty.java new file mode 100644 index 0000000..c2345df --- /dev/null +++ b/api/src/main/java/com/alttd/cometskyblock/api/challenges/ChallengeDifficulty.java @@ -0,0 +1,9 @@ +package com.alttd.cometskyblock.api.challenges; + +public enum ChallengeDifficulty { + EASY, + MEDIUM, + HARD, + LEGENDARY, + EXOTIC +} diff --git a/api/src/main/java/com/alttd/cometskyblock/api/challenges/ChallengeType.java b/api/src/main/java/com/alttd/cometskyblock/api/challenges/ChallengeType.java new file mode 100644 index 0000000..c60fdeb --- /dev/null +++ b/api/src/main/java/com/alttd/cometskyblock/api/challenges/ChallengeType.java @@ -0,0 +1,28 @@ +package com.alttd.cometskyblock.api.challenges; + +public enum ChallengeType { + /** + * Player must have the items to turn in + */ + ON_PLAYER, + /** + * Requirements must be on the island + */ + ON_ISLAND, + /** + * Island level requirement + */ + ISLAND_LEVEL; + + public static ChallengeType of(String challengeType) { + if (challengeType == null || challengeType.isEmpty()) + return null; + return switch (challengeType.toUpperCase()) { + case "ON_PLAYER" -> ChallengeType.ON_PLAYER; + case "ON_ISLAND" -> ChallengeType.ON_ISLAND; + case "ISLAND_LEVEL" -> ChallengeType.ISLAND_LEVEL; + default -> null; + }; + } + +} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index a318b45..cd74e45 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -9,12 +9,19 @@ dependencies { compileOnly("org.projectlombok:lombok:1.18.24") annotationProcessor("org.projectlombok:lombok:1.18.24") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") } tasks { jar { archiveFileName.set("${rootProject.name}-${project.name}.jar") } + + test { + useJUnitPlatform() + } } bukkit { @@ -32,12 +39,12 @@ bukkit { permission = "${rootProject.name}.command.island" } - register("Challenges") { + register("challenges") { description = "Opens the challenges menu." permission = "${rootProject.name}.command.island" } - register("cometskyblock") { + register("skyblock") { description = "${rootProject.name} admin command." permission = "${rootProject.name}.command.admin" } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java b/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java index 98b4b2b..aaf75f4 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java @@ -1,5 +1,8 @@ package com.alttd.cometskyblock; +import com.alttd.cometskyblock.challenges.ChallengeHandler; +import com.alttd.cometskyblock.challenges.ChallengeLoader; +import com.alttd.cometskyblock.commands.admin.SkyBlockCommand; import com.alttd.cometskyblock.commands.challenges.ChallengeCommand; import com.alttd.cometskyblock.commands.island.IslandCommand; import com.alttd.cometskyblock.configuration.*; @@ -25,12 +28,13 @@ public class CometSkyBlockPlugin extends JavaPlugin implements CometSkyBlockAPI @Getter private ConfigurationContainer pluginConfiguration; @Getter private ConfigurationContainer databaseConfiguration; @Getter private ConfigurationContainer messagesConfiguration; - @Getter private ConfigurationContainer challengesConfiguration; @Getter private ConfigurationContainer cobblestoneGeneratorConfiguration; @Getter private ConfigurationContainer worldBorderConfiguration; @Getter private IslandManager islandManager; @Getter private MasterWorldGenerator worldGenerator; + @Getter private ChallengeHandler challengeHandler; + @Getter private ChallengeLoader challengeLoader; @Override public void onLoad() { @@ -60,6 +64,8 @@ public class CometSkyBlockPlugin extends JavaPlugin implements CometSkyBlockAPI // load worlds & manager islandManager = new IslandManager(this); worldGenerator = new MasterWorldGenerator(this); + challengeHandler = new ChallengeHandler(this); + loadChallenges(); worldGenerator.checkMasterIslandWorld(); } @@ -72,13 +78,18 @@ public class CometSkyBlockPlugin extends JavaPlugin implements CometSkyBlockAPI // close data connection } + public void loadChallenges() { + challengeHandler.clearChallenges(); + challengeLoader = new ChallengeLoader(this); + challengeLoader.loadAllChallenges(); + } + public void loadConfiguration() { Path path = this.getDataFolder().toPath(); Logger logger = this.getSLF4JLogger(); pluginConfiguration = ConfigurationContainer.load(logger, path, PluginConfiguration.class, "config"); databaseConfiguration = ConfigurationContainer.load(logger, path, DatabaseConfiguration.class, "database"); messagesConfiguration = ConfigurationContainer.load(logger, path, MessageConfiguration.class, "messages"); - challengesConfiguration = ConfigurationContainer.load(logger, path, ChallengesConfiguration.class, "challenges"); cobblestoneGeneratorConfiguration = ConfigurationContainer.load(logger, path, CobblestoneGeneratorConfiguration.class, "coblestonegenerator"); worldBorderConfiguration = ConfigurationContainer.load(logger, path, WorldBorderConfiguration.class, "worldborder"); } @@ -86,7 +97,7 @@ public class CometSkyBlockPlugin extends JavaPlugin implements CometSkyBlockAPI public void loadCommands() { getCommand("island").setExecutor(new IslandCommand(this)); getCommand("challenges").setExecutor(new ChallengeCommand(this)); -// getCommand("cometskyblock").setExecutor( new AdminCommands(this)); + getCommand("skyblock").setExecutor( new SkyBlockCommand(this)); } public void loadEventListeners() { diff --git a/plugin/src/main/java/com/alttd/cometskyblock/challenges/Challenge.java b/plugin/src/main/java/com/alttd/cometskyblock/challenges/Challenge.java index dc34678..a7ec41d 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/challenges/Challenge.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/challenges/Challenge.java @@ -1,29 +1,30 @@ package com.alttd.cometskyblock.challenges; +import com.alttd.cometskyblock.api.challenges.ChallengeDifficulty; +import com.alttd.cometskyblock.api.challenges.ChallengeType; import lombok.Getter; +import lombok.Setter; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; -import org.spongepowered.configurate.objectmapping.ConfigSerializable; import java.util.List; -@Getter -@ConfigSerializable +@Getter @Setter public class Challenge { private String challengeName; - private String mySQLKey; + private String key; private Material shownItem; private ChallengeDifficulty difficulty; private ChallengeType challengeType; private String description; - private String neededText; + private String requiredText; private String rewardText; private String repeatRewardText; private List rewards; private List repeatRewards; - private List neededItems; - private Integer neededLevel; - private Integer slot; + private List requiredItems; + private Integer requiredLevel; + private Integer radius; } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeDifficulty.java b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeDifficulty.java deleted file mode 100644 index ff6cba7..0000000 --- a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeDifficulty.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.alttd.cometskyblock.challenges; - -public class ChallengeDifficulty { -} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeHandler.java b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeHandler.java new file mode 100644 index 0000000..c141927 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeHandler.java @@ -0,0 +1,59 @@ +package com.alttd.cometskyblock.challenges; + +import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.api.challenges.ChallengeDifficulty; + +import java.util.HashMap; +import java.util.Map; + +public class ChallengeHandler { + + private final Map easyChallenges = new HashMap<>(); + private final Map mediumChallenges = new HashMap<>(); + private final Map hardChallenges = new HashMap<>(); + private final Map impossibleChallenges = new HashMap<>(); + private final Map exoticChallenges = new HashMap<>(); + + private final CometSkyBlockPlugin plugin; + + public ChallengeHandler(CometSkyBlockPlugin plugin) { + this.plugin = plugin; + } + + public void addChallenge(Challenge challenge) { + switch (challenge.difficulty()) { + case EASY -> easyChallenges.put(challenge.key(), challenge); + case MEDIUM -> mediumChallenges.put(challenge.key(), challenge); + case HARD -> hardChallenges.put(challenge.key(), challenge); + case LEGENDARY -> impossibleChallenges.put(challenge.key(), challenge); + case EXOTIC -> exoticChallenges.put(challenge.key(), challenge); + } + } + + public void clearChallenges() { + for (ChallengeDifficulty difficulty : ChallengeDifficulty.values()) { + getChallenges(difficulty).clear(); + } + } + + public Map getChallenges(ChallengeDifficulty challengeDifficulty) { + switch (challengeDifficulty) { + case MEDIUM -> { + return mediumChallenges; + } + case HARD -> { + return hardChallenges; + } + case LEGENDARY -> { + return impossibleChallenges; + } + case EXOTIC -> { + return exoticChallenges; + } + default -> { + return easyChallenges; + } + } + } + +} \ No newline at end of file diff --git a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeLoader.java b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeLoader.java new file mode 100644 index 0000000..69ab722 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeLoader.java @@ -0,0 +1,212 @@ +package com.alttd.cometskyblock.challenges; + +import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.api.challenges.ChallengeDifficulty; +import com.alttd.cometskyblock.api.challenges.ChallengeType; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Data loader for challenges + * Parse the data and build the challenges to be used by the plugin. + */ +public class ChallengeLoader extends YamlConfiguration { + + private final CometSkyBlockPlugin plugin; + private final File file; + private final Object saveLock = new Object(); + private final Object2IntMap difficultyLevels = new Object2IntOpenHashMap<>(); + + public ChallengeLoader(CometSkyBlockPlugin plugin) { + super(); + this.plugin = plugin; + this.file = new File(plugin.getDataFolder(), "challenges.yml"); + difficultyLevels.defaultReturnValue(0); + reload(); + } + + private void reload() { + synchronized (saveLock) { + try { + load(file); + } catch (Exception ignore) { + } + } + } + + // Todo - do we continue loading or end the loading procedure? + public void loadAllChallenges() { + for (ChallengeDifficulty difficulty : ChallengeDifficulty.values()) { + if (!loadChallenges(difficulty)) { + plugin.getLogger().warning(String.format("could not load %s challenges", difficulty.toString())); + } + loadDifficultyLevel(difficulty); + } + } + + private void loadDifficultyLevel(ChallengeDifficulty challengeDifficulty) { + if (challengeDifficulty == null) { + plugin.getLogger().warning("Could not load challenges!"); + return; + } + String difficulty = challengeDifficulty.toString().toLowerCase(); + int level = getInt("level." + difficulty, 0); + if (level > 0) + difficultyLevels.put(challengeDifficulty, level); + } + + private boolean loadChallenges(ChallengeDifficulty challengeDifficulty) { + if (challengeDifficulty == null) { + plugin.getLogger().warning("Could not load challenges!"); + return false; + } + String difficulty = challengeDifficulty.toString().toLowerCase(); + ConfigurationSection section = getConfigurationSection(difficulty); + if (section == null) { + plugin.getLogger().warning(String.format("could not load challenges for difficulty %s", difficulty)); + return false; + } + Set challenges = section.getKeys(false); + for (String currentChallenge : challenges) { + String challengeName = section.getString(currentChallenge + ".name"); + String key = section.getString(currentChallenge + ".key"); + String shownItem = section.getString(currentChallenge + ".shown-item"); + if (shownItem == null || shownItem.isEmpty() || isNotMaterial(shownItem)) { + plugin.getLogger().warning(String.format("could not load challenge %s material data", currentChallenge)); + continue; + } + Material shownItemMaterial = Material.getMaterial(shownItem.toUpperCase()); + ChallengeType challengeType = ChallengeType.of(section.getString(currentChallenge + ".challenge-type")); + String description = section.getString(currentChallenge + ".description"); + String requiredText = section.getString(currentChallenge + ".required-text"); + String rewardText = section.getString(currentChallenge + ".reward-text"); + String repeatRewardText = section.getString(currentChallenge + ".repeat-reward-text"); + List rewards = section.getStringList(currentChallenge + ".reward-items"); + List repeatRewards = section.getStringList(currentChallenge + ".repeat-reward-items"); + List requiredItems = section.getStringList(currentChallenge + ".required-items"); + Integer requiredLevel = section.getInt(currentChallenge + ".required-level", 0); + Integer radius = section.getInt(currentChallenge + ".radius", 50); + // Build the challenge + Challenge challenge = new Challenge() + .challengeName(challengeName) + .difficulty(challengeDifficulty) + .key(key) + .shownItem(shownItemMaterial) + .description(description) + .requiredText(requiredText) + .rewardText(rewardText) + .repeatRewardText(repeatRewardText) + .requiredLevel(requiredLevel) + .challengeType(challengeType) + .rewards(createItems(rewards)) + .repeatRewards(createItems(repeatRewards)) + .requiredItems(createItems(requiredItems)) + .radius(radius); + if (!validateChallenge(challenge)) { + plugin.getLogger().warning(String.format("Invalid %s challenge: %s", difficulty, currentChallenge)); + continue; + } + plugin.challengeHandler().addChallenge(challenge); + } + return true; + } + + private boolean isNotMaterial(String material) { + return Material.matchMaterial(material.toUpperCase()) == null; + } + + private List createItems(List items) { + List itemStacks = new ArrayList<>(); + for (String currentItem : items) { + String material = currentItem; + int amount = 1; + if (currentItem.contains(";")) { + String[] strings = currentItem.split(";"); + if (strings.length != 2) { + plugin.getLogger().warning(String.format("Could not make itemstack for item %s, invalid separator", currentItem)); + continue; + } + if (!isNumber(strings[1])) { + plugin.getLogger().warning(String.format("Could not make itemstack for item %s, %s is not an int", currentItem, strings[1])); + continue; + } + material = strings[0]; + amount = Integer.parseInt(strings[1]); + } + ItemStack itemStack = makeItemStack(material, amount); + if (itemStack == null) { + plugin.getLogger().warning(String.format("Could not make itemstack for item %s", currentItem)); + continue; + } + itemStacks.add(itemStack); + } + return itemStacks; + } + + private ItemStack makeItemStack(String materialString, int amount) { + if (materialString == null || materialString.isEmpty() || isNotMaterial(materialString)) { + return null; + } + Material material = Material.getMaterial(materialString.toUpperCase()); + if (material == null) { + return null; + } + return new ItemStack(material, amount); + } + + private boolean validateChallenge(Challenge challenge) { + if (challenge.challengeName() != null && + challenge.key() != null && + challenge.challengeType() != null && + challenge.description() != null && + challenge.difficulty() != null && + challenge.shownItem() != null && + challenge.requiredText() != null && + challenge.rewardText() != null && + challenge.rewards() != null && + !challenge.rewards().isEmpty() + ) { + switch (challenge.challengeType()) { + case ISLAND_LEVEL -> { + if (challenge.requiredLevel() != null) + return true; + } + case ON_ISLAND -> { + if (challenge.requiredItems() != null && !challenge.requiredItems().isEmpty() && challenge.radius() != null) + return true; + + } + case ON_PLAYER -> { + if (challenge.requiredItems() != null && !challenge.requiredItems().isEmpty() && challenge.repeatRewards() != null && !challenge.repeatRewards().isEmpty() && challenge.repeatRewardText() != null) + return true; + } + default -> { + return false; + } + } + } + return false; + } + + public int requiredLevel(ChallengeDifficulty challengeDifficulty) { + return difficultyLevels.getInt(challengeDifficulty); + } + + private boolean isNumber(String s) { + try { + Integer.parseInt(s); + } catch (NumberFormatException nfe) { + return false; + } + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeType.java b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeType.java deleted file mode 100644 index 12687a4..0000000 --- a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengeType.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alttd.cometskyblock.challenges; - -import org.spongepowered.configurate.objectmapping.ConfigSerializable; - -@ConfigSerializable -public enum ChallengeType { - - onPlayer, - onIsland, - islandLevel - -} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengesGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengesGUI.java index 35b0dbf..b83450c 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengesGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/challenges/ChallengesGUI.java @@ -1,27 +1,186 @@ package com.alttd.cometskyblock.challenges; +import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.api.challenges.ChallengeDifficulty; +import com.alttd.cometskyblock.api.challenges.ChallengeType; +import com.alttd.cometskyblock.gui.GUIButton; import com.alttd.cometskyblock.gui.GUIInventory; import com.alttd.cometskyblock.island.Island; +import com.alttd.cometskyblock.util.PlayerUtils; +import com.alttd.cometskyblock.util.StringUtil; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; public class ChallengesGUI extends GUIInventory { + protected ChallengeDifficulty challengeDifficulty = ChallengeDifficulty.EASY; + public ChallengesGUI(Island island) { super(island); } @Override protected Inventory createInventory() { // TODO - config - return Bukkit.createInventory(this, 54, MiniMessage.miniMessage().deserialize("GUI")); + return Bukkit.createInventory(this, 36, MiniMessage.miniMessage().deserialize("Island - challenges menu")); } @Override public void decorate(Player player) { + resetButtons(); makeMenuBar(player); + // Add buttons to change difficulty + if (!challengeDifficulty.equals(ChallengeDifficulty.EASY)) + addButton(getInventory().getSize() - 5, createMenuButton(Material.WHITE_BANNER, "Easy Challenges", new ArrayList<>(), event -> { + changeDifficulty(player, ChallengeDifficulty.EASY); + })); + if (!challengeDifficulty.equals(ChallengeDifficulty.MEDIUM)) + addButton(getInventory().getSize() - 4, createMenuButton(Material.GREEN_BANNER, "Medium Challenges", new ArrayList<>(), event -> { + changeDifficulty(player, ChallengeDifficulty.MEDIUM); + })); + if (!challengeDifficulty.equals(ChallengeDifficulty.HARD)) + addButton(getInventory().getSize() - 3, createMenuButton(Material.BLUE_BANNER, "Hard Challenges", new ArrayList<>(), event -> { + changeDifficulty(player, ChallengeDifficulty.HARD); + })); + if (!challengeDifficulty.equals(ChallengeDifficulty.LEGENDARY)) + addButton(getInventory().getSize() - 2, createMenuButton(Material.PURPLE_BANNER, "Legendary Challenges", new ArrayList<>(), event -> { + changeDifficulty(player, ChallengeDifficulty.LEGENDARY); + })); + if (!challengeDifficulty.equals(ChallengeDifficulty.EXOTIC)) + addButton(getInventory().getSize() - 1, createMenuButton(Material.YELLOW_BANNER, "Exotic Challenges", new ArrayList<>(), event -> { + changeDifficulty(player, ChallengeDifficulty.EXOTIC); + })); + + currentSlot = 0; + int startIndex = pageIndex * (getInventory().getSize() - 9); + List challenges = CometSkyBlockPlugin.instance().challengeHandler().getChallenges(challengeDifficulty).values().stream().toList(); + for (int i = startIndex; i < challenges.size(); i++) { + Challenge challenge = challenges.get(i); + int challengeCount = island.challengeCount(challenge.key()); + GUIButton guiButton = createChallengeButton(challenge, event -> { + // TODO -- Clean up + boolean repeat = challengeCount != 0; + List rewards = repeat ? challenge.repeatRewards() : challenge.rewards(); + boolean hasRequirements = false; + boolean takeItems = false; + switch (challenge.challengeType()) { + case ON_PLAYER -> { + hasRequirements = PlayerUtils.hasItems(player.getInventory(), challenge.requiredItems()); + takeItems = true; + } + case ON_ISLAND -> { + if (!player.getWorld().getUID().equals(island.islandUUID())) { + player.sendRichMessage(CometSkyBlockPlugin.instance().messagesConfiguration().get().island().notOnIsland()); + return; + } + Object2IntMap blocks = PlayerUtils.getBlocks(player, challenge.radius()); + for (ItemStack itemStack : challenge.requiredItems()) { + if (blocks.getInt(itemStack.getType()) < itemStack.getAmount()) { + hasRequirements = false; + break; + } + + hasRequirements = true; + } + } + case ISLAND_LEVEL -> { + hasRequirements = island.level() >= challenge.requiredLevel(); + if (!hasRequirements) { + player.sendRichMessage("You do no have the required island level."); + return; + } + } + default -> { + return; + } + } + if (!hasRequirements) { + player.sendRichMessage("You do not have all the required items."); + return; + } + if (!PlayerUtils.hasRoom(player.getInventory(), rewards)) { + player.sendRichMessage("You do not have room for the reward items."); + return; + } + island.challengeCount(challenge.key(), challengeCount + 1); + TagResolver placeholders = TagResolver.resolver( + Placeholder.component("player", player.displayName()), + Placeholder.unparsed("challengename", challenge.challengeName()) + ); + island.broadCast(" has completed challenge .", placeholders); + PlayerUtils.giveItems(player.getInventory(), rewards); + if (takeItems) { + PlayerUtils.takeItems(player.getInventory(), challenge.requiredItems()); + } + this.decorate(player); + }, challengeCount); + if (!addItem(guiButton)) { + createNextPageButton(player); + break; + } + } + if (pageIndex > 0) { + createPrevPageButton(player); + } super.decorate(player); } + protected void changeDifficulty(Player player, ChallengeDifficulty challengeDifficulty) { + int requiredLevel = CometSkyBlockPlugin.instance().challengeLoader().requiredLevel(challengeDifficulty); + if (!(island.level() >= requiredLevel)) { + TagResolver placeholders = TagResolver.resolver( + Placeholder.unparsed("requiredlevel", requiredLevel + ""), + Placeholder.unparsed("difficulty", challengeDifficulty.toString().toLowerCase()) + ); + player.sendRichMessage("You need island level to access challenges", placeholders); + return; + } + this.challengeDifficulty = challengeDifficulty; + this.decorate(player); + } + + private GUIButton createChallengeButton(Challenge challenge, Consumer eventConsumer, int challengeCount) { + return new GUIButton() + .creator(player -> { + ItemStack itemStack = new ItemStack(challenge.shownItem(), Math.min(Math.max(1, challengeCount), 64)); + itemStack.editMeta(meta -> { + MiniMessage miniMessage = MiniMessage.miniMessage(); + meta.displayName(miniMessage.deserialize(challenge.challengeName())); + List lore = new ArrayList<>(StringUtil.splitMiniMessageString(challenge.description()).stream().map(miniMessage::deserialize).toList()); + String prefix = switch (challenge.challengeType()) { + case ON_PLAYER -> "Required items:"; + case ON_ISLAND -> "Build:"; + case ISLAND_LEVEL -> "Requires"; + }; + lore.addAll(StringUtil.splitMiniMessageString(prefix + " " + challenge.requiredText()).stream().map(miniMessage::deserialize).toList()); + lore.add(miniMessage.deserialize("Reward: " + StringUtil.splitMiniMessageString( + (challenge.challengeType().equals(ChallengeType.ON_PLAYER) && challengeCount != 0) ? + challenge.repeatRewardText() : challenge.rewardText()))); + // TODO -- Add glow for challenges that are completed +// if (challengeCount > 0 || challenge.repeatRewards() != null && !challenge.repeatRewards().isEmpty()) { +// meta.addEnchant(Enchantment.MENDING, 1, true); +// } + meta.addItemFlags(ItemFlag.values()); + + meta.lore(lore); + }); + return itemStack; + }) + .consumer(eventConsumer); + } } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/commands/admin/ReloadCommand.java b/plugin/src/main/java/com/alttd/cometskyblock/commands/admin/ReloadCommand.java new file mode 100644 index 0000000..06314f8 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/commands/admin/ReloadCommand.java @@ -0,0 +1,30 @@ +package com.alttd.cometskyblock.commands.admin; + +import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.commands.SubCommand; +import org.bukkit.command.CommandSender; + +public class ReloadCommand extends SubCommand { + + public ReloadCommand(CometSkyBlockPlugin plugin) { + super(plugin, "reload"); + } + + @Override + public boolean execute(CommandSender sender, String... args) { + if (args.length < 1) { + sender.sendRichMessage("Wrong usage try /skyblock reload config|challenges|all"); + return true; + } + switch (args[0].toLowerCase()) { + case "all" -> { + plugin.loadConfiguration(); + plugin.loadChallenges(); + } + case "config" -> plugin.loadConfiguration(); + case "challenges" -> plugin.loadChallenges(); + default -> sender.sendRichMessage("Wrong usage try /skyblock reload config|challenges|all"); + } + return true; + } +} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/commands/admin/SkyBlockCommand.java b/plugin/src/main/java/com/alttd/cometskyblock/commands/admin/SkyBlockCommand.java new file mode 100644 index 0000000..6097d75 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/commands/admin/SkyBlockCommand.java @@ -0,0 +1,24 @@ +package com.alttd.cometskyblock.commands.admin; + +import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.commands.SubCommand; +import org.bukkit.command.CommandSender; + +public class SkyBlockCommand extends SubCommand { + + private final CometSkyBlockPlugin plugin; + + public SkyBlockCommand(CometSkyBlockPlugin plugin) { + super(plugin, "skyblock"); + this.plugin = plugin; + + registerSubCommand(new ReloadCommand(plugin)); + } + + @Override + public boolean execute(CommandSender sender, String... args) { + sender.sendRichMessage("Wrong usage try /skyblock reload config|challenges"); + return true; + } + +} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/commands/challenges/ChallengeCommand.java b/plugin/src/main/java/com/alttd/cometskyblock/commands/challenges/ChallengeCommand.java index 478b182..6120996 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/commands/challenges/ChallengeCommand.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/commands/challenges/ChallengeCommand.java @@ -1,7 +1,9 @@ package com.alttd.cometskyblock.commands.challenges; import com.alttd.cometskyblock.CometSkyBlockPlugin; +import com.alttd.cometskyblock.challenges.ChallengesGUI; import com.alttd.cometskyblock.commands.PlayerSubCommand; +import com.alttd.cometskyblock.island.Island; import com.alttd.cometskyblock.island.IslandPlayer; import org.bukkit.entity.Player; @@ -13,11 +15,19 @@ public class ChallengeCommand extends PlayerSubCommand { super(plugin, "challenge"); this.plugin = plugin; } - // TODO - Challenges, ChallengesGUI and ChallengesCommand + @Override public boolean execute(Player player, IslandPlayer islandPlayer, String[] args) { - // open challenge inventory - not implemented yet -- TODO - player.sendRichMessage("Not implemented yet, please wait for a future update."); + if (islandPlayer.islandId() == 0) { + player.sendRichMessage("You should create an island before doing this. Do /island go"); + return true; + } + Island island = Island.getIsland(islandPlayer.islandUUID()); + if (island == null) { + player.sendRichMessage("Could not load your island. Contact an administrator"); + return true; + } + new ChallengesGUI(island).open(player); return true; } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/configuration/ChallengesConfiguration.java b/plugin/src/main/java/com/alttd/cometskyblock/configuration/ChallengesConfiguration.java deleted file mode 100644 index 4343ebe..0000000 --- a/plugin/src/main/java/com/alttd/cometskyblock/configuration/ChallengesConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.alttd.cometskyblock.configuration; - -import com.alttd.cometskyblock.challenges.Challenge; -import lombok.Getter; -import org.spongepowered.configurate.objectmapping.ConfigSerializable; - -import java.util.ArrayList; -import java.util.List; - -@ConfigSerializable -@Getter -@SuppressWarnings({"CanBeFinal", "FieldMayBeFinal"}) -public class ChallengesConfiguration implements Configuration { - - private List challenges = new ArrayList<>(); -} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java b/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java index d4cb94d..dd2ce2e 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/gui/GUIInventory.java @@ -65,11 +65,16 @@ public abstract class GUIInventory implements GUI, InventoryHolder { } + public void resetButtons() { + this.buttons.clear(); + } + public void addButton(int slot, GUIButton button) { this.buttons.put(slot, button); } protected void decorate(Player player) { + this.inventory.clear(); this.buttons.forEach((slot, button) -> { ItemStack icon = button.iconCreator().apply(player); this.inventory.setItem(slot, icon); @@ -99,11 +104,16 @@ public abstract class GUIInventory implements GUI, InventoryHolder { } protected void makeMenuBar(Player player) { + makeMenuBar(player, false); + } + + protected void makeMenuBar(Player player, boolean makeTopBar) { for (int i = inventory.getSize() - 9; i < inventory.getSize(); ++i) { addButton(i, createMenuButton(Material.BLACK_STAINED_GLASS_PANE, "", new ArrayList<>(), event -> {})); } createMainMenuButton(player, inventory.getSize() - 9); - makeTopBar(); + if (makeTopBar) + makeTopBar(); } protected void makeTopBar() { @@ -129,14 +139,14 @@ public abstract class GUIInventory implements GUI, InventoryHolder { } protected void createPrevPageButton(Player player) { - addButton(48, createMenuButton(Material.PAPER, "<< Previous Page", new ArrayList<>(), event -> { + addButton(inventory.getSize() - 8, createMenuButton(Material.PAPER, "<< Previous Page", new ArrayList<>(), event -> { --pageIndex; decorate(player); })); } protected void createNextPageButton(Player player) { - addButton(50, createMenuButton(Material.PAPER, "Next Page >>", new ArrayList<>(), event -> { + addButton(inventory.getSize() - 7, createMenuButton(Material.PAPER, "Next Page >>", new ArrayList<>(), event -> { ++pageIndex; decorate(player); })); diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java b/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java index 77213f9..1cd42aa 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java @@ -251,4 +251,14 @@ public class Island extends YamlConfiguration { public IslandRecord toRecord() { return new IslandRecord(islandId(), islandName(), level()); } + + + public int challengeCount(String key) { + return getInt("challenges." + key, 0); + } + + public void challengeCount(String key, int amount) { + set("challenges." + key, amount); + save(); + } } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/IslandGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/IslandGUI.java index 9b92a47..d7e6c7e 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/IslandGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/IslandGUI.java @@ -24,7 +24,7 @@ public class IslandGUI extends GUIInventory { @Override public void decorate(Player player) { - makeMenuBar(player); + makeMenuBar(player, true); // Island GO addButton(10, createMenuButton(Material.GRASS_BLOCK, "Visit your island!", new ArrayList<>(), event -> { diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java index 6544c66..63b80df 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/MembersGUI.java @@ -33,7 +33,7 @@ public class MembersGUI extends GUIInventory { @Override public void decorate(Player player) { currentSlot = 9; - makeMenuBar(player); + makeMenuBar(player, true); int startIndex = pageIndex * 45; for (int i = startIndex; i < island.members().size(); i++) { diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java index 87f9527..6394dec 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/SettingsGUI.java @@ -27,7 +27,7 @@ public class SettingsGUI extends GUIInventory { @Override public void decorate(Player player) { - makeMenuBar(player); + makeMenuBar(player, true); // setHome addButton(10, createMenuButton(Material.GRASS_BLOCK, "Set your IslandSpawn location!", List.of( "This sets your island spawn location to your current location." diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java index 9f5cfc6..229886d 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/gui/UpgradesGUI.java @@ -32,7 +32,7 @@ public class UpgradesGUI extends GUIInventory { @Override public void decorate(Player player) { - makeMenuBar(player); + makeMenuBar(player, true); MessageConfiguration.Island islandMessages = CometSkyBlockPlugin.instance().messagesConfiguration().get().island(); // Todo - move to handlers, add costs, validation ... // WorldBorder diff --git a/plugin/src/main/java/com/alttd/cometskyblock/util/PlayerUtils.java b/plugin/src/main/java/com/alttd/cometskyblock/util/PlayerUtils.java new file mode 100644 index 0000000..df594e6 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/util/PlayerUtils.java @@ -0,0 +1,115 @@ +package com.alttd.cometskyblock.util; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Map; + +public class PlayerUtils { + + public static boolean hasItems(Inventory inventory, List items) { + if (inventory == null || items == null) + return false; + + for (ItemStack itemStack : items) { + int requiredAmount = itemStack.getAmount(); + for (int slot = 0; slot < inventory.getSize(); slot++) { + if (inventory.getItem(slot) != null) { + if (inventory.getItem(slot).getType().equals(itemStack.getType())) { + requiredAmount = requiredAmount - inventory.getItem(slot).getAmount(); + } + } + } + if (requiredAmount > 0) { + return false; + } + } + return true; + } + + public static boolean hasRoom(Inventory inventory, List items) { + for (ItemStack itemStack : items) { + if (!hasRoom(inventory, itemStack)) + return false; + } + return true; + } + + public static boolean hasRoom(Inventory inventory, ItemStack itemStack) { + if (inventory == null) + return false; + + if (itemStack.getAmount() <= 0) + return true; + + Inventory test = Bukkit.createInventory(null, 36); + test.setContents(inventory.getContents()); + Map remainingItems = test.addItem(itemStack); + + return remainingItems.isEmpty(); + } + + public static void giveItems(Inventory inventory, List items) { + for (ItemStack current : items) { + inventory.addItem(current.clone()); + } + } + + public static Object2IntMap getBlocks(Player player, Integer radius) { + Object2IntMap blocks = new Object2IntOpenHashMap<>(); + blocks.defaultReturnValue(0); + + Location loc = player.getLocation(); + int blockX = loc.getBlockX(); + int blockY = loc.getBlockY(); + int blockZ = loc.getBlockZ(); + + for (int x = blockX - radius; x < blockX + radius; x++ ) { + for (int z = blockZ - radius; z < blockZ + radius; z++) { + if (player.getWorld().isChunkLoaded(x >> 4, z >> 4)) { + for (int y = blockY - radius; y < blockY + radius; y++) { + Material blockType = player.getWorld().getBlockAt(x, y, z).getType(); + if (!blockType.isAir()) { + blocks.put(blockType, blocks.getInt(blockType) + 1); + } + } + } + } + } + return blocks; + } + + public static void takeItems(Inventory inventory, List items) { + if (inventory == null || items == null) + return; + + for (ItemStack itemStack : items) { + int amount = itemStack.getAmount(); + for (int slot = 0; slot < inventory.getSize(); slot++) { + ItemStack inventoryItem = inventory.getItem(slot); + if (inventoryItem != null) { + if (inventoryItem.getType().equals(itemStack.getType())) { + int newAmount = inventoryItem.getAmount() - amount; + if (newAmount > 0) { + inventoryItem.setAmount(newAmount); + break; + } else { + inventory.clear(slot); + amount = amount - inventoryItem.getAmount(); + if (amount == 0) { + break; + } + } + } + } + } + } + } +} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/util/StringUtil.java b/plugin/src/main/java/com/alttd/cometskyblock/util/StringUtil.java new file mode 100644 index 0000000..94d61c2 --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/util/StringUtil.java @@ -0,0 +1,109 @@ +package com.alttd.cometskyblock.util; + +import java.util.*; +import java.util.stream.Collectors; + +public class StringUtil { + + /** + * Splits a mini message string into smaller chunks. It re-opens any unclosed tags it encounters for + * each part of the string added to the list + * + * @param string the mini message string to split + * @return a list of the split strings + */ + public static List splitMiniMessageString(String string) { + Set tags = findTags(string); + List splitString = new ArrayList<>(); + Stack openTags = new Stack<>(); + Stack previousOpenTags = new Stack<>(); + int startCurString = 0; + int len = 0; + int i = 0; + for (; i < string.length(); i++) { + while (string.charAt(i) == '<') { + i = skipTags(i, string, openTags, tags); + } + len++; + if (len != 30) { + continue; + } + while (i < string.length() && !Character.isWhitespace(string.charAt(i))) { + while (string.charAt(i) == '<') { + i = skipTags(i, string, openTags, tags); + } + if (Character.isWhitespace(string.charAt(i))) { + break; + } + i++; + } + String prependTags = previousOpenTags.stream().map(str -> "<" + str + ">").collect(Collectors.joining("")); + splitString.add(prependTags + string.substring(startCurString, i)); + previousOpenTags.clear(); + previousOpenTags.addAll(openTags); + startCurString = ++i; + len = 0; + } + if (startCurString < string.length()) { + String prependTags = previousOpenTags.stream().map(str -> "<" + str + ">").collect(Collectors.joining("")); + splitString.add(prependTags + string.substring(startCurString)); + } + return splitString; + } + + private static int skipTags(int i, String string, Stack openTags, Set tags) { + StringBuilder tagBuilder = new StringBuilder(); + i++; + for (; i < string.length() && string.charAt(i) != '>'; i++) { + tagBuilder.append(string.charAt(i)); + } + String tagString = tagBuilder.toString(); + if (!tagString.isEmpty() && tagString.charAt(0) == '/') { + if (!openTags.isEmpty() && openTags.peek().contentEquals(tagString.substring(1))) { + openTags.pop(); + } + } else if (tags.contains(tagString)) { + openTags.add(tagString); + } + tagBuilder.setLength(0); + return Math.min(++i, string.length() - 1); + } + + public static Set findTags(String string) { + List tags = new ArrayList<>(); + HashSet closedTags = new HashSet<>(); + StringBuilder tag = new StringBuilder(); + boolean tagFound = false; + + for (int i = 0; i < string.length(); i++) { + if (string.charAt(i) == '<') { + tagFound = true; + continue; + } + + if (string.charAt(i) == '>') { + tagFound = false; + + if (tag.charAt(0) != '/') { + tags.add(tag.toString()); + } else { + String closingTag = tag.substring(1); + if (tags.contains(closingTag)) { + tags.remove(closingTag); + closedTags.add(closingTag); + } + } + + tag.setLength(0); + continue; + } + + if (tagFound) { + tag.append(string.charAt(i)); + } + } + + return closedTags; + } + +} diff --git a/plugin/src/main/resources/challenges.yml b/plugin/src/main/resources/challenges.yml new file mode 100644 index 0000000..153802f --- /dev/null +++ b/plugin/src/main/resources/challenges.yml @@ -0,0 +1,219 @@ +level: + easy: 0 + medium: 20 + hard: 50 + legendary: 75 + exotic: 100 +easy: + CobblestoneGenerator: + name: Cobblestone Generator + key: easy_cobblestone_generator + shown-item: Cobblestone + challenge-type: ON_PLAYER + description: "Build a cobblestone generator and farm 64 cobblestone." + required-text: "64 cobblestone" + required-items: + - Cobblestone;64 + reward-text: "3 Leather" + reward-items: + - Leather;3 + repeat-reward-text: "1 Leather" + repeat-reward-items: + - Leather;1 + CropFarmer: + name: Wheat Farmer + key: easy_crop_farmer + shown-item: Wheat + challenge-type: ON_PLAYER + description: "Collect 64 wheat." + required-text: "64 wheat" + required-items: + - Wheat;64 + reward-text: "variety seed pack." + reward-items: + - Melon_seeds;1 + - pumpkin_seeds;1 + repeat-reward-text: "variety seed pack." + repeat-reward-items: + - Melon_seeds;1 + - pumpkin_seeds;1 + CactusFarmer: + name: Cactus Farmer + key: easy_cactus_farmer + shown-item: Cactus + challenge-type: ON_PLAYER + description: "Collect 64 cactus." + required-text: "64 cactus" + required-items: + - Cactus;64 + reward-text: "5 sand" + reward-items: + - Sand;5 + repeat-reward-text: "1 sand" + repeat-reward-items: + - Sand;1 + AppleFarmer: + name: Apple Farmer + key: medium_apple_farmer + shown-item: Apple + challenge-type: ON_PLAYER + description: "Collect 8 apples." + required-text: "8 apples" + required-items: + - Apple;8 + reward-text: "One sapling from each type." + reward-items: + - Oak_Sapling;1 + - Spruce_Sapling;1 + - Birch_Sapling;1 + - Jungle_Sapling;1 + - Acacia_Sapling;1 + - Dark_Oak_Sapling;1 + repeat-reward-text: "One sapling from each type." + repeat-reward-items: + - Oak_Sapling;1 + - Spruce_Sapling;1 + - Birch_Sapling;1 + - Jungle_Sapling;1 + - Acacia_Sapling;1 + - Dark_Oak_Sapling;1 +medium: + Lumberjack: + name: Lumberjack + key: medium_lumberjack + shown-item: Diamond_axe + challenge-type: ON_PLAYER + description: "Obtain 32 oak-, spruce-, birch-, jungle-, acacia-, and dark oak logs." + required-text: "32 oak logs, 32 spruce logs, 32 birch logs, 32 jungle logs, 32 acacia logs, 32 dark oak logs" + required-items: + - Oak_Log;32 + - Spruce_Log;32 + - Birch_Log;32 + - Jungle_Log;32 + - Acacia_Log;32 + - Dark_Oak_Log;32 + reward-text: "16 gilded blackstone" + reward-items: + - Gilded_Blackstone;16 + repeat-reward-text: "8 gilded blackstone" + repeat-reward-items: + - Gilded_Blackstone;8 + AppleFarmer: + name: Apple Farmer + key: medium_apple_farmer + shown-item: Apple + challenge-type: ON_PLAYER + description: "Collect 8 apples." + required-text: "8 apples" + required-items: + - Apple;8 + reward-text: "One sapling from each type." + reward-items: + - Oak_Sapling;1 + - Spruce_Sapling;1 + - Birch_Sapling;1 + - Jungle_Sapling;1 + - Acacia_Sapling;1 + - Dark_Oak_Sapling;1 + repeat-reward-text: "One sapling from each type." + repeat-reward-items: + - Oak_Sapling;1 + - Spruce_Sapling;1 + - Birch_Sapling;1 + - Jungle_Sapling;1 + - Acacia_Sapling;1 + - Dark_Oak_Sapling;1 + MonsterHunter: + name: Monster Hunter + key: medium_monster_hunter + shown-item: Bone + challenge-type: ON_PLAYER + description: "Kill monsters and collect 64 rotten flesh, bones and arrows." + required-text: "64 rotten flesh, bones and arrows." + required-items: + - Rotten_Flesh;64 + - Bone;64 + - Arrow;64 + reward-text: "2 experience bottles!" + reward-items: + - experience_bottle;2 + repeat-reward-text: "One experience bottles!" + repeat-reward-items: + - experience_bottle + Exterminator: + name: Exterminator + key: medium_exterminator + shown-item: Cobweb + challenge-type: ON_PLAYER + description: "Kill spiders and collect their drops." + required-text: "64 strings, 32 spider eyes" + required-items: + - String;64 + - Spider_Eye;32 + reward-text: "2 Cobwebs" + reward-items: + - Cobweb;8 + repeat-reward-text: "One cobweb" + repeat-reward-items: + - Cobweb + FisherMan: + name: Fisherman + key: medium_fisherman + shown-item: Fishing_rod + challenge-type: ON_PLAYER + description: "This could have been sushi" + required-text: "10 cooked cod and 10 cooked salmon" + required-items: + - Cooked_Cod;10 + - Cooked_Salmon;10 + reward-text: "2 experience bottles!" + reward-items: + - experience_bottle;2 + repeat-reward-text: "One experience bottles!" + repeat-reward-items: + - experience_bottle +hard: + NetherLumberjack: + name: Nether Lumberjack + key: hard_nether_lumberjack + shown-item: Netherite_axe + challenge-type: ON_PLAYER + description: "Obtain 64 crimson stems and 64 warped stems" + required-text: "64 crimson stems, 64 warped stems" + required-items: + - Crimson_Stem;64 + - Warped_Stem;64 + reward-text: "16 gilded blackstone" + reward-items: + - Gilded_Blackstone;16 + repeat-reward-text: "8 gilded blackstone" + repeat-reward-items: + - Gilded_Blackstone;8 +legendary: + BeastSlayer: + name: Beast Slayer + key: legendary_beast_slayer + shown-item: Beacon + challenge-type: ON_ISLAND + description: "Place a beacon on your island." + required-text: "One beacon" + required-items: + - Beacon;1 + reward-text: "One Nether Star" + reward-items: + - Nether_star + repeat-reward-text: "1 Diamond_Block" + repeat-reward-items: + - Diamond_Block;1 +exotic: + Babylon: + name: Babylon + key: exotic_babylon + shown-item: bedrock + challenge-type: ISLAND_LEVEL + description: "Open the gates of Babylon!" + required-text: "Island level 1000" + reward-text: "1 bedrock" + reward-items: + - Bedrock;1 + required-level: 1000 diff --git a/plugin/src/test/java/StringUtilTest.java b/plugin/src/test/java/StringUtilTest.java new file mode 100644 index 0000000..a1cb8fd --- /dev/null +++ b/plugin/src/test/java/StringUtilTest.java @@ -0,0 +1,91 @@ +import com.alttd.cometskyblock.util.StringUtil; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class StringUtilTest { + + /** + * StringUtilTest tests the splitString method in the StringUtil class. + * The splitString method is designed to split a string, making them shorter and more suitable + * for use in minecraft lore. + */ + + @Test + public void testSplitString() { + String testString = "This is a long string that needs to be split into multiple smaller chunks."; + List expectedOutput = List.of( + "This is a long string that needs", + "to be split into multiple smaller", + "chunks."); + assertEquals(expectedOutput, StringUtil.splitMiniMessageString(testString)); + } + + @Test + public void testSplitStringWithMarkups() { + String testString = "This is a long tag string that needs to be split into smaller chunks."; + List expectedOutput = List.of( + "This is a long tag string that", + "needs to be split into", + "smaller chunks."); + assertEquals(expectedOutput, StringUtil.splitMiniMessageString(testString)); + } + + @Test + public void testUsedString() { + String testString = "Required items: 64 Cobblestone"; + List expectedOutput = List.of("Required items: 64 Cobblestone"); + assertEquals(expectedOutput, StringUtil.splitMiniMessageString(testString)); + } + + @Test + public void testSplitStringWithShortString() { + String testString = "Short string."; + List expectedOutput = List.of("Short string."); + assertEquals(expectedOutput, StringUtil.splitMiniMessageString(testString)); + } + + @Test + public void testSplitStringWithManyTagsString() { + String testString = "This is a long string with a and a lot of text"; + List expectedOutput = List.of("This is a long string with a ", + "and a lot of text"); + assertEquals(expectedOutput, StringUtil.splitMiniMessageString(testString)); + } + + @Test + public void testFindTagsWithCorrectTags() { + String input = "This is a test string."; + Set result = StringUtil.findTags(input); + assertTrue(result.containsAll(List.of("red", "blue"))); + assertEquals(2, result.size()); + } + + @Test + public void testFindTagsWithUnclosedTag() { + String input = "This is a test string."; + Set result = StringUtil.findTags(input); + assertTrue(result.contains("red")); + assertFalse(result.contains("placeholder")); + assertEquals(1, result.size()); + } + + @Test + public void testFindTagsWithNoTags() { + String input = "This is a test string."; + Set result = StringUtil.findTags(input); + assertTrue(result.isEmpty()); + } + + @Test + public void testFindTagsWithManyTags() { + String input = "This is a test string."; + Set result = StringUtil.findTags(input); + assertTrue(result.containsAll(List.of("red", "blue", "green", "orange"))); + assertFalse(result.contains("not_a_color")); + assertEquals(4, result.size()); + } +} \ No newline at end of file