diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java new file mode 100644 index 0000000..00f8264 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java @@ -0,0 +1,543 @@ +package world.bentobox.challenges.panel.user; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.LevelStatus; +import world.bentobox.challenges.database.object.Challenges; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.TryToComplete; + + +/** + * This is UserGUI class. It contains everything necessary for user to use it. + */ +public class ChallengesGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + /** + * Default constructor that inits panels with minimal requirements, without parent panel. + * + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + */ + public ChallengesGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, world, user, topLabel, permissionPrefix); + this.challengesManager = this.addon.getChallengesManager(); + + this.levelStatusList = this.challengesManager.getChallengeLevelStatus(this.user, this.world); + this.lastSelectedLevel = this.levelStatusList.get(0); + } + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation("challenges.gui.title")); + + // TODO: get last completed level. + + if (this.addon.getChallengesSettings().isFreeChallengesFirst()) + { + this.addFreeChallenges(panelBuilder); + + // Start new row for challenges. + while (panelBuilder.nextSlot() % 9 != 0) + { + panelBuilder.item( + new PanelItemBuilder().icon(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name(" ").build()); + } + } + + this.addChallenges(panelBuilder); + + // Start new row for levels. + while (panelBuilder.nextSlot() % 9 != 0) + { + panelBuilder.item( + new PanelItemBuilder().icon(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name(" ").build()); + } + + this.addChallengeLevels(panelBuilder); + + if (!this.addon.getChallengesSettings().isFreeChallengesFirst()) + { + // Start new row for free challenges. + while (panelBuilder.nextSlot() % 9 != 0) + { + panelBuilder.item( + new PanelItemBuilder().icon(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name(" ").build()); + } + + this.addFreeChallenges(panelBuilder); + } + + panelBuilder.build(); + } + + + /** + * This method adds free challenges to panelBuilder. + * @param panelBuilder where free challenges must be added. + */ + private void addFreeChallenges(PanelBuilder panelBuilder) + { + List freeChallenges = this.challengesManager.getFreeChallenges(this.user, this.world); + final int freeChallengesCount = freeChallenges.size(); + + if (freeChallengesCount > 18) + { + int firstIndex = panelBuilder.nextSlot(); + + if (this.freeChallengeIndex > 0) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name("Previous"). + clickHandler((panel, user1, clickType, slot) -> { + this.freeChallengeIndex--; + this.build(); + return true; + }).build()); + } + + int currentIndex = this.freeChallengeIndex; + + while (panelBuilder.nextSlot() != firstIndex + 18 && currentIndex < freeChallengesCount) + { + panelBuilder.item(this.getChallengeButton(freeChallenges.get(currentIndex++))); + } + + // Check if one challenge is left + if (currentIndex + 1 == freeChallengesCount) + { + panelBuilder.item(this.getChallengeButton(freeChallenges.get(currentIndex))); + } + else if (currentIndex < freeChallengesCount) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name("Next"). + clickHandler((panel, user1, clickType, slot) -> { + this.freeChallengeIndex++; + this.build(); + return true; + }).build()); + } + } + else + { + for (Challenges challenge : freeChallenges) + { + // there are no limitations. Just bunch insert. + panelBuilder.item(this.getChallengeButton(challenge)); + } + } + } + + + /** + * This method adds last selected level challenges to panelBuilder. + * @param panelBuilder where last selected level challenges must be added. + */ + private void addChallenges(PanelBuilder panelBuilder) + { + List challenges = this.challengesManager.getChallenges(this.lastSelectedLevel.getLevel()); + final int challengesCount = challenges.size(); + + if (challengesCount > 18) + { + int firstIndex = panelBuilder.nextSlot(); + + if (this.pageIndex > 0) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name("Previous"). + clickHandler((panel, user1, clickType, slot) -> { + this.pageIndex--; + this.build(); + return true; + }).build()); + } + + int currentIndex = this.pageIndex; + + while (panelBuilder.nextSlot() != firstIndex + 18 && currentIndex < challengesCount) + { + panelBuilder.item(this.getChallengeButton(challenges.get(currentIndex++))); + } + + // Check if one challenge is left + if (currentIndex + 1 == challengesCount) + { + panelBuilder.item(this.getChallengeButton(challenges.get(currentIndex))); + } + else if (currentIndex < challengesCount) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name("Next"). + clickHandler((panel, user1, clickType, slot) -> { + this.pageIndex++; + this.build(); + return true; + }).build()); + } + } + else + { + for (Challenges challenge : challenges) + { + // there are no limitations. Just bunch insert. + panelBuilder.item(this.getChallengeButton(challenge)); + } + } + } + + + /** + * This method adds challenge levels to panelBuilder. + * @param panelBuilder where challenge levels must be added. + */ + private void addChallengeLevels(PanelBuilder panelBuilder) + { + // Clone to avoid creating new level on each build. + List leftLevels = new ArrayList<>(this.levelStatusList); + leftLevels.remove(this.lastSelectedLevel); + + // TODO: Focusing on middle should be awsome. + + final int levelCounts = leftLevels.size(); + + if (levelCounts > 9) + { + int firstIndex = panelBuilder.nextSlot(); + + if (this.levelIndex > 0) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name("Previous"). + clickHandler((panel, user1, clickType, slot) -> { + this.levelIndex--; + this.build(); + return true; + }).build()); + } + + int currentIndex = this.levelIndex; + + while (panelBuilder.nextSlot() != firstIndex + 9 && currentIndex < levelCounts) + { + panelBuilder.item(this.getLevelButton(leftLevels.get(currentIndex++))); + } + + // Check if one challenge is left + if (currentIndex + 1 == levelCounts) + { + panelBuilder.item(this.getLevelButton(leftLevels.get(currentIndex))); + } + else if (currentIndex < levelCounts) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name("Next"). + clickHandler((panel, user1, clickType, slot) -> { + this.levelIndex++; + this.build(); + return true; + }).build()); + } + } + else + { + for (LevelStatus level : leftLevels) + { + // there are no limitations. Just bunch insert. + panelBuilder.item(this.getLevelButton(level)); + } + } + } + + +// --------------------------------------------------------------------- +// Section: Icon building +// --------------------------------------------------------------------- + + + /** + * This method creates given challenges icon that on press tries to complete it. + * @param challenge which icon must be constructed. + * @return PanelItem icon for challenge. + */ + private PanelItem getChallengeButton(Challenges challenge) + { + return new PanelItemBuilder(). + icon(challenge.getIcon()). + name(challenge.getFriendlyName().isEmpty() ? challenge.getUniqueId() : challenge.getFriendlyName()). + description(this.createChallengeDescription(challenge)). + clickHandler((panel, user1, clickType, slot) -> { + new TryToComplete(this.addon, + this.user, + this.challengesManager, + challenge, + this.world, + this.permissionPrefix, + this.topLabel); + return true; + }). + glow(this.challengesManager.isChallengeComplete(this.user, challenge)). + build(); + } + + + /** + * This method creates challenges description by adding all information that is necessary for this challenge. + * @param challenge Which information must be retrieved. + * @return List with strings that contains information about given challenge. + */ + private List createChallengeDescription(Challenges challenge) + { + List result = new ArrayList<>(); + + result.add(this.user.getTranslation("challenges.level", + "[level]", this.challengesManager.getChallengesLevel(challenge))); + + boolean completed = this.challengesManager.isChallengeComplete(this.user, challenge); + + if (completed) + { + result.add(this.user.getTranslation("challenges.complete")); + } + + if (challenge.isRepeatable()) + { + int maxTimes = challenge.getMaxTimes(); + long doneTimes = this.challengesManager.checkChallengeTimes(this.user, challenge); + + if (maxTimes > 0) + { + if (doneTimes < maxTimes) + { + result.add(this.user.getTranslation("challenges.completed-times", + "[donetimes]", String.valueOf(doneTimes), + "[maxtimes]", String.valueOf(maxTimes))); + + // Change value to false, as max count not reached. + completed = false; + } + else + { + result.add(this.user.getTranslation("challenges.maxed-reached", + "[donetimes]", String.valueOf(doneTimes), + "[maxtimes]", String.valueOf(maxTimes))); + } + } + else + { + result.add(this.user.getTranslation("challenges.completed-times", + "[donetimes]", String.valueOf(doneTimes))); + + // Change value to false, as max count not reached. + completed = false; + } + } + + if (!completed) + { + result.addAll(challenge.getDescription()); + + if (challenge.getChallengeType().equals(Challenges.ChallengeType.INVENTORY)) + { + if (challenge.isTakeItems()) + { + result.add(this.user.getTranslation("challenges.item-take-warning")); + } + } + else if (challenge.getChallengeType().equals(Challenges.ChallengeType.ISLAND)) + { + result.add(this.user.getTranslation("challenges.items-closeby")); + + if (challenge.isRemoveEntities() && !challenge.getRequiredEntities().isEmpty()) + { + result.add(this.user.getTranslation("challenges.entities-kill-warning")); + } + + if (challenge.isRemoveBlocks() && !challenge.getRequiredBlocks().isEmpty()) + { + result.add(this.user.getTranslation("challenges.blocks-take-warning")); + } + } + } + + if (completed) + { + result.add(this.user.getTranslation("challenges.not-repeatable")); + } + else + { + result.addAll(this.challengeRewards(challenge)); + } + + return result; + } + + + /** + * This method returns list of strings that contains basic information about challenge rewards. + * @param challenge which reward message must be created. + * @return list of strings that contains rewards message. + */ + private List challengeRewards(Challenges challenge) + { + String rewardText; + double rewardMoney; + int rewardExperience; + + if (!this.challengesManager.isChallengeComplete(this.user, challenge)) + { + rewardText = challenge.getRewardText(); + rewardMoney = challenge.getRewardMoney(); + rewardExperience = challenge.getRewardExp(); + } + else + { + rewardText = challenge.getRepeatRewardText(); + rewardMoney = challenge.getRepeatMoneyReward(); + rewardExperience = challenge.getRepeatExpReward(); + } + + List result = new ArrayList<>(); + + // Add reward text + result.add(rewardText); + + // Add message about reward XP + if (rewardExperience > 0) + { + result.add(this.user.getTranslation("challenges.exp-reward", + "[reward]", Integer.toString(rewardExperience))); + } + + // Add message about reward money + if (this.addon.getPlugin().getSettings().isUseEconomy() && rewardMoney > 0) + { + result.add(this.user.getTranslation("challenges.money-reward", + "[reward]", Double.toString(rewardMoney))); + } + + return result; + } + + + /** + * This method creates button for given level. + * @param level which button must be created. + * @return Button for given level. + */ + private PanelItem getLevelButton(LevelStatus level) + { + // Create a nice name for the level + String name = level.getLevel().getFriendlyName().isEmpty() ? + level.getLevel().getUniqueId() : + level.getLevel().getFriendlyName(); + + ItemStack icon; + List description; + PanelItem.ClickHandler clickHandler; + + if (level.isUnlocked()) + { + icon = level.getLevel().getIcon(); + description = Collections.singletonList( + this.user.getTranslation("challenges.navigation", "[level]", name)); + clickHandler = (panel, user1, clickType, slot) -> { + this.lastSelectedLevel = level; + + // Reset level and page index. + this.levelIndex = 0; + this.pageIndex = 0; + + this.build(); + return true; + }; + } + else + { + icon = new ItemStack(Material.BOOK); + + // This should be safe as first level always should be unlocked. + LevelStatus previousLevel = this.levelStatusList.get(this.levelStatusList.indexOf(level) - 1); + + description = Collections.singletonList( + this.user.getTranslation("challenges.to-complete", + "[challengesToDo]", Integer.toString(previousLevel.getNumberOfChallengesStillToDo()), + "[thisLevel]", previousLevel.getLevel().getFriendlyName())); + + clickHandler = null; + } + + return new PanelItem(icon, name, description, false, clickHandler, false); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This will be used if free challenges are more then 18. + */ + private int freeChallengeIndex = 0; + + /** + * This will be used if levels are more then 9. + */ + private int levelIndex; + + /** + * This list contains all information about level completion in current world. + */ + private List levelStatusList; + + /** + * This indicate last selected level. + */ + private LevelStatus lastSelectedLevel; + + /** + * Challenge Manager object. + */ + private ChallengesManager challengesManager; +}