diff --git a/api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameRule.java b/api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameRule.java index 9fe894aa..28554857 100644 --- a/api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameRule.java +++ b/api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameRule.java @@ -180,10 +180,6 @@ public class GameRule { * The amount of goals to score before the game ends. -1 = not used. */ public static final GameRule SCORE_GOAL = new GameRule<>(Integer.class, "scoreGoal", -1); - /** - * Maximum time in hours since the dungeons specified by other rules were finished. 0 = ignore. - */ - public static final GameRule TIME_LAST_PLAYED_REQUIRED_DUNGEONS = new GameRule<>(Integer.class, "timeLastPlayedRequiredDungeons", 0); /** * When loot may be taken away out of the dungeon again. */ @@ -231,14 +227,6 @@ public class GameRule { } return requirements; }, ArrayList::new); - /** - * One of these Dungeons must be finished ("any" for any dungeon). - */ - public static final GameRule> MUST_FINISH_ONE = new CollectionGameRule<>("mustFinishOne", new ArrayList<>(), ArrayList::new); - /** - * All of these Dungeons must be finished. If you do not want any, leave this empty. - */ - public static final GameRule> MUST_FINISH_ALL = new CollectionGameRule<>("mustFinishAll", new ArrayList<>(), ArrayList::new); /** * This can be used to give rewards. The default implementation does not do this at the moment. */ diff --git a/core/src/main/java/de/erethon/dungeonsxl/DXLModule.java b/core/src/main/java/de/erethon/dungeonsxl/DXLModule.java index 550a347f..a0318eee 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/DXLModule.java +++ b/core/src/main/java/de/erethon/dungeonsxl/DXLModule.java @@ -38,6 +38,7 @@ public class DXLModule implements DungeonModule { public void initRequirements(Registry> requirementRegistry) { requirementRegistry.add("feeLevel", FeeLevelRequirement.class); requirementRegistry.add("feeMoney", FeeMoneyRequirement.class); + requirementRegistry.add("finishedDungeons", FinishedDungeonsRequirement.class); requirementRegistry.add("forbiddenItems", ForbiddenItemsRequirement.class); requirementRegistry.add("groupSize", GroupSizeRequirement.class); requirementRegistry.add("keyItems", KeyItemsRequirement.class); diff --git a/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java b/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java index c01a5afa..e679d4a7 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java +++ b/core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java @@ -26,6 +26,9 @@ import de.erethon.dungeonsxl.util.commons.config.Message; public enum DMessage implements Message { ANNOUNCER_CLICK("announcer.click"), + BUTTON_ACCEPT("button.accept"), + BUTTON_DENY("button.deny"), + BUTTON_OKAY("button.okay"), CMD_ANNOUNCE_HELP("cmd.announce.help"), CMD_BREAK_BREAK_MODE("cmd.break.breakMode"), CMD_BREAK_HELP("cmd.break.help"), @@ -172,9 +175,6 @@ public enum DMessage implements Message { GROUP_REWARD_CHEST("group.rewardChest"), GROUP_UNINVITED_PLAYER("group.uninvitedPlayer"), GROUP_WAVE_FINISHED("group.waveFinished"), - BUTTON_ACCEPT("button.accept"), - BUTTON_DENY("button.deny"), - BUTTON_OKAY("button.okay"), PLAYER_BLOCK_INFO("player.blockInfo"), PLAYER_CHECKPOINT_REACHED("player.checkpointReached"), PLAYER_DEATH("player.death"), @@ -213,6 +213,10 @@ public enum DMessage implements Message { REQUIREMENT_FEE_ITEMS("requirement.feeItems"), REQUIREMENT_FEE_LEVEL("requirement.feeLevel"), REQUIREMENT_FEE_MONEY("requirement.feeMoney"), + REQUIREMENT_FINISHED_DUNGEONS_AND("requirement.finishedDungeons.and"), + REQUIREMENT_FINISHED_DUNGEONS_NAME("requirement.finishedDungeons.name"), + REQUIREMENT_FINISHED_DUNGEONS_OR("requirement.finishedDungeons.or"), + REQUIREMENT_FINISHED_DUNGEONS_WITHIN_TIME("requirement.finishedDungeons.withinTime"), REQUIREMENT_FORBIDDEN_ITEMS("requirement.forbiddenItems"), REQUIREMENT_GROUP_SIZE("requirement.groupSize"), REQUIREMENT_KEY_ITEMS("requirement.keyItems"), diff --git a/core/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java b/core/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java index e1e6300f..90c4fe41 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java +++ b/core/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java @@ -289,60 +289,6 @@ public class DGlobalPlayer implements GlobalPlayer { msgs.forEach(msg -> MessageUtil.sendMessage(player, msg)); } - if (!rules.getState(GameRule.MUST_FINISH_ALL).isEmpty()) { - List finished = new ArrayList<>(rules.getState(GameRule.MUST_FINISH_ALL)); - if (!rules.getState(GameRule.MUST_FINISH_ONE).isEmpty()) { - finished.addAll(rules.getState(GameRule.MUST_FINISH_ONE)); - } - - if (!finished.isEmpty()) { - - long bestTime = 0; - int numOfNeeded = 0; - boolean doneTheOne = false; - - if (finished.size() == rules.getState(GameRule.MUST_FINISH_ALL).size()) { - doneTheOne = true; - } - - for (String played : finished) { - for (String dungeonName : DungeonsXL.MAPS.list()) { - if (new File(DungeonsXL.MAPS, dungeonName).isDirectory()) { - if (played.equalsIgnoreCase(dungeonName) || played.equalsIgnoreCase("any")) { - - Long time = getData().getTimeLastFinished(dungeonName); - if (time != -1) { - if (rules.getState(GameRule.MUST_FINISH_ALL).contains(played)) { - numOfNeeded++; - } else { - doneTheOne = true; - } - if (bestTime < time) { - bestTime = time; - } - } - break; - - } - } - } - } - - if (bestTime == 0) { - fulfilled = false; - - } else if (rules.getState(GameRule.TIME_LAST_PLAYED_REQUIRED_DUNGEONS) != 0) { - if (System.currentTimeMillis() - bestTime > rules.getState(GameRule.TIME_LAST_PLAYED_REQUIRED_DUNGEONS) * (long) 3600000) { - fulfilled = false; - } - } - - if (numOfNeeded < rules.getState(GameRule.MUST_FINISH_ALL).size() || !doneTheOne) { - fulfilled = false; - } - } - } - return fulfilled || DPermission.hasPermission(player, DPermission.IGNORE_REQUIREMENTS); } diff --git a/core/src/main/java/de/erethon/dungeonsxl/player/DPermission.java b/core/src/main/java/de/erethon/dungeonsxl/player/DPermission.java index eb6ff3e2..64ef93e5 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/player/DPermission.java +++ b/core/src/main/java/de/erethon/dungeonsxl/player/DPermission.java @@ -51,7 +51,6 @@ public enum DPermission { GROUP_ADMIN("group.admin", OP, GROUP), HELP("help", TRUE), IGNORE_REQUIREMENTS("ignorerequirements", OP), - IGNORE_TIME_LIMIT("ignoretimelimit", OP), IMPORT("IMPORT", OP), INVITE("invite", OP), INSECURE("insecure", OP), diff --git a/core/src/main/java/de/erethon/dungeonsxl/requirement/FinishedDungeonsRequirement.java b/core/src/main/java/de/erethon/dungeonsxl/requirement/FinishedDungeonsRequirement.java new file mode 100644 index 00000000..ac74b7e6 --- /dev/null +++ b/core/src/main/java/de/erethon/dungeonsxl/requirement/FinishedDungeonsRequirement.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2012-2021 Frank Baumann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.erethon.dungeonsxl.requirement; + +import de.erethon.dungeonsxl.api.DungeonsAPI; +import de.erethon.dungeonsxl.api.Requirement; +import de.erethon.dungeonsxl.config.DMessage; +import de.erethon.dungeonsxl.player.DGlobalPlayer; +import de.erethon.dungeonsxl.player.DPermission; +import de.erethon.dungeonsxl.player.DPlayerData; +import de.erethon.dungeonsxl.util.commons.misc.NumberUtil; +import de.erethon.dungeonsxl.util.commons.misc.SimpleDateUtil; +import java.util.ArrayList; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +/** + * The dungeons that need to be finished before this one may be played. + * + * @author Daniel Saukel + */ +public class FinishedDungeonsRequirement implements Requirement { + + private static final long HOUR_IN_MILLIS = 3600000L; + + private class DungeonAndTime { + String dungeon; + double time = Double.NaN; + + @Override + public String toString() { + return dungeon + (!Double.isNaN(time) ? " " + DMessage.REQUIREMENT_FINISHED_DUNGEONS_WITHIN_TIME.getMessage(SimpleDateUtil.decimalToSexagesimalTime(time, 2)) : ""); + } + } + + private DungeonsAPI api; + + public FinishedDungeonsRequirement(DungeonsAPI api) { + this.api = api; + } + + /* + * finishedDungeons: # all of: + * - 7:vdf # vdf within the last 7 hours + * - sku/test # one of sku and test + */ + private List> dungeons; + + @Override + public void setup(ConfigurationSection config) { + List entries = config.getStringList("finishedDungeons"); + dungeons = new ArrayList<>(entries.size()); + for (String entry : entries) { + List alternatives = new ArrayList<>(); + for (String string : entry.split("/")) { + String[] split = string.split(":"); + DungeonAndTime dat = new DungeonAndTime(); + if (split.length > 1) { + dat.time = NumberUtil.parseDouble(split[0], Double.NaN); + dat.dungeon = split[1]; + } else { + dat.dungeon = split[0]; + } + alternatives.add(dat); + } + dungeons.add(alternatives); + } + } + + @Override + public boolean check(Player player) { + DPlayerData data = ((DGlobalPlayer) api.getPlayerCache().get(player)).getData(); + allOf: + for (List dats : dungeons) { + oneOf: + for (DungeonAndTime dat : dats) { + if (Double.isNaN(dat.time)) { + if (data.getTimeLastFinished(dat.dungeon) != -1) { + continue allOf; + } + } else if (data.getTimeLastFinished(dat.dungeon) + dat.time * HOUR_IN_MILLIS >= System.currentTimeMillis()) { + continue allOf; + } + return false; + } + } + return true; + } + + @Override + public BaseComponent[] getCheckMessage(Player player) { + DPlayerData data = ((DGlobalPlayer) api.getPlayerCache().get(player)).getData(); + ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_FINISHED_DUNGEONS_NAME.getMessage() + ":\n").color(ChatColor.GOLD); + boolean firstAnd = true; + for (List dats : dungeons) { + // GREEN if the dungeon is finished within the timeframe + // WHITE if the dungeon is not finished, but part of a list where to have another one finished is sufficient + // RED if no dungeon of the list is finished within the timeframe + List finished = new ArrayList<>(); + List notFinished = new ArrayList<>(); + for (DungeonAndTime dat : dats) { + if ((Double.isNaN(dat.time) && data.getTimeLastFinished(dat.dungeon) != -1) + || !Double.isNaN(dat.time) && data.getTimeLastFinished(dat.dungeon) + dat.time * HOUR_IN_MILLIS + >= System.currentTimeMillis()) { + finished.add(dat); + } else { + notFinished.add(dat); + } + } + if (!firstAnd) { + builder.append(";\n" + DMessage.REQUIREMENT_FINISHED_DUNGEONS_AND.getMessage() + " ").color(ChatColor.GOLD); + } else { + firstAnd = false; + } + boolean firstOr = true; + for (DungeonAndTime dat : finished) { + if (!firstOr) { + builder.append("\n" + DMessage.REQUIREMENT_FINISHED_DUNGEONS_OR.getMessage() + " ").color(ChatColor.GOLD); + } else { + firstOr = false; + } + builder.append(dat.toString()).color(ChatColor.GREEN); + } + for (DungeonAndTime dat : notFinished) { + if (!firstOr) { + builder.append("\n" + DMessage.REQUIREMENT_FINISHED_DUNGEONS_OR.getMessage() + " ").color(ChatColor.GOLD); + } else { + firstOr = false; + } + builder.append(dat.toString()).color(finished.isEmpty() ? ChatColor.DARK_RED : ChatColor.WHITE); + } + } + return builder.create(); + } + + @Override + public void demand(Player player) { + } + +} diff --git a/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceFinishRequirement.java b/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceFinishRequirement.java index b50c3926..7e4f5140 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceFinishRequirement.java +++ b/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceFinishRequirement.java @@ -20,7 +20,6 @@ import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; -import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.commons.misc.SimpleDateUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; @@ -35,7 +34,7 @@ import org.bukkit.entity.Player; */ public class TimeSinceFinishRequirement implements Requirement { - private static final long MILLIS_TO_HOURS = 3600000L; + private static final long HOUR_IN_MILLIS = 3600000L; private DungeonsAPI api; @@ -52,21 +51,15 @@ public class TimeSinceFinishRequirement implements Requirement { @Override public boolean check(Player player) { - if (DPermission.hasPermission(player, DPermission.IGNORE_TIME_LIMIT)) { - return true; - } DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); return (globalPlayer.getData().getTimeLastFinished(globalPlayer.getGroup().getDungeon().getName()) - + time * MILLIS_TO_HOURS) < System.currentTimeMillis(); + + time * HOUR_IN_MILLIS) < System.currentTimeMillis(); } @Override public BaseComponent[] getCheckMessage(Player player) { - int hours = (int) time; - int minutes = (int) Math.round((time - hours) * 60); - String timeFormatted = hours + ":" + (minutes < 10 ? 0 : "") + minutes; ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_TIME_SINCE_FINISH - .getMessage(timeFormatted) + ": ").color(ChatColor.GOLD); + .getMessage(SimpleDateUtil.decimalToSexagesimalTime(time, 2)) + ":\n").color(ChatColor.GOLD); DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); String dungeonName = globalPlayer.getGroup().getDungeon().getName(); @@ -74,7 +67,7 @@ public class TimeSinceFinishRequirement implements Requirement { if (lastTime == -1) { builder.append(DMessage.REQUIREMENT_TIME_SINCE_NEVER.getMessage()).color(ChatColor.GREEN); } else { - ChatColor color = lastTime + time * MILLIS_TO_HOURS < System.currentTimeMillis() ? ChatColor.GREEN : ChatColor.DARK_RED; + ChatColor color = lastTime + time * HOUR_IN_MILLIS < System.currentTimeMillis() ? ChatColor.GREEN : ChatColor.DARK_RED; builder.append(SimpleDateUtil.ddMMMMyyyyhhmmss(lastTime)).color(color); } diff --git a/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceStartRequirement.java b/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceStartRequirement.java index 9a6ba121..f72c28f1 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceStartRequirement.java +++ b/core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceStartRequirement.java @@ -20,7 +20,6 @@ import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; -import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.commons.misc.SimpleDateUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; @@ -35,7 +34,7 @@ import org.bukkit.entity.Player; */ public class TimeSinceStartRequirement implements Requirement { - private static final long MILLIS_TO_HOURS = 3600000L; + private static final long HOUR_IN_MILLIS = 3600000L; private DungeonsAPI api; @@ -52,21 +51,15 @@ public class TimeSinceStartRequirement implements Requirement { @Override public boolean check(Player player) { - if (DPermission.hasPermission(player, DPermission.IGNORE_TIME_LIMIT)) { - return true; - } DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); return (globalPlayer.getData().getTimeLastStarted(globalPlayer.getGroup().getDungeon().getName()) - + time * MILLIS_TO_HOURS) < System.currentTimeMillis(); + + time * HOUR_IN_MILLIS) < System.currentTimeMillis(); } @Override public BaseComponent[] getCheckMessage(Player player) { - int hours = (int) time; - int minutes = (int) Math.round((time - hours) * 60); - String timeFormatted = hours + ":" + (minutes < 10 ? 0 : "") + minutes; ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_TIME_SINCE_START - .getMessage(timeFormatted) + ": ").color(ChatColor.GOLD); + .getMessage(SimpleDateUtil.decimalToSexagesimalTime(time, 2)) + ":\n").color(ChatColor.GOLD); DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); String dungeonName = globalPlayer.getGroup().getDungeon().getName(); @@ -74,7 +67,7 @@ public class TimeSinceStartRequirement implements Requirement { if (lastTime == -1) { builder.append(DMessage.REQUIREMENT_TIME_SINCE_NEVER.getMessage()).color(ChatColor.GREEN); } else { - ChatColor color = lastTime + time * MILLIS_TO_HOURS < System.currentTimeMillis() ? ChatColor.GREEN : ChatColor.DARK_RED; + ChatColor color = lastTime + time * HOUR_IN_MILLIS < System.currentTimeMillis() ? ChatColor.GREEN : ChatColor.DARK_RED; builder.append(SimpleDateUtil.ddMMMMyyyyhhmmss(lastTime)).color(color); } diff --git a/core/src/main/java/de/erethon/dungeonsxl/world/block/RewardChest.java b/core/src/main/java/de/erethon/dungeonsxl/world/block/RewardChest.java index 0e5388d7..b64ad291 100644 --- a/core/src/main/java/de/erethon/dungeonsxl/world/block/RewardChest.java +++ b/core/src/main/java/de/erethon/dungeonsxl/world/block/RewardChest.java @@ -194,7 +194,7 @@ public class RewardChest extends GameBlock { for (Player player : group.getMembers().getOnlinePlayers()) { DGamePlayer dPlayer = (DGamePlayer) api.getPlayerCache().getGamePlayer(player); - if (dPlayer == null || !dPlayer.canLoot(game.getDungeon())) { + if (!dPlayer.canLoot(game.getDungeon())) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_REWARDS_TIME.getMessage(SimpleDateUtil.ddMMyyyyhhmm(dPlayer.getTimeNextLoot(game.getDungeon())))); continue; } diff --git a/core/src/main/resources/languages/english.yml b/core/src/main/resources/languages/english.yml index 961bd124..4246a4d8 100644 --- a/core/src/main/resources/languages/english.yml +++ b/core/src/main/resources/languages/english.yml @@ -229,6 +229,11 @@ requirement: feeItems: "Items" feeLevel: "Levels" feeMoney: "Money" + finishedDungeons: + and: "and" + name: "Finished dungeons" + or: "or" + withinTime: "within the last &v1 hours" forbiddenItems: "Forbidden items" groupSize: "Group size" keyItems: "Keys" diff --git a/core/src/main/resources/languages/french.yml b/core/src/main/resources/languages/french.yml index 3734868d..3b53e092 100644 --- a/core/src/main/resources/languages/french.yml +++ b/core/src/main/resources/languages/french.yml @@ -229,6 +229,11 @@ requirement: feeItems: "Items" feeLevel: "Niveaux" feeMoney: "Argent" + finishedDungeons: + and: "et" + name: "Donjons complétés" + or: "ou" + withinTime: "au cours des &v1 dernières heurs" forbiddenItems: "Objets défendus" groupSize: "Quantité des joueurs" keyItems: "Clés" diff --git a/core/src/main/resources/languages/german.yml b/core/src/main/resources/languages/german.yml index 1e0567bc..8a18de04 100644 --- a/core/src/main/resources/languages/german.yml +++ b/core/src/main/resources/languages/german.yml @@ -229,6 +229,11 @@ requirement: feeItems: "Items" feeLevel: "Level" feeMoney: "Geld" + finishedDungeons: + and: "und" + name: "Abgeschlossene Dungeons" + or: "oder" + withinTime: "innerhalb der letzten &v1 Stunden" forbiddenItems: "Verbotene Items" groupSize: "Gruppengröße" keyItems: "Schlüssel"