diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts index 25e065bb..a164ac25 100644 --- a/paper/build.gradle.kts +++ b/paper/build.gradle.kts @@ -155,30 +155,36 @@ repositories { } } + maven("https://repo.xenondevs.xyz/releases") + maven("https://nexus.betonquest.org/repository/betonquest/") + maven("https://maven.citizensnpcs.co/repo") + maven("https://repo.magmaguy.com/releases") //mavenLocal() } dependencies { - implementation(project(path= ":common", configuration= "shadow")) + implementation(project(path = ":common", configuration = "shadow")) paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") //compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT!!") //implementation("de.themoep:inventorygui:1.5-SNAPSHOT") - //compileOnly("net.citizensnpcs:citizens-main:2.0.30-SNAPSHOT") - compileOnly(files("libs/citizens-2.0.34-b3410.jar")) + compileOnly("net.citizensnpcs:citizens-main:2.0.30-SNAPSHOT") { + exclude(group = "*", module = "*") + } compileOnly("me.clip:placeholderapi:2.11.5") compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") compileOnly("io.lumine:Mythic-Dist:5.3.0-SNAPSHOT") + compileOnly("com.magmaguy:EliteMobs:9.1.9") compileOnly(files("libs/EliteMobs-8.7.11.jar")) - compileOnly(files("libs/ProjectKorra-1.11.2.jar")) + compileOnly(files("libs/ProjectKorra-1.11.3.jar")) //compileOnly(files("libs/UltimateJobs-0.2.0-SNAPSHOT.jar")) - compileOnly(files("libs/betonquest-2.0.1.jar")) + compileOnly(files("libs/BetonQuest.jar")); compileOnly("com.sk89q.worldedit:worldedit-core:7.3.0-SNAPSHOT") compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.0-SNAPSHOT") @@ -195,8 +201,6 @@ dependencies { compileOnly("org.geysermc.floodgate:api:2.2.2-SNAPSHOT") - - //Shaded @@ -213,7 +217,11 @@ dependencies { } //Else it errors: implementation("io.leangen.geantyref:geantyref:1.3.13") - //Interfaces + + //Interfaces + InvUI + + implementation("xyz.xenondevs.invui:invui:1.38") + implementation("org.incendo.interfaces:interfaces-core:1.0.0-SNAPSHOT") implementation("org.incendo.interfaces:interfaces-paper:1.0.0-SNAPSHOT") { @@ -243,7 +251,7 @@ dependencies { compileOnly(files("libs/EcoBosses-v8.78.0.jar")) compileOnly("com.willfp:eco:6.38.3") - compileOnly(files("libs/znpcs-4.6.jar")) + compileOnly(files("libs/znpcs-4.8.jar")) implementation("com.github.Redempt:Crunch:2.0.3") @@ -267,7 +275,7 @@ tasks { shadowJar { - minimize() + //minimize() //exclude('com.mojang:brigadier') @@ -288,6 +296,7 @@ tasks { //relocate('net.kyori.adventure.platform.bukkit', path.concat('.kyori.platform-bukkit')) relocate("net.kyori.adventure.text.serializer.bungeecord", "$shadowPath.kyori.bungeecord") + relocate("xyz.xenondevs.invui", "$shadowPath.invui") relocate("org.incendo.interfaces", "$shadowPath.interfaces") @@ -305,6 +314,7 @@ tasks { dependencies { //include(dependency('org.apache.commons:') include(dependency("commons-io:commons-io:")) + include(dependency("xyz.xenondevs.invui:")) //include(dependency('io.papermc:paperlib') //include(dependency("de.themoep:inventorygui:1.5-SNAPSHOT")) @@ -351,8 +361,6 @@ tasks { //} - - /*shadowJar { dependsOn(reobfJar) }*/ @@ -366,9 +374,11 @@ tasks { javadoc { options.encoding = Charsets.UTF_8.name() } + processResources { filteringCharset = Charsets.UTF_8.name() } + runServer { // Configure the Minecraft version for our task. // This is the only required configuration besides applying the plugin. diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/NotQuests.java b/paper/src/main/java/rocks/gravili/notquests/paper/NotQuests.java index 154684ad..24f0ab56 100644 --- a/paper/src/main/java/rocks/gravili/notquests/paper/NotQuests.java +++ b/paper/src/main/java/rocks/gravili/notquests/paper/NotQuests.java @@ -35,6 +35,7 @@ import rocks.gravili.notquests.paper.events.QuestEvents; import rocks.gravili.notquests.paper.events.TriggerEvents; import rocks.gravili.notquests.paper.events.notquests.NotQuestsFullyLoadedEvent; +import rocks.gravili.notquests.paper.gui.GuiService; import rocks.gravili.notquests.paper.managers.*; import rocks.gravili.notquests.paper.managers.integrations.IntegrationsManager; import rocks.gravili.notquests.paper.managers.integrations.bstats.Metrics; @@ -84,6 +85,7 @@ public class NotQuests extends NotQuestsMainAbstract { private ItemsManager itemsManager; //Registering Managers + private GuiService guiService; private ObjectiveManager objectiveManager; private ConditionsManager conditionsManager; private ActionManager actionManager; @@ -180,7 +182,7 @@ public void onEnable() { updateManager = new UpdateManager(this); - guiManager = new GUIManager(this); + reloadGuis(); /* * Tell the Data Manager: Hey, NPCs have not been loaded yet. If this is set to false, the plugin will @@ -213,6 +215,8 @@ public void onEnable() { actionManager = new ActionManager(this); triggerManager = new TriggerManager(this); + guiManager = new GUIManager(this); + variablesManager.alreadyFullRegisteredVariables.addAll(variablesManager.getVariableIdentifiers()); commandManager.setupCommands(); @@ -282,6 +286,17 @@ public void onEnable() { } + + /** + * Loads all guis located in the gui folder + * Use this for refreshing guis after making changes in the gui files + */ + public void reloadGuis() { + guiService = new GuiService(this); + guiService.saveAllDefaultGuis(); + guiService.loadAllGuis(); + } + public void setupBStats() { //bStats statistics final int pluginId = 12824; // <- Plugin ID (on bstats) @@ -564,4 +579,7 @@ public final NPCManager getNPCManager() { return npcManager; } + public GuiService getGuiService() { + return guiService; + } } diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/commands/AdminCommands.java b/paper/src/main/java/rocks/gravili/notquests/paper/commands/AdminCommands.java index bfcb57ae..982c6fad 100644 --- a/paper/src/main/java/rocks/gravili/notquests/paper/commands/AdminCommands.java +++ b/paper/src/main/java/rocks/gravili/notquests/paper/commands/AdminCommands.java @@ -29,10 +29,15 @@ import cloud.commandframework.bukkit.parsers.selector.SinglePlayerSelectorArgument; import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.paper.PaperCommandManager; + +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; + +import com.gamingmesh.jobs.economy.PaymentData; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; @@ -49,6 +54,7 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; +import org.spongepowered.configurate.ConfigurateException; import rocks.gravili.notquests.paper.NotQuests; import rocks.gravili.notquests.paper.commands.arguments.*; import rocks.gravili.notquests.paper.commands.arguments.variables.BooleanVariableValueArgument; @@ -618,13 +624,14 @@ public AdminCommands(final NotQuests main, PaperCommandManager ma main.getDataManager().loadGeneralConfig(); main.getLanguageManager().loadLanguageConfig(false); + main.reloadGuis(); if(main.getConversationManager() != null) { main.getConversationManager().loadConversationsFromConfig(); }else{ context.getSender().sendMessage(" Loading conversations has been skipped: ConversationManager is null"); } context.getSender().sendMessage(Component.empty()); - context.getSender().sendMessage(main.parse("NotQuests general.yml, language configuration and conversations have been re-loaded. If you want to reload more, please use the ServerUtils plugin (available on spigot) or restart the server. This reload command does not reload the quests file or the database.")); + context.getSender().sendMessage(main.parse("NotQuests general.yml, language configuration, guis and conversations have been re-loaded. If you want to reload more, please use the ServerUtils plugin (available on spigot) or restart the server. This reload command does not reload the quests file or the database.")); })); manager.command(builder.literal("reload", "load") @@ -645,6 +652,15 @@ public AdminCommands(final NotQuests main, PaperCommandManager ma context.getSender().sendMessage(main.parse("Languages have been reloaded.")); })); + manager.command(builder.literal("reload", "load") + .literal("guis") + .meta(CommandMeta.DESCRIPTION, "Reload the guis from gui files.") + .handler((context) -> { + main.reloadGuis(); + context.getSender().sendMessage(Component.empty()); + context.getSender().sendMessage(main.parse("Guis have been reloaded.")); + })); + manager.command(builder.literal("reload", "load") .literal("conversations") .meta(CommandMeta.DESCRIPTION, "Reload the conversations from conversations files.") diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/commands/UserCommands.java b/paper/src/main/java/rocks/gravili/notquests/paper/commands/UserCommands.java index df0af038..bd554090 100644 --- a/paper/src/main/java/rocks/gravili/notquests/paper/commands/UserCommands.java +++ b/paper/src/main/java/rocks/gravili/notquests/paper/commands/UserCommands.java @@ -49,6 +49,7 @@ import rocks.gravili.notquests.paper.commands.arguments.QuestSelector; import rocks.gravili.notquests.paper.conversation.ConversationLine; import rocks.gravili.notquests.paper.conversation.ConversationPlayer; +import rocks.gravili.notquests.paper.gui.GuiContext; import rocks.gravili.notquests.paper.managers.data.Category; import rocks.gravili.notquests.paper.structs.ActiveQuest; import rocks.gravili.notquests.paper.structs.Quest; @@ -481,232 +482,10 @@ public void constructGUICommands() { final QuestPlayer questPlayer = main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - //main.getGuiManager().showMainQuestsGUI(questPlayer); - main.getGuiManager().showTakeQuestsGUI(questPlayer); - })); - - manager.command( - builder - .literal("take") - .senderType(Player.class) - .meta(CommandMeta.DESCRIPTION, "Starts a Quest.") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - - main.getGuiManager().showTakeQuestsGUI(questPlayer); - })); - - manager.command( - builder - .literal("activeQuests") - .senderType(Player.class) - .meta(CommandMeta.DESCRIPTION, "Shows your active Quests.") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - final QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - - main.getGuiManager().showActiveQuestsGUI(questPlayer); - })); - - manager.command( - builder - .literal("abort") - .senderType(Player.class) - .meta(CommandMeta.DESCRIPTION, "Aborts an active Quest.") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - final QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - if (questPlayer != null) { - - main.getGuiManager().showAbortQuestsGUI(questPlayer); - } else { - context - .getSender() - .sendMessage( - main.parse( - main.getLanguageManager() - .getString("chat.no-quests-accepted", player))); - } - })); - - manager.command( - builder - .literal("preview") - .senderType(Player.class) - .meta(CommandMeta.DESCRIPTION, "Shows a Preview for a Quest.") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - final QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - - main.getGuiManager().showTakeQuestsGUI(questPlayer); - })); - - manager.command( - builder - .literal("abort") - .senderType(Player.class) - .argument( - ActiveQuestSelector.of("Active Quest", main, null), - ArgumentDescription.of("Name of the active Quest which should be aborted/failed")) - .meta(CommandMeta.DESCRIPTION, "Aborts an active Quest") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - final ActiveQuest activeQuest = context.get("Active Quest"); - - if (questPlayer != null && questPlayer.getActiveQuests().size() > 0) { - main.getGuiManager().showAbortQuestGUI(questPlayer, activeQuest); - } else { - context - .getSender() - .sendMessage( - main.parse( - main.getLanguageManager() - .getString("chat.no-quests-accepted", player))); - } - })); - - manager.command( - builder - .literal("preview") - .senderType(Player.class) - .argument( - QuestSelector.newBuilder("Quest Name", main) - .takeEnabledOnly() - .build(), - ArgumentDescription.of("Quest Name")) - .meta(CommandMeta.DESCRIPTION, "Previews a Quest") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - final QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer((player.getUniqueId())); - final Quest quest = context.get("Quest Name"); - - main.getGuiManager().showPreviewQuestGUI(questPlayer, quest); - })); - - manager.command( - builder - .literal("progress") - .senderType(Player.class) - .argument( - ActiveQuestSelector.of("Active Quest", main, null), - ArgumentDescription.of( - "Name of the active Quest of which you want to see the progress")) - .meta(CommandMeta.DESCRIPTION, "Shows progress for an active Quest") - .handler( //TODO: This does text stuff. Add better GUI later - (context) -> { - final Player player = (Player) context.getSender(); - QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - if (!questPlayer.getActiveQuests().isEmpty()) { - final ActiveQuest activeQuest = context.get("Active Quest"); - - context - .getSender() - .sendMessage( - main.parse( - "Completed Objectives for Quest " - + activeQuest.getQuest().getDisplayNameOrIdentifier() - + ":")); - main.getQuestManager() - .sendCompletedObjectivesAndProgress(questPlayer, activeQuest); - context - .getSender() - .sendMessage( - main.parse( - "Active Objectives for Quest " - + activeQuest.getQuest().getDisplayNameOrIdentifier() - + ":")); - main.getQuestManager() - .sendActiveObjectivesAndProgress(questPlayer, activeQuest, 0); - - } else { - context - .getSender() - .sendMessage( - main.parse( - main.getLanguageManager() - .getString("chat.no-quests-accepted", player))); - } - }) - /*.handler( - (context) -> { - final Player player = (Player) context.getSender(); - QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer(player.getUniqueId()); - if (!questPlayer.getActiveQuests().isEmpty()) { - final ActiveQuest activeQuest = context.get("Active Quest"); - - main.getGuiManager().showQuestProgressGUI(questPlayer, activeQuest); - - /*for (final ActiveObjective activeObjective : activeQuest.getCompletedObjectives()) { - - final Material materialToUse = Material.FILLED_MAP; - - int count = activeObjective.getObjectiveID(); - if (!main.getConfiguration().showObjectiveItemAmount) { - count = 0; - } - - String descriptionToDisplay = main.getLanguageManager().getString("gui.progress.button.completedObjective.description-empty", player, activeObjective, questPlayer); - if (!activeObjective.getObjective().getObjectiveDescription().isBlank()) { - descriptionToDisplay = activeObjective.getObjective().getObjectiveDescription(main.getConfiguration().guiObjectiveDescriptionMaxLineLength); - } - - group.addElement(new StaticGuiElement('e', - new ItemStack(materialToUse), - count, - click -> { - return true; - }, - convert( - main.getLanguageManager().getString("gui.progress.button.completedObjective.text", player, activeObjective, questPlayer) - .replace("%OBJECTIVEDESCRIPTION%", descriptionToDisplay) - .replace("%COMPLETEDOBJECTIVETASKDESCRIPTION%", main.getQuestManager().getObjectiveTaskDescription(activeObjective.getObjective(), true, player)) - ) - )); - }*//* - - } else { - context - .getSender() - .sendMessage( - main.parse( - main.getLanguageManager() - .getString("chat.no-quests-accepted", player))); - } - })*/); - manager.command( - builder - .literal("category") - .senderType(Player.class) - .argument( - CategorySelector.newBuilder("Category", main) - .takeEnabledOnly() - .build(), - ArgumentDescription.of("Category Name")) - .meta(CommandMeta.DESCRIPTION, "Opens the category view") - .handler( - (context) -> { - final Player player = (Player) context.getSender(); - final QuestPlayer questPlayer = - main.getQuestPlayerManager().getOrCreateQuestPlayer((player.getUniqueId())); - final Category category = context.get("Category"); - - main.getGuiManager().showTakeQuestsGUIOfCategory(questPlayer, category); + var mainGuiName = main.getConfiguration().getMainGuiName(); + var guiContext = new GuiContext(); + guiContext.setPlayer(player); + main.getGuiService().showGui(mainGuiName, player, guiContext); })); } diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/CustomGui.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/CustomGui.java new file mode 100644 index 00000000..75fb63be --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/CustomGui.java @@ -0,0 +1,248 @@ +package rocks.gravili.notquests.paper.gui; + +import rocks.gravili.notquests.paper.NotQuests; +import rocks.gravili.notquests.paper.gui.icon.Button; +import rocks.gravili.notquests.paper.gui.icon.ButtonType; +import rocks.gravili.notquests.paper.gui.icon.SpecialIconType; +import rocks.gravili.notquests.paper.gui.property.types.StringIconProperty; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.gui.PagedGui; +import xyz.xenondevs.invui.gui.TabGui; +import xyz.xenondevs.invui.gui.structure.Markers; +import xyz.xenondevs.invui.gui.structure.Structure; +import xyz.xenondevs.invui.item.Item; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class CustomGui { + private final String pathToTitle; + private final String type; + private String[] structure; + private final Map icons; + private final List additionalGuis; + private NotQuests notQuests; + + private Gui gui; + + public CustomGui(String[] structure, String pathToTitle, String type, Map icons, List additionalGuis) { + this.pathToTitle = pathToTitle; + this.structure = structure; + this.type = type; + this.icons = icons; + this.additionalGuis = additionalGuis; + } + + public Gui buildGui(NotQuests notQuests, GuiContext guiContext) { + this.notQuests = notQuests; + + switch (type) { + case "TAB" -> this.gui = handleTabGui(guiContext); + case "PAGED_ITEMS" -> this.gui = handlePagedItemsGui(guiContext); + /* + case "PAGED_GUIS" -> this.gui = handlePagedGuisGui(guiObjectContext); + case "SCROLL_ITEMS" -> this.gui = handleScrollItemsGui(guiObjectContext); + case "SCROLL_GUIS" -> this.gui = handleScrollGuisGui(guiObjectContext); + + */ + default -> this.gui = handleNormalGui(guiContext); + } + return this.gui; + } + + + + private SpecialIconType fetchSpecialIconType(Button icon) { + if (icon.getIconProperty("specialicontype").isPresent()) { + if (icon.getIconProperty("specialicontype").get().getValue() instanceof StringIconProperty stringIconProperty) { + return SpecialIconType.valueOf(stringIconProperty.value()); + } + } + return SpecialIconType.DEFAULT; + } + + private Gui handleNormalGui(GuiContext guiContext) { + var guiBuilder = Gui.normal().setStructure(structure); + + icons.forEach((key, icon) -> guiBuilder.addIngredient(key, icon.buildItem(notQuests, guiContext))); + for (Object o : guiContext.getAsObjectArray()) { + notQuests.getLogManager().debug(pathToTitle + o); + } + + return guiBuilder.build(); + } + private Gui handlePagedItemsGui(GuiContext guiContext) { + var structureObject = new Structure(structure); + var targetQuestPlayer = notQuests.getQuestPlayerManager().getOrCreateQuestPlayerFromDatabase(guiContext.getPlayer().getUniqueId()); + + var guiBuilder = PagedGui.items().setStructure(structure); + + //X and Y are reserved characters! + var pagedIcon = icons.get('X'); + var fillerIcon = icons.get('Y'); + + if (pagedIcon == null) { + notQuests.getLogManager().debug("Paged icon null"); + return guiBuilder.build(); + } + + + var pagedIconSpecialType = fetchSpecialIconType(pagedIcon); + + guiBuilder.addIngredient('X', Markers.CONTENT_LIST_SLOT_HORIZONTAL); + structureObject.addIngredient('X', Markers.CONTENT_LIST_SLOT_HORIZONTAL); + + + var items = new ArrayList(); + var guiSize = structureObject.getIngredientList().findContentListSlots().length; + var numberOfTotalItems = guiSize; + + switch (pagedIconSpecialType) { + case PLAYER_ACTIVE_QUEST -> { + numberOfTotalItems = guiSize * ((targetQuestPlayer.getActiveQuests().size() / guiSize) + 1); + targetQuestPlayer.getActiveQuests().forEach(activeQuest -> { + if (activeQuest != null) { + var itemGuiContext = guiContext.clone(); + itemGuiContext.setActiveQuest(activeQuest); + items.add(pagedIcon.buildItem(notQuests, itemGuiContext)); + } + }); + } + + case NPC_SHOWN_QUEST -> { + if (guiContext.getNqnpc() != null) { + var npc = guiContext.getNqnpc(); + var npcQuests = notQuests.getQuestManager().getQuestsFromListWithVisibilityEvaluations(targetQuestPlayer, notQuests.getQuestManager().getQuestsAttachedToNPCWithShowing(npc)); + numberOfTotalItems = guiSize * ((npcQuests.size() / guiSize) + 1); + npcQuests.forEach(quest -> { + var itemGuiContext = guiContext.clone(); + itemGuiContext.setQuest(quest); + items.add(pagedIcon.buildItem(notQuests, itemGuiContext)); + }); + } else if (guiContext.getArmorStand() != null) { + var armorStand = guiContext.getArmorStand(); + var armorStandQuests = notQuests.getQuestManager().getQuestsFromListWithVisibilityEvaluations(targetQuestPlayer, notQuests.getQuestManager().getQuestsAttachedToArmorstandWithShowing(armorStand)); + armorStandQuests.forEach(quest -> { + var itemGuiContext = guiContext.clone(); + itemGuiContext.setQuest(quest); + items.add(pagedIcon.buildItem(notQuests, itemGuiContext)); + }); + } + } + + case CATEGORY_AVAILABLE_QUEST -> { + if (guiContext.getCategory() != null) { + var category = guiContext.getCategory(); + var categoryQuests = category.getQuests(); + numberOfTotalItems = guiSize * ((categoryQuests.size() / guiSize) + 1); + categoryQuests.forEach(quest -> { + var itemGuiContext = guiContext.clone(); + itemGuiContext.setQuest(quest); + items.add(pagedIcon.buildItem(notQuests, itemGuiContext)); + }); + } + } + + case CATEGORY_PLAYER_AVAILABLE_QUEST -> { + if (guiContext.getCategory() != null) { + var category = guiContext.getCategory(); + var categoryQuests = notQuests.getQuestManager().getAllQuestsWithVisibilityEvaluations(targetQuestPlayer) + .stream() + .filter(quest -> quest.getCategory().getCategoryFullName().equals(category.getCategoryFullName())) + .collect(Collectors.toSet()); + numberOfTotalItems = guiSize * ((categoryQuests.size() / guiSize) + 1); + categoryQuests.forEach(quest -> { + var itemGuiContext = guiContext.clone(); + itemGuiContext.setQuest(quest); + items.add(pagedIcon.buildItem(notQuests, itemGuiContext)); + }); + } + } + + case CATEGORY -> { + var categories = notQuests.getDataManager().getCategories(); + numberOfTotalItems = guiSize * ((categories.size() / guiSize) + 1); + categories.forEach(category -> { + var itemGuiContext = guiContext.clone(); + itemGuiContext.setCategory(category); + notQuests.getLogManager().info("category: " + category); + items.add(pagedIcon.buildItem(notQuests, itemGuiContext)); + }); + } + + case DEFAULT -> items.add(pagedIcon.buildItem(notQuests, guiContext)); + } + + if (fillerIcon != null) { + while(items.size() < numberOfTotalItems) { + items.add(fillerIcon.buildItem(notQuests, guiContext)); + } + } + + icons.forEach((key, icon) -> { + if (icon == null) { + return; + } + if (fetchSpecialIconType(icon) != SpecialIconType.DEFAULT) { + return; + } + guiBuilder.addIngredient(key, icon.buildItem(notQuests, guiContext)); + }); + + + guiBuilder.setContent(items); + + return guiBuilder.build(); + } + + /* + private GUI handlePagedGuisGui(GuiObjectContext guiObjectContext) {} + private GUI handleScrollItemsGui(GuiObjectContext guiObjectContext){} + private GUI handleScrollGuisGui(GuiObjectContext guiObjectContext) {} + + */ + private Gui handleTabGui(GuiContext guiContext) { + var tabGuis = new ArrayList(); + + additionalGuis.forEach(string -> { + var tabGui = notQuests.getGuiService().getGuis().get(string); + if (tabGui == null) { + return; + } + + tabGuis.add(tabGui.buildGui(notQuests, guiContext)); + }); + + var guiBuilder = TabGui.normal() + .setTabs(tabGuis) + .setStructure(structure) + .addIngredient('X', Markers.CONTENT_LIST_SLOT_HORIZONTAL); + + icons.forEach((key, icon) -> { + if (icon == null) { + return; + } + if (icon.getType() == ButtonType.PAGED) { + return; + } + guiBuilder.addIngredient(key, icon.buildItem(notQuests, guiContext)); + }); + + return guiBuilder.build(); + } + + + public String getPathToTitle() { + return pathToTitle; + } + + public String getType() { + return type; + } + + public Map getIcons() { + return icons; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/GuiContext.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/GuiContext.java new file mode 100644 index 00000000..708d5419 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/GuiContext.java @@ -0,0 +1,95 @@ +package rocks.gravili.notquests.paper.gui; + +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import rocks.gravili.notquests.paper.managers.data.Category; +import rocks.gravili.notquests.paper.managers.npc.NQNPC; +import rocks.gravili.notquests.paper.structs.ActiveQuest; +import rocks.gravili.notquests.paper.structs.Quest; + +public class GuiContext { + private Player player; + private Quest quest; + private ActiveQuest activeQuest; + private NQNPC nqnpc; + + private ArmorStand armorStand; + + private Category category; + + public GuiContext() { + + } + + public GuiContext(Player player, Quest quest, ActiveQuest activeQuest, NQNPC nqnpc, ArmorStand armorStand, Category category) { + this.player = player; + this.quest = quest; + this.activeQuest = activeQuest; + this.nqnpc = nqnpc; + this.armorStand = armorStand; + this.category = category; + } + + public Player getPlayer() { + return player; + } + + public void setPlayer(Player player) { + this.player = player; + } + + public Quest getQuest() { + return quest; + } + + public ActiveQuest getActiveQuest() { + return activeQuest; + } + + public void setQuest(Quest quest) { + this.quest = quest; + } + + public void setActiveQuest(ActiveQuest activeQuest) { + this.activeQuest = activeQuest; + } + + public NQNPC getNqnpc() { + return nqnpc; + } + + public void setNqnpc(NQNPC nqnpc) { + this.nqnpc = nqnpc; + } + + public ArmorStand getArmorStand() { + return armorStand; + } + + public void setArmorStand(ArmorStand armorStand) { + this.armorStand = armorStand; + } + + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + public Object[] getAsObjectArray() { + return new Object[]{ + player, + activeQuest, + quest, + nqnpc, + category + }; + } + + public GuiContext clone() { + return new GuiContext(player, quest, activeQuest, nqnpc, armorStand, category); + } + +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/GuiService.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/GuiService.java new file mode 100644 index 00000000..a9d21dfb --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/GuiService.java @@ -0,0 +1,168 @@ +package rocks.gravili.notquests.paper.gui; + +import org.bukkit.entity.Player; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.yaml.YamlConfigurationLoader; +import rocks.gravili.notquests.paper.NotQuests; +import rocks.gravili.notquests.paper.gui.icon.Button; +import rocks.gravili.notquests.paper.gui.icon.Icon; +import rocks.gravili.notquests.paper.gui.typeserializer.IconTypeSerializer; +import rocks.gravili.notquests.paper.gui.typeserializer.ItemTypeSerializer; +import xyz.xenondevs.inventoryaccess.component.AdventureComponentWrapper; +import xyz.xenondevs.invui.window.Window; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.logging.Level; + +public class GuiService { + private final NotQuests notQuests; + private final Map guis; + private final Set knownProperties; + + public GuiService(NotQuests notQuests) { + this.notQuests = notQuests; + this.guis = new HashMap<>(); + this.knownProperties = new HashSet<>(); + } + + public void showGui(String guiName, Player player, GuiContext guiContext) { + + var customGui = guis.get(guiName); + if (customGui == null) { + notQuests.getLogManager().info("Failed showing gui '" + guiName + "' to player " + player.getName()); + return; + } + var title = new AdventureComponentWrapper(notQuests.getLanguageManager().getComponent(customGui.getPathToTitle(), player, guiContext.getAsObjectArray())); + var window = Window.single() + .setViewer(player) + .setTitle(title) + .setGui(customGui.buildGui(notQuests, guiContext)).build(); + window.open(); + } + + public void saveAllDefaultGuis() { + saveDefaultGui("main-base"); + saveDefaultGui("main-take"); + saveDefaultGui("main-active"); + saveDefaultGui("category-take"); + saveDefaultGui("active-quest-abort-confirm"); + saveDefaultGui("active-quest-info"); + saveDefaultGui("npc-available-quests"); + saveDefaultGui("quest-preview"); + } + + public void saveDefaultGui(String guiName) { + notQuests.getMain().saveResource("guis/" + guiName + ".yml", false); + } + + public void loadAllGuis() { + var guisFolder = notQuests.getMain().getDataFolder().toPath().resolve(Paths.get("guis")); + + var fileNames = guisFolder.toFile().list(); + + if (fileNames == null) { + notQuests.getLogManager().warn("No guis found"); + return; + } + + Arrays.stream(fileNames).filter(fileName -> fileName.endsWith(".yml")).forEach(fileName -> { + notQuests.getLogManager().info("Found gui file: " + fileName); + notQuests.getLogManager().info("Attempting to load gui from file..."); + loadGui(guisFolder, fileName); + }); + } + + public void loadGui(Path path, String name) { + var guiName = name.replace(".yml", ""); + var guiPath = path.resolve(Paths.get(name)); + var yamlLoader = YamlConfigurationLoader.builder() + .path(guiPath) + .defaultOptions(opts -> opts.serializers(build -> build.register(Button.class, new IconTypeSerializer()))) + .defaultOptions(opts -> opts.serializers(build -> build.register(Icon.class, new ItemTypeSerializer()))) + .build(); + + CommentedConfigurationNode rootNode = null; + + try { + rootNode = yamlLoader.load(); + } catch (ConfigurateException e) { + notQuests.getLogManager().warn("Failed loading gui '" + name + "'", e); + notQuests.getMain().getLogger().log(Level.WARNING, "Error:", e); + + return; + } + + List structure = null; + + try { + structure = rootNode.node("structure").getList(String.class); + } catch (SerializationException e) { + notQuests.getLogManager().warn("An error occurred while loading the gui " + name); + notQuests.getMain().getLogger().log(Level.WARNING, "ERROR:", e); + } + + if (structure == null) { + notQuests.getLogManager().warn("An error occurred while loading the gui " + name); + notQuests.getLogManager().warn("ERROR: No structure specified"); + return; + } + + var type = rootNode.node("type").getString(); + if (type == null) { + type = "NORMAL"; + } + + var pathToTitle = rootNode.node("title").getString(); + + // Load gui "tabs" + var additionalGuis = List.of(""); + + try { + additionalGuis = rootNode.node("additionalguis").getList(String.class); + } catch (SerializationException e) { + notQuests.getLogManager().warn("An error occurred while loading the gui " + name); + notQuests.getMain().getLogger().log(Level.WARNING, "Error:", e); + } + + + var iconsMap = rootNode.node("icons").childrenMap(); + var icons = new HashMap(); + iconsMap.forEach((key, node) -> { + try { + var icon = iconsMap.get(key).get(Button.class); + icons.put(String.valueOf(key).charAt(0), icon); + } catch (SerializationException | IllegalArgumentException e) { + notQuests.getLogManager().warn("An error occurred while loading the gui " + name); + notQuests.getMain().getLogger().log(Level.WARNING, "Error:", e); + } + + }); + + var itemsMap = rootNode.node("items").childrenMap(); + var items = new HashMap(); + itemsMap.forEach((key, node) -> { + try { + var item = itemsMap.get(key).get(Icon.class); + items.put(String.valueOf(key), item); + } catch (SerializationException e) { + notQuests.getLogManager().warn("An error occurred while loading the gui " + name); + notQuests.getMain().getLogger().log(Level.WARNING, "Error:", e); + } + + }); + + icons.forEach((key, icon) -> icon.registerIcons(items)); + + var customGui = new CustomGui(structure.toArray(String[]::new), pathToTitle, type, icons, additionalGuis); + + guis.put(guiName, customGui); + } + + public Map getGuis() { + return guis; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/Button.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/Button.java new file mode 100644 index 00000000..228b9b31 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/Button.java @@ -0,0 +1,113 @@ +package rocks.gravili.notquests.paper.gui.icon; + +import net.kyori.adventure.text.Component; +import org.apache.logging.log4j.core.util.Integers; +import rocks.gravili.notquests.paper.NotQuests; +import rocks.gravili.notquests.paper.gui.GuiContext; +import rocks.gravili.notquests.paper.gui.item.*; +import rocks.gravili.notquests.paper.gui.property.IconProperty; +import rocks.gravili.notquests.paper.gui.property.types.StringIconProperty; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.SimpleItem; + +import java.util.*; +import java.util.stream.Collectors; + +public class Button { + private final ButtonType type; + private final Set iconProperties; + private final Set itemKeys; + private final List icons; + + public Button( + ButtonType type, + Set iconProperties, + Set itemKeys + ) { + this.type = type; + this.iconProperties = iconProperties; + this.itemKeys = itemKeys; + this.icons = new ArrayList<>(); + } + + public void registerIcons(Map possibleItems) { + possibleItems.forEach((key, item) -> { + if (itemKeys.contains(key)) { + icons.add(item); + } + }); + } + + public Item buildItem(NotQuests notQuests, GuiContext guiContext) { + switch (type) { + case FORWARD -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new PageForwardItem(itemStackInWrapper); + } + case BACK -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new PageBackItem(itemStackInWrapper); + } + case UP -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new ScrollUpItem(itemStackInWrapper); + } + case DOWN -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new ScrollDownItem(itemStackInWrapper); + } + case PAGED -> { + + } + case TAB -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + var tabIndexPropertyOpt = iconProperties.stream().filter(iconProperty -> iconProperty.getKey().equals("tabindex")).findFirst(); + if (tabIndexPropertyOpt.isEmpty()) { + return new SimpleItem(itemStackInWrapper); + } + if (!(tabIndexPropertyOpt.get().getValue() instanceof StringIconProperty stringIconProperty)) { + return new SimpleItem(itemStackInWrapper); + } + var newTitleProperty = iconProperties.stream().filter(iconProperty -> iconProperty.getKey().equals("tabtitle")).findFirst(); + Component newTitle = null; + if (newTitleProperty.isPresent() && newTitleProperty.get().getValue() instanceof StringIconProperty stringTitleProperty) { + newTitle = notQuests.getLanguageManager().getComponent( + stringTitleProperty.value(), guiContext.getPlayer(), guiContext.getAsObjectArray() + ); + } + return new CustomTabItem( + Integers.parseInt(stringIconProperty.value()), + newTitle, + icons.stream().map(icon -> ItemHelper.assembleItemStack( + icon, notQuests, guiContext + )).collect(Collectors.toList())); + } + case ACTION -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new ActionItem(notQuests, itemStackInWrapper, this, guiContext); + } + default -> { + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new SimpleItem(itemStackInWrapper); + } + } + var itemStackInWrapper = new ItemWrapper(ItemHelper.assembleItemStack(icons.get(0), notQuests, guiContext)); + return new SimpleItem(itemStackInWrapper); + } + + public ButtonType getType() { + return type; + } + + public Set getIconProperties() { + return iconProperties; + } + public Optional getIconProperty(String key) { + return iconProperties.stream().filter(iconProperty -> iconProperty.getKey().equals(key)).findFirst(); + } + + public List getItems() { + return icons; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/ButtonType.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/ButtonType.java new file mode 100644 index 00000000..bc50bbd5 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/ButtonType.java @@ -0,0 +1,13 @@ +package rocks.gravili.notquests.paper.gui.icon; + +public enum ButtonType { + BLANK, + PAGED, + TAB, + ACTION, + BACK, + UP, + DOWN, + FORWARD + +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/Icon.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/Icon.java new file mode 100644 index 00000000..7b16c470 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/Icon.java @@ -0,0 +1,8 @@ +package rocks.gravili.notquests.paper.gui.icon; + +public record Icon( + String material, + String pathToDisplayName, + String pathToLore, + String skullTexture +) { } diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/ItemHelper.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/ItemHelper.java new file mode 100644 index 00000000..ee76753f --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/ItemHelper.java @@ -0,0 +1,127 @@ +package rocks.gravili.notquests.paper.gui.icon; + +import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.ProfileProperty; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import rocks.gravili.notquests.paper.NotQuests; +import rocks.gravili.notquests.paper.gui.GuiContext; + + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class ItemHelper { + + private static final String EMPTY_STRING_EXPRESSION = "EMPTY"; + + public static ItemStack assembleItemStack(Icon icon, NotQuests notQuests, GuiContext guiContext) { + var material = fetchMaterial(icon, guiContext); + + var itemStack = new ItemStack(material); + var itemMeta = itemStack.getItemMeta(); + + var questPlayer = guiContext.getPlayer() != null ? notQuests.getQuestPlayerManager().getActiveQuestPlayer(guiContext.getPlayer().getUniqueId()) : null; + + // Replace placeholders of displayname + if (icon.pathToDisplayName() != null) { + if (icon.pathToDisplayName().equals(EMPTY_STRING_EXPRESSION)) { + itemMeta.displayName(Component.empty()); + } else { + var displayName = notQuests.getLanguageManager().getString( + icon.pathToDisplayName(), guiContext.getPlayer(), guiContext.getAsObjectArray() + ); + + itemMeta.displayName(notQuests.parse(displayName)); + } + } + + // Replace placeholders of lore + if (icon.pathToLore() != null) { + if (icon.pathToLore().equals(EMPTY_STRING_EXPRESSION)) { + itemMeta.lore(List.of(Component.empty())); + } else { + var lore = notQuests.getLanguageManager().getStringList( + icon.pathToLore(), guiContext.getPlayer(), guiContext.getAsObjectArray() + ); + + var newLore = new ArrayList(); + for (String loreString : lore) { + if (loreString.contains("%QUESTREWARDS%")) { + if (questPlayer != null && (guiContext.getQuest() != null || guiContext.getActiveQuest() != null )) { + newLore.add(loreString.replace("%QUESTREWARDS%", "")); + newLore.addAll(notQuests.getQuestManager().getQuestRewardsList( + guiContext.getQuest() == null ? guiContext.getActiveQuest().getQuest() : guiContext.getQuest(), questPlayer)); + } + continue; + } + if (loreString.contains("%QUESTREQUIREMENTS%")) { + if (questPlayer != null && (guiContext.getQuest() != null || guiContext.getActiveQuest() != null )) { + newLore.add(loreString.replace("%QUESTREQUIREMENTS%", "")); + newLore.addAll(notQuests.getQuestManager().getQuestRequirementsList( + guiContext.getQuest() == null ? guiContext.getActiveQuest().getQuest() : guiContext.getQuest(), questPlayer)); + } + continue; + } + if (loreString.contains("%WRAPPEDQUESTDESCRIPTION%")) { + if (guiContext.getQuest() != null || guiContext.getActiveQuest() != null) { + var quest = guiContext.getQuest() == null ? guiContext.getActiveQuest().getQuest() : guiContext.getQuest(); + newLore.add(loreString.replace("%WRAPPEDQUESTDESCRIPTION%", "")); + newLore.addAll(quest.getQuestDescriptionList(notQuests.getConfiguration().guiQuestDescriptionMaxLineLength)); + continue; + } + } + newLore.add(loreString); + } + + + itemMeta.lore(newLore.stream().map(notQuests::parse).collect(Collectors.toList())); + } + } + + // Handle skull textures + if (material == Material.PLAYER_HEAD) { + if (icon.skullTexture() != null) { + var texture = icon.skullTexture(); + SkullMeta meta = (SkullMeta) itemMeta; + PlayerProfile prof = Bukkit.createProfile(UUID.randomUUID(), null); + prof.getProperties().add( + new ProfileProperty( + "textures", + texture + )); + meta.setPlayerProfile(prof); + itemStack.setItemMeta(meta); + } else { + itemStack.setItemMeta(itemMeta); + } + } else { + itemStack.setItemMeta(itemMeta); + } + return itemStack; + } + + public static Material fetchMaterial(Icon item, GuiContext guiContext) { + if (item.material().equals("%QUEST_ITEM_MATERIAL%")) { + if (guiContext.getActiveQuest() != null) { + return guiContext.getActiveQuest().getQuest().getTakeItem().getType(); + } + if (guiContext.getQuest() != null) { + return guiContext.getQuest().getTakeItem().getType(); + } + } else if (item.material().equals("%CATEGORY_ITEM_MATERIAL%")) { + if (guiContext.getCategory() != null) { + return guiContext.getCategory().getGuiItem().getType(); + } + } else { + + return Material.valueOf(item.material()); + } + return Material.STONE; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/SpecialIconType.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/SpecialIconType.java new file mode 100644 index 00000000..0a1b5f96 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/icon/SpecialIconType.java @@ -0,0 +1,11 @@ +package rocks.gravili.notquests.paper.gui.icon; + +public enum SpecialIconType { + DEFAULT, + PLAYER_ACTIVE_QUEST, + PLAYER_AVAILABLE_QUEST, + CATEGORY, + CATEGORY_AVAILABLE_QUEST, + CATEGORY_PLAYER_AVAILABLE_QUEST, + NPC_SHOWN_QUEST +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ActionItem.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ActionItem.java new file mode 100644 index 00000000..01f6ffd8 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ActionItem.java @@ -0,0 +1,104 @@ +package rocks.gravili.notquests.paper.gui.item; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; +import rocks.gravili.notquests.paper.NotQuests; +import rocks.gravili.notquests.paper.gui.GuiContext; +import rocks.gravili.notquests.paper.gui.icon.Button; +import rocks.gravili.notquests.paper.gui.property.types.ListIconProperty; +import rocks.gravili.notquests.paper.structs.QuestPlayer; +import rocks.gravili.notquests.paper.structs.actions.Action; +import rocks.gravili.notquests.paper.structs.conditions.Condition; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.AbstractItem; + +import java.util.ArrayList; +import java.util.List; + +public class ActionItem extends AbstractItem { + + private final NotQuests notQuests; + private final Button icon; + private final GuiContext guiContext; + private final ItemWrapper itemWrapper; + + public ActionItem(NotQuests notQuests, ItemWrapper itemWrapper, Button icon, GuiContext guiContext) { + this.notQuests = notQuests; + this.itemWrapper = itemWrapper; + this.icon = icon; + this.guiContext = guiContext; + } + + + + @Override + public ItemProvider getItemProvider() { + return itemWrapper; + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + + if (notQuests.getConversationManager() == null) { + return; + } + + // get possible actions and replace all placeholders + var actionProperty = icon.getIconProperty("actions"); + if (actionProperty.isEmpty() || !(actionProperty.get().getValue() instanceof ListIconProperty listActionProperty)) { + return; + } + var replacedActionStrings = replaceWithPlaceholders(listActionProperty.value(), player, guiContext); + + // get possible conditions and replace all placeholders + var conditionProperty = icon.getIconProperty("conditions"); + + var questPlayer = notQuests.getQuestPlayerManager().getActiveQuestPlayer(player.getUniqueId()); + var actions = notQuests.getConversationManager().parseActionString(replacedActionStrings); + + List conditions = null; + + if (conditionProperty.isPresent() && actionProperty.get().getValue() instanceof ListIconProperty listConditionProperty) { + var replacedConditionStrings = replaceWithPlaceholders(listConditionProperty.value(), player, guiContext); + conditions = notQuests.getConversationManager().parseConditionsString(replacedConditionStrings); + } + + executeActionsWithConditions(actions, conditions, questPlayer); + } + + public void executeActionsWithConditions(List actions, List conditions, QuestPlayer questPlayer) { + actions.forEach(action -> { + if (conditions != null) { + conditions.forEach(condition -> action.addCondition(condition, false, null, null)); + } + notQuests.getActionManager().executeActionWithConditions(action, questPlayer, questPlayer.getPlayer(), true); + }); + } + + public List replaceWithPlaceholders(List toReplace, Player player, GuiContext guiContext) { + var replacedStrings = new ArrayList(); + + toReplace.forEach(actionString -> { + var replacedString = actionString; + + var languageManager = notQuests.getLanguageManager(); + if (!notQuests.getConfiguration().supportPlaceholderAPIInTranslationStrings || !notQuests.getIntegrationsManager().isPlaceholderAPIEnabled() || player == null) { + replacedString = languageManager.applySpecial( + languageManager.applyInternalPlaceholders(replacedString, player, guiContext.getAsObjectArray()) + ); + } else { + replacedString = languageManager.applySpecial(PlaceholderAPI.setPlaceholders( + player, languageManager.applyInternalPlaceholders( + replacedString, player, guiContext.getAsObjectArray() + )) + ); + } + replacedStrings.add(replacedString); + }); + return replacedStrings; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/CustomTabItem.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/CustomTabItem.java new file mode 100644 index 00000000..1842a4a0 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/CustomTabItem.java @@ -0,0 +1,50 @@ +package rocks.gravili.notquests.paper.gui.item; + +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.inventoryaccess.component.AdventureComponentWrapper; +import xyz.xenondevs.invui.gui.TabGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.controlitem.TabItem; + +import java.util.List; + +public class CustomTabItem extends TabItem { + + private final int tab; + private final List itemStacks; + private final Component newTitle; + public CustomTabItem(int tab, Component newTitle, List itemStacks) { + super(tab); + this.tab = tab; + this.itemStacks = itemStacks; + this.newTitle = newTitle; + } + + @Override + public ItemProvider getItemProvider(TabGui gui) { + var activeTabItem = itemStacks.get(0); + var inactiveTabItem = itemStacks.get(0); + if (itemStacks.size() > 1) { + inactiveTabItem = itemStacks.get(1); + } + if (gui.getCurrentTab() == tab) { + return new ItemWrapper(activeTabItem); + } else { + return new ItemWrapper(inactiveTabItem); + } + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + super.handleClick(clickType, player, event); + var window = getWindows().stream().filter(w -> w.getCurrentViewer().equals(player)).findFirst().get(); + + window.changeTitle(new AdventureComponentWrapper(newTitle)); + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/PageBackItem.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/PageBackItem.java new file mode 100644 index 00000000..4a8e886b --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/PageBackItem.java @@ -0,0 +1,22 @@ +package rocks.gravili.notquests.paper.gui.item; + + +import xyz.xenondevs.invui.gui.PagedGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.controlitem.PageItem; + +public class PageBackItem extends PageItem { + + private ItemWrapper itemWrapper; + + public PageBackItem(ItemWrapper itemWrapper) { + super(false); + this.itemWrapper = itemWrapper; + } + + @Override + public ItemProvider getItemProvider(PagedGui gui) { + return itemWrapper; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/PageForwardItem.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/PageForwardItem.java new file mode 100644 index 00000000..f50c2b76 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/PageForwardItem.java @@ -0,0 +1,20 @@ +package rocks.gravili.notquests.paper.gui.item; + +import xyz.xenondevs.invui.gui.PagedGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.controlitem.PageItem; + +public class PageForwardItem extends PageItem { + private ItemWrapper itemWrapper; + + public PageForwardItem(ItemWrapper itemWrapper) { + super(true); + this.itemWrapper = itemWrapper; + } + + @Override + public ItemProvider getItemProvider(PagedGui gui) { + return itemWrapper; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ScrollDownItem.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ScrollDownItem.java new file mode 100644 index 00000000..375e82e1 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ScrollDownItem.java @@ -0,0 +1,20 @@ +package rocks.gravili.notquests.paper.gui.item; + +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; + +public class ScrollDownItem extends ScrollItem { + + private final ItemWrapper itemWrapper; + public ScrollDownItem(ItemWrapper itemWrapper) { + super(1); + this.itemWrapper = itemWrapper; + } + + @Override + public ItemProvider getItemProvider(ScrollGui gui) { + return itemWrapper; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ScrollUpItem.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ScrollUpItem.java new file mode 100644 index 00000000..bc33a16f --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/item/ScrollUpItem.java @@ -0,0 +1,19 @@ +package rocks.gravili.notquests.paper.gui.item; + +import xyz.xenondevs.invui.gui.ScrollGui; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.ItemWrapper; +import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem; + +public class ScrollUpItem extends ScrollItem { + private final ItemWrapper itemWrapper; + public ScrollUpItem(ItemWrapper itemWrapper) { + super(-1); + this.itemWrapper = itemWrapper; + } + + @Override + public ItemProvider getItemProvider(ScrollGui gui) { + return itemWrapper; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/IconProperty.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/IconProperty.java new file mode 100644 index 00000000..3df9ebb0 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/IconProperty.java @@ -0,0 +1,25 @@ +package rocks.gravili.notquests.paper.gui.property; + +import rocks.gravili.notquests.paper.gui.property.types.BaseIconProperty; + +public class IconProperty { + private final String key; + private final BaseIconProperty value; + + private IconProperty(String key, BaseIconProperty value) { + this.key = key; + this.value = value; + } + + public static IconProperty of(String key, BaseIconProperty value) { + return new IconProperty(key, value); + } + + public String getKey() { + return key; + } + + public BaseIconProperty getValue() { + return value; + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/BaseIconProperty.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/BaseIconProperty.java new file mode 100644 index 00000000..8cb2c542 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/BaseIconProperty.java @@ -0,0 +1,5 @@ +package rocks.gravili.notquests.paper.gui.property.types; + +public interface BaseIconProperty { + +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/ListIconProperty.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/ListIconProperty.java new file mode 100644 index 00000000..25d3fb6e --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/ListIconProperty.java @@ -0,0 +1,10 @@ +package rocks.gravili.notquests.paper.gui.property.types; + +import java.util.List; + +public record ListIconProperty(List value) implements BaseIconProperty { + + public static ListIconProperty of(List value) { + return new ListIconProperty(value); + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/StringIconProperty.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/StringIconProperty.java new file mode 100644 index 00000000..09db4469 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/property/types/StringIconProperty.java @@ -0,0 +1,8 @@ +package rocks.gravili.notquests.paper.gui.property.types; + +public record StringIconProperty(String value) implements BaseIconProperty { + + public static StringIconProperty of(String value) { + return new StringIconProperty(value); + } +} diff --git a/paper/src/main/java/rocks/gravili/notquests/paper/gui/typeserializer/IconTypeSerializer.java b/paper/src/main/java/rocks/gravili/notquests/paper/gui/typeserializer/IconTypeSerializer.java new file mode 100644 index 00000000..781ddbc3 --- /dev/null +++ b/paper/src/main/java/rocks/gravili/notquests/paper/gui/typeserializer/IconTypeSerializer.java @@ -0,0 +1,55 @@ +package rocks.gravili.notquests.paper.gui.typeserializer; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; +import rocks.gravili.notquests.paper.gui.icon.Button; +import rocks.gravili.notquests.paper.gui.icon.ButtonType; +import rocks.gravili.notquests.paper.gui.property.IconProperty; +import rocks.gravili.notquests.paper.gui.property.types.ListIconProperty; +import rocks.gravili.notquests.paper.gui.property.types.StringIconProperty; + +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; + +public class IconTypeSerializer implements TypeSerializer