diff --git a/pom.xml b/pom.xml index 08309c7..107a920 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ net.knarcraft knarlib - 1.2.3 + 1.2.3-SNAPSHOT compile diff --git a/src/main/java/net/knarcraft/minigames/MiniGames.java b/src/main/java/net/knarcraft/minigames/MiniGames.java index 1e72037..40e97c7 100644 --- a/src/main/java/net/knarcraft/minigames/MiniGames.java +++ b/src/main/java/net/knarcraft/minigames/MiniGames.java @@ -29,9 +29,12 @@ import net.knarcraft.minigames.arena.reward.CommandReward; import net.knarcraft.minigames.arena.reward.EconomyReward; import net.knarcraft.minigames.arena.reward.ItemReward; import net.knarcraft.minigames.arena.reward.PermissionReward; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.command.LeaveArenaCommand; import net.knarcraft.minigames.command.MenuCommand; import net.knarcraft.minigames.command.ReloadCommand; +import net.knarcraft.minigames.command.SetArenaRewardCommand; +import net.knarcraft.minigames.command.SetArenaRewardTabCompleter; import net.knarcraft.minigames.command.dropper.CreateDropperArenaCommand; import net.knarcraft.minigames.command.dropper.DropperGroupListCommand; import net.knarcraft.minigames.command.dropper.DropperGroupSetCommand; @@ -276,6 +279,7 @@ public final class MiniGames extends JavaPlugin { ConfigurationSerialization.registerClass(EconomyReward.class); ConfigurationSerialization.registerClass(ItemReward.class); ConfigurationSerialization.registerClass(PermissionReward.class); + ConfigurationSerialization.registerClass(RewardCondition.class); } @Override @@ -386,6 +390,7 @@ public final class MiniGames extends JavaPlugin { registerCommand("miniGamesReload", new ReloadCommand(), null); registerCommand("miniGamesLeave", new LeaveArenaCommand(), null); registerCommand("miniGamesMenu", new MenuCommand(), null); + registerCommand("miniGamesReward", new SetArenaRewardCommand(), new SetArenaRewardTabCompleter()); registerDropperCommands(); registerParkourCommands(); diff --git a/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java index 16362fb..74c79b1 100644 --- a/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java +++ b/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java @@ -2,9 +2,12 @@ package net.knarcraft.minigames.arena; import net.knarcraft.knarlib.formatting.StringFormatter; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.config.MiniGameMessage; import net.knarcraft.minigames.property.RecordResult; +import net.knarcraft.minigames.property.RecordType; import net.knarcraft.minigames.util.PlayerTeleporter; +import net.knarcraft.minigames.util.RewardHelper; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -59,9 +62,9 @@ public abstract class AbstractArenaSession implements ArenaSession { * Announces a record set by this player * * @param recordResult

The result of the record

- * @param type

The type of record set (time or deaths)

+ * @param recordType

The type of record set (time or deaths)

*/ - protected void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) { + protected void announceRecord(@NotNull RecordResult recordResult, @NotNull RecordType recordType) { if (recordResult == RecordResult.NONE) { return; } @@ -75,11 +78,15 @@ public abstract class AbstractArenaSession implements ArenaSession { default -> throw new IllegalStateException("Unexpected value: " + recordResult); }; StringFormatter stringFormatter = MiniGames.getInstance().getStringFormatter(); - String recordInfo = stringFormatter.replacePlaceholder(recordInfoMiniGameMessage, "{recordType}", type); + String recordInfo = stringFormatter.replacePlaceholder(recordInfoMiniGameMessage, "{recordType}", + recordType.name().toLowerCase().replace("_", " ")); stringFormatter.displaySuccessMessage(player, stringFormatter.replacePlaceholders( MiniGameMessage.SUCCESS_RECORD_ACHIEVED, new String[]{"{gameMode}", "{recordInfo}"}, new String[]{gameModeString, recordInfo})); + + // Reward the player + rewardRecord(recordResult, recordType); } /** @@ -88,8 +95,32 @@ public abstract class AbstractArenaSession implements ArenaSession { protected void registerRecord() { ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode); long timeElapsed = System.currentTimeMillis() - this.startTime; - announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time"); - announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths"); + announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), RecordType.TIME); + announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), RecordType.DEATHS); + } + + /** + * Rewards the specified achieved record + * + * @param recordResult

The result of the record achieved

+ * @param recordType

The type of record achieved

+ */ + protected void rewardRecord(RecordResult recordResult, RecordType recordType) { + RewardCondition condition = null; + if (recordResult == RecordResult.WORLD_RECORD) { + if (recordType == RecordType.DEATHS) { + condition = RewardCondition.GLOBAL_DEATH_RECORD; + } else if (recordType == RecordType.TIME) { + condition = RewardCondition.GLOBAL_TIME_RECORD; + } + } else if (recordResult == RecordResult.PERSONAL_BEST) { + if (recordType == RecordType.DEATHS) { + condition = RewardCondition.PERSONAL_DEATH_RECORD; + } else if (recordType == RecordType.TIME) { + condition = RewardCondition.PERSONAL_TIME_RECORD; + } + } + RewardHelper.grantRewards(this.player, this.arena.getRewards(condition)); } /** diff --git a/src/main/java/net/knarcraft/minigames/arena/Arena.java b/src/main/java/net/knarcraft/minigames/arena/Arena.java index ad15e81..ecb4574 100644 --- a/src/main/java/net/knarcraft/minigames/arena/Arena.java +++ b/src/main/java/net/knarcraft/minigames/arena/Arena.java @@ -1,10 +1,13 @@ package net.knarcraft.minigames.arena; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; import org.bukkit.Location; import org.bukkit.block.Block; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Set; import java.util.UUID; /** @@ -91,4 +94,27 @@ public interface Arena { */ @Nullable Location getExitLocation(); + /** + * Adds a reward to this arena + * + * @param rewardCondition

The condition for the reward to be granted

+ * @param reward

The reward to be granted

+ */ + void addReward(@NotNull RewardCondition rewardCondition, @NotNull Reward reward); + + /** + * Clears this arena's rewards for the given condition + * + * @param rewardCondition

The reward condition to clear all rewards for

+ */ + void clearRewards(@NotNull RewardCondition rewardCondition); + + /** + * Gets all rewards for the given reward condition + * + * @param rewardCondition

The condition to get the rewards for

+ * @return

All rewards

+ */ + @NotNull Set getRewards(RewardCondition rewardCondition); + } diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java index ba3d627..e4200fb 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java @@ -4,6 +4,8 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.Arena; import net.knarcraft.minigames.arena.ArenaGameMode; import net.knarcraft.minigames.arena.ArenaRecordsRegistry; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.config.DropperConfiguration; import net.knarcraft.minigames.util.DropperArenaStorageHelper; import net.knarcraft.minigames.util.StringSanitizer; @@ -15,7 +17,9 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.UUID; import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid; @@ -70,6 +74,8 @@ public class DropperArena implements Arena { private final DropperArenaHandler dropperArenaHandler; + private Map> rewards = new HashMap<>(); + private static final DropperConfiguration dropperConfiguration = MiniGames.getInstance().getDropperConfiguration(); /** @@ -82,13 +88,14 @@ public class DropperArena implements Arena { * @param playerVerticalVelocity

The velocity to use for players' vertical velocity

* @param playerHorizontalVelocity

The velocity to use for players' horizontal velocity (-1 to 1)

* @param winBlockType

The material of the block players have to hit to win this dropper arena

+ * @param rewards

The rewards given by this arena

* @param dropperArenaData

The arena data keeping track of which players have done what in this arena

* @param arenaHandler

The arena handler used for saving any changes

*/ public DropperArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation, @Nullable Location exitLocation, double playerVerticalVelocity, float playerHorizontalVelocity, - @NotNull Material winBlockType, @NotNull DropperArenaData dropperArenaData, - @NotNull DropperArenaHandler arenaHandler) { + @NotNull Material winBlockType, @NotNull Map> rewards, + @NotNull DropperArenaData dropperArenaData, @NotNull DropperArenaHandler arenaHandler) { this.arenaId = arenaId; this.arenaName = arenaName; this.spawnLocation = spawnLocation; @@ -98,6 +105,7 @@ public class DropperArena implements Arena { this.winBlockType = winBlockType; this.dropperArenaData = dropperArenaData; this.dropperArenaHandler = arenaHandler; + this.rewards = rewards; } /** @@ -155,6 +163,28 @@ public class DropperArena implements Arena { return this.exitLocation != null ? this.exitLocation.clone() : null; } + @Override + public void addReward(@NotNull RewardCondition rewardCondition, @NotNull Reward reward) { + this.rewards.computeIfAbsent(rewardCondition, k -> new HashSet<>()); + this.rewards.get(rewardCondition).add(reward); + this.dropperArenaHandler.saveArenas(); + } + + @Override + public void clearRewards(@NotNull RewardCondition rewardCondition) { + this.rewards.remove(rewardCondition); + this.dropperArenaHandler.saveArenas(); + } + + @Override + public @NotNull Set getRewards(RewardCondition rewardCondition) { + if (this.rewards.containsKey(rewardCondition) && this.rewards.get(rewardCondition) != null) { + return this.rewards.get(rewardCondition); + } else { + return new HashSet<>(); + } + } + /** * Gets the vertical velocity for players in this arena * @@ -237,7 +267,7 @@ public class DropperArena implements Arena { return false; } else { this.spawnLocation = newLocation; - dropperArenaHandler.saveArenas(); + this.dropperArenaHandler.saveArenas(); return true; } } @@ -253,7 +283,7 @@ public class DropperArena implements Arena { return false; } else { this.exitLocation = newLocation; - dropperArenaHandler.saveArenas(); + this.dropperArenaHandler.saveArenas(); return true; } } @@ -269,8 +299,8 @@ public class DropperArena implements Arena { String oldName = this.getArenaNameSanitized(); this.arenaName = arenaName; // Update the arena lookup map to make sure the new name can be used immediately - dropperArenaHandler.updateLookupName(oldName, this.getArenaNameSanitized()); - dropperArenaHandler.saveArenas(); + this.dropperArenaHandler.updateLookupName(oldName, this.getArenaNameSanitized()); + this.dropperArenaHandler.saveArenas(); return true; } else { return false; @@ -290,7 +320,7 @@ public class DropperArena implements Arena { return false; } else { this.winBlockType = material; - dropperArenaHandler.saveArenas(); + this.dropperArenaHandler.saveArenas(); return true; } } @@ -308,7 +338,7 @@ public class DropperArena implements Arena { return false; } else { this.playerHorizontalVelocity = horizontalVelocity; - dropperArenaHandler.saveArenas(); + this.dropperArenaHandler.saveArenas(); return true; } } @@ -324,7 +354,7 @@ public class DropperArena implements Arena { return false; } else { this.playerVerticalVelocity = verticalVelocity; - dropperArenaHandler.saveArenas(); + this.dropperArenaHandler.saveArenas(); return true; } } diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java index de57f88..b63bffe 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java @@ -4,10 +4,12 @@ import net.knarcraft.knarlib.formatting.StringFormatter; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.AbstractArenaSession; import net.knarcraft.minigames.arena.PlayerEntryState; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.config.MiniGameMessage; import net.knarcraft.minigames.gui.ArenaGUI; import net.knarcraft.minigames.gui.DropperGUI; import net.knarcraft.minigames.util.PlayerTeleporter; +import net.knarcraft.minigames.util.RewardHelper; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -98,8 +100,10 @@ public class DropperArenaSession extends AbstractArenaSession { // Mark the arena as cleared if (this.arena.getData().setCompleted(this.gameMode, this.player)) { stringFormatter.displaySuccessMessage(this.player, MiniGameMessage.SUCCESS_ARENA_FIRST_CLEAR); + RewardHelper.grantRewards(this.player, this.arena.getRewards(RewardCondition.FIRST_WIN)); } stringFormatter.displaySuccessMessage(this.player, MiniGameMessage.SUCCESS_ARENA_WIN); + RewardHelper.grantRewards(this.player, this.arena.getRewards(RewardCondition.WIN)); // Teleport the player out of the arena teleportToExit(false); diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java index c8299a8..b79db3e 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java @@ -43,9 +43,14 @@ public enum DropperArenaStorageKey { WIN_BLOCK_TYPE("winBlockType"), /** - * The hey for this arena's data + * The key for this arena's data */ DATA("arenaData"), + + /** + * The key for this arena's rewards + */ + REWARDS("rewards"), ; private final @NotNull String key; diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java index 3d05507..1cbd678 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java @@ -5,6 +5,8 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.Arena; import net.knarcraft.minigames.arena.ArenaGameMode; import net.knarcraft.minigames.arena.ArenaRecordsRegistry; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.util.ParkourArenaStorageHelper; import net.knarcraft.minigames.util.StringSanitizer; import org.bukkit.Location; @@ -82,21 +84,27 @@ public class ParkourArena implements Arena { private final @NotNull ParkourArenaHandler parkourArenaHandler; + private Map> rewards = new HashMap<>(); + /** * Instantiates a new parkour arena * - * @param arenaId

The id of the arena

- * @param arenaName

The name of the arena

- * @param spawnLocation

The location players spawn in when entering the arena

- * @param exitLocation

The location the players are teleported to when exiting the arena, or null

- * @param winBlockType

The material of the block players have to hit to win this parkour arena

- * @param winLocation

The location a player has to reach to win this arena

- * @param parkourArenaData

The arena data keeping track of which players have done what in this arena

- * @param arenaHandler

The arena handler used for saving any changes

+ * @param arenaId

The id of the arena

+ * @param arenaName

The name of the arena

+ * @param spawnLocation

The location players spawn in when entering the arena

+ * @param exitLocation

The location the players are teleported to when exiting the arena, or null

+ * @param winBlockType

The material of the block players have to hit to win this parkour arena

+ * @param winLocation

The location a player has to reach to win this arena

+ * @param killPlaneBlockNames

The names of the type of blocks

+ * @param checkpoints

The checkpoints set for this arena

+ * @param rewards

The rewards given by this arena

+ * @param parkourArenaData

The arena data keeping track of which players have done what in this arena

+ * @param arenaHandler

The arena handler used for saving any changes

*/ public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation, @Nullable Location exitLocation, @NotNull Material winBlockType, @Nullable Location winLocation, @Nullable Set killPlaneBlockNames, @NotNull List checkpoints, + @NotNull Map> rewards, @NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) { this.arenaId = arenaId; this.arenaName = arenaName; @@ -110,6 +118,7 @@ public class ParkourArena implements Arena { this.checkpoints = checkpoints; this.parkourArenaData = parkourArenaData; this.parkourArenaHandler = arenaHandler; + this.rewards = rewards; } /** @@ -167,6 +176,28 @@ public class ParkourArena implements Arena { return this.exitLocation; } + @Override + public void addReward(@NotNull RewardCondition rewardCondition, @NotNull Reward reward) { + this.rewards.computeIfAbsent(rewardCondition, k -> new HashSet<>()); + this.rewards.get(rewardCondition).add(reward); + this.parkourArenaHandler.saveArenas(); + } + + @Override + public void clearRewards(@NotNull RewardCondition rewardCondition) { + this.rewards.remove(rewardCondition); + this.parkourArenaHandler.saveArenas(); + } + + @Override + public @NotNull Set getRewards(RewardCondition rewardCondition) { + if (this.rewards.containsKey(rewardCondition)) { + return this.rewards.get(rewardCondition); + } else { + return new HashSet<>(); + } + } + /** * Gets the type of block a player has to hit to win this arena * diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java index 2c3c6e7..5b8d442 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java @@ -1,12 +1,15 @@ package net.knarcraft.minigames.arena.parkour; +import net.knarcraft.knarlib.formatting.StringFormatter; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.AbstractArenaSession; import net.knarcraft.minigames.arena.PlayerEntryState; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.config.MiniGameMessage; import net.knarcraft.minigames.gui.ArenaGUI; import net.knarcraft.minigames.gui.ParkourGUI; import net.knarcraft.minigames.util.PlayerTeleporter; +import net.knarcraft.minigames.util.RewardHelper; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -87,13 +90,15 @@ public class ParkourArenaSession extends AbstractArenaSession { registerRecord(); } + StringFormatter stringFormatter = MiniGames.getInstance().getStringFormatter(); + // Mark the arena as cleared if (this.arena.getData().setCompleted(this.gameMode, this.player)) { - MiniGames.getInstance().getStringFormatter().displaySuccessMessage(this.player, - MiniGameMessage.SUCCESS_ARENA_FIRST_CLEAR); + stringFormatter.displaySuccessMessage(this.player, MiniGameMessage.SUCCESS_ARENA_FIRST_CLEAR); + RewardHelper.grantRewards(this.player, this.arena.getRewards(RewardCondition.FIRST_WIN)); } - MiniGames.getInstance().getStringFormatter().displaySuccessMessage(this.player, - MiniGameMessage.SUCCESS_ARENA_WIN); + stringFormatter.displaySuccessMessage(this.player, MiniGameMessage.SUCCESS_ARENA_WIN); + RewardHelper.grantRewards(this.player, this.arena.getRewards(RewardCondition.WIN)); // Teleport the player out of the arena teleportToExit(false); diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java index 45a8dce..aaca647 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java @@ -51,6 +51,11 @@ public enum ParkourArenaStorageKey { * The hey for this arena's data */ DATA("arenaData"), + + /** + * The key for this arena's rewards + */ + REWARDS("rewards"), ; private final @NotNull String key; diff --git a/src/main/java/net/knarcraft/minigames/arena/reward/RewardCondition.java b/src/main/java/net/knarcraft/minigames/arena/reward/RewardCondition.java new file mode 100644 index 0000000..4c8693e --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/arena/reward/RewardCondition.java @@ -0,0 +1,83 @@ +package net.knarcraft.minigames.arena.reward; + +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * The condition for granting a reward + */ +public enum RewardCondition implements ConfigurationSerializable { + + /** + * The reward is granted each time the player wins/clears the arena + */ + WIN, + + /** + * The reward is granted the first time the player wins/clears the arena + */ + FIRST_WIN, + + /** + * The reward is granted if the player beats their personal least deaths record + */ + PERSONAL_DEATH_RECORD, + + /** + * The reward is granted if the player beats their personal least time record + */ + PERSONAL_TIME_RECORD, + + /** + * The reward is granted if the player beats the global least deaths record + */ + GLOBAL_DEATH_RECORD, + + /** + * The reward is granted if the player beats the global least time record + */ + GLOBAL_TIME_RECORD, + ; + + /** + * Gets a reward condition from the given string + * + * @param condition

The string specifying a reward condition

+ * @return

The matching reward condition, or null if not found

+ */ + public static @Nullable RewardCondition getFromString(@NotNull String condition) { + for (RewardCondition rewardCondition : RewardCondition.values()) { + if (rewardCondition.name().equalsIgnoreCase(condition.replace("-", "_"))) { + return rewardCondition; + } + } + + return null; + } + + @NotNull + @Override + public Map serialize() { + Map data = new HashMap<>(); + data.put("condition", this.name()); + return data; + } + + /** + * Deserializes a reward condition from the given data + * + * @param data

The data to deserialize

+ * @return

The deserialized reward condition

+ */ + @SuppressWarnings({"unused"}) + public static @NotNull RewardCondition deserialize(@NotNull Map data) { + RewardCondition rewardCondition = getFromString(String.valueOf(data.get("condition"))); + return Objects.requireNonNullElse(rewardCondition, RewardCondition.FIRST_WIN); + } + +} diff --git a/src/main/java/net/knarcraft/minigames/arena/reward/RewardType.java b/src/main/java/net/knarcraft/minigames/arena/reward/RewardType.java new file mode 100644 index 0000000..0da99c9 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/arena/reward/RewardType.java @@ -0,0 +1,70 @@ +package net.knarcraft.minigames.arena.reward; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The type of a specific reward + */ +public enum RewardType { + + /** + * A command reward + */ + COMMAND(CommandReward.class), + + /** + * An economy reward + */ + ECONOMY(EconomyReward.class), + + /** + * An item reward + */ + ITEM(ItemReward.class), + + /** + * A permission reward + */ + PERMISSION(PermissionReward.class), + ; + + private final Class classType; + + RewardType(Class classType) { + this.classType = classType; + } + + /** + * Gets the type of reward the given object represents + * + * @param object

A reward object

+ * @return

The reward type of the given object, or null if not recognized

+ */ + public static @Nullable RewardType getFromObject(@NotNull K object) { + for (RewardType rewardType : RewardType.values()) { + if (object.getClass() == rewardType.classType) { + return rewardType; + } + } + + return null; + } + + /** + * Gets a reward type from the given string + * + * @param condition

The string specifying a reward type

+ * @return

The matching reward type, or null if not found

+ */ + public static RewardType getFromString(@NotNull String condition) { + for (RewardType rewardType : RewardType.values()) { + if (rewardType.name().equalsIgnoreCase(condition.replace("-", "_"))) { + return rewardType; + } + } + + return null; + } + +} diff --git a/src/main/java/net/knarcraft/minigames/command/SetArenaRewardCommand.java b/src/main/java/net/knarcraft/minigames/command/SetArenaRewardCommand.java new file mode 100644 index 0000000..10fe008 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/command/SetArenaRewardCommand.java @@ -0,0 +1,90 @@ +package net.knarcraft.minigames.command; + +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.Arena; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; +import net.knarcraft.minigames.config.MiniGameMessage; +import net.knarcraft.minigames.util.RewardHelper; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +/** + * The command used for setting arena rewards + */ +public class SetArenaRewardCommand implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] arguments) { + if (!(commandSender instanceof Player player)) { + MiniGames.getInstance().getStringFormatter().displayErrorMessage(commandSender, + MiniGameMessage.ERROR_PLAYER_ONLY); + return false; + } + + if (arguments.length < 4) { + return false; + } + + /* + + /MiniGamesReward add dropper [data] + /MiniGamesReward add parkour [data] + /MiniGamesReward clear dropper + /MiniGamesReward clear parkour + + */ + + Arena arena = null; + if (arguments[1].equalsIgnoreCase("dropper")) { + arena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[2]); + } else if (arguments[1].equalsIgnoreCase("parkour")) { + arena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[2]); + } + if (arena == null) { + MiniGames.getInstance().getStringFormatter().displayErrorMessage(commandSender, + MiniGameMessage.ERROR_ARENA_NOT_FOUND); + return false; + } + + RewardCondition condition = RewardCondition.getFromString(arguments[3]); + if (condition == null) { + MiniGames.getInstance().getStringFormatter().displayErrorMessage(player, + MiniGameMessage.ERROR_REWARD_CONDITION_INVALID); + return false; + } + + if (arguments[0].equalsIgnoreCase("clear")) { + arena.clearRewards(condition); + MiniGames.getInstance().getStringFormatter().displaySuccessMessage(player, + MiniGameMessage.SUCCESS_REWARDS_CLEARED); + return true; + } + + if (!arguments[0].equalsIgnoreCase("add") || arguments.length < 5) { + return false; + } + + String firstArgument = arguments.length > 5 ? arguments[5] : null; + String secondArgument = arguments.length > 6 ? arguments[6] : null; + + Reward reward = RewardHelper.parseRewardInput(player, arguments[4], firstArgument, secondArgument, + Arrays.copyOfRange(arguments, 5, arguments.length)); + + if (reward != null) { + arena.addReward(condition, reward); + MiniGames.getInstance().getStringFormatter().displaySuccessMessage(player, + MiniGameMessage.SUCCESS_REWARD_ADDED); + return true; + } else { + return false; + } + } + +} diff --git a/src/main/java/net/knarcraft/minigames/command/SetArenaRewardTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/SetArenaRewardTabCompleter.java new file mode 100644 index 0000000..2537618 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/command/SetArenaRewardTabCompleter.java @@ -0,0 +1,23 @@ +package net.knarcraft.minigames.command; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * The tab completer for the reward setting command + */ +public class SetArenaRewardTabCompleter implements TabCompleter { + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] strings) { + return null; + } + +} diff --git a/src/main/java/net/knarcraft/minigames/config/MiniGameMessage.java b/src/main/java/net/knarcraft/minigames/config/MiniGameMessage.java index dfcea44..21b684d 100644 --- a/src/main/java/net/knarcraft/minigames/config/MiniGameMessage.java +++ b/src/main/java/net/knarcraft/minigames/config/MiniGameMessage.java @@ -135,6 +135,36 @@ public enum MiniGameMessage implements TranslatableMessage { */ ERROR_HARDCORE_NO_CHECKPOINTS, + /** + * The message displayed if a user specifies an invalid material + */ + ERROR_INVALID_MATERIAL, + + /** + * The message displayed if a user specifies an invalid world + */ + ERROR_INVALID_WORLD, + + /** + * The message displayed if a user specifies an invalid number + */ + ERROR_INVALID_NUMBER, + + /** + * The message displayed if a user specifies an invalid command (for a command reward) + */ + ERROR_INVALID_COMMAND_STRING, + + /** + * The message displayed if a user specified an invalid reward type + */ + ERROR_REWARD_TYPE_INVALID, + + /** + * The message displayed if a user specified an invalid reward condition + */ + ERROR_REWARD_CONDITION_INVALID, + /* **************** * * Success messages * * **************** */ @@ -253,6 +283,16 @@ public enum MiniGameMessage implements TranslatableMessage { * The message displayed when a player is rewarded with an amount of currency */ SUCCESS_ECONOMY_REWARDED, + + /** + * The message displayed when an arena reward has been successfully added + */ + SUCCESS_REWARD_ADDED, + + /** + * The message displayed when arena rewards have been cleared + */ + SUCCESS_REWARDS_CLEARED, ; @Override diff --git a/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java index 0b2c22d..7e22f31 100644 --- a/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java @@ -9,6 +9,8 @@ import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry; import net.knarcraft.minigames.arena.dropper.DropperArenaStorageKey; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.config.MiniGameMessage; import net.knarcraft.minigames.container.SerializableMaterial; import net.knarcraft.minigames.container.SerializableUUID; @@ -105,6 +107,7 @@ public final class DropperArenaStorageHelper { configSection.set(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey(), arena.getPlayerVerticalVelocity()); configSection.set(DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity()); configSection.set(DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType())); + RewardStorageHelper.saveRewards(arena, configSection, DropperArenaStorageKey.REWARDS.getKey()); saveDropperArenaData(arena.getData()); } configuration.save(dropperArenaFile); @@ -169,6 +172,9 @@ public final class DropperArenaStorageHelper { winBlockType = new SerializableMaterial(Material.WATER); } + Map> rewards = RewardStorageHelper.loadRewards(configurationSection, + DropperArenaStorageKey.REWARDS.getKey()); + // Generate new, empty arena data if not available DropperArenaData arenaData = loadDropperArenaData(arenaId); if (arenaData == null) { @@ -178,7 +184,7 @@ public final class DropperArenaStorageHelper { } return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity, - winBlockType.getRawValue(), arenaData, MiniGames.getInstance().getDropperArenaHandler()); + winBlockType.getRawValue(), rewards, arenaData, MiniGames.getInstance().getDropperArenaHandler()); } /** diff --git a/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java index 1c00c64..876f823 100644 --- a/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java @@ -9,6 +9,8 @@ import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry; import net.knarcraft.minigames.arena.parkour.ParkourArenaStorageKey; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; import net.knarcraft.minigames.config.MiniGameMessage; import net.knarcraft.minigames.container.SerializableMaterial; import net.knarcraft.minigames.container.SerializableUUID; @@ -108,6 +110,7 @@ public final class ParkourArenaStorageHelper { configSection.set(ParkourArenaStorageKey.WIN_LOCATION.getKey(), arena.getWinLocation()); configSection.set(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey(), arena.getKillPlaneBlockNames()); configSection.set(ParkourArenaStorageKey.CHECKPOINTS.getKey(), arena.getCheckpoints()); + RewardStorageHelper.saveRewards(arena, configSection, ParkourArenaStorageKey.REWARDS.getKey()); saveParkourArenaData(arena.getData()); } configuration.save(parkourArenaFile); @@ -163,6 +166,9 @@ public final class ParkourArenaStorageHelper { List killPlaneBlockNames = configurationSection.getList(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey()); List checkpoints = (List) configurationSection.get(ParkourArenaStorageKey.CHECKPOINTS.getKey()); + Map> rewards = RewardStorageHelper.loadRewards(configurationSection, + ParkourArenaStorageKey.REWARDS.getKey()); + // The arena name and spawn location must be present if (arenaName == null || spawnLocation == null) { MiniGames.log(Level.SEVERE, MiniGames.getInstance().getStringFormatter().replacePlaceholders( @@ -189,7 +195,8 @@ public final class ParkourArenaStorageHelper { } return new ParkourArena(arenaId, arenaName, spawnLocation, exitLocation, winBlockType.getRawValue(), winLocation, - (Set) killPlaneBlockNames, checkpoints, arenaData, MiniGames.getInstance().getParkourArenaHandler()); + (Set) killPlaneBlockNames, checkpoints, rewards, arenaData, + MiniGames.getInstance().getParkourArenaHandler()); } /** diff --git a/src/main/java/net/knarcraft/minigames/util/RewardHelper.java b/src/main/java/net/knarcraft/minigames/util/RewardHelper.java new file mode 100644 index 0000000..bffd1ed --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/util/RewardHelper.java @@ -0,0 +1,189 @@ +package net.knarcraft.minigames.util; + +import net.knarcraft.knarlib.formatting.StringFormatter; +import net.knarcraft.knarlib.util.MaterialHelper; +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.reward.CommandReward; +import net.knarcraft.minigames.arena.reward.EconomyReward; +import net.knarcraft.minigames.arena.reward.ItemReward; +import net.knarcraft.minigames.arena.reward.PermissionReward; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardType; +import net.knarcraft.minigames.config.MiniGameMessage; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.UUID; + +/** + * A helopr class for getting the reward specified in user input + */ +public final class RewardHelper { + + private RewardHelper() { + + } + + /** + * Grants the given rewards to the given player + * + * @param player

The player to reward

+ * @param rewards

The rewards to give

+ */ + public static void grantRewards(@NotNull Player player, @NotNull Collection rewards) { + StringFormatter stringFormatter = MiniGames.getInstance().getStringFormatter(); + for (Reward reward : rewards) { + boolean granted = reward.grant(player); + if (granted) { + stringFormatter.displaySuccessMessage(player, reward.getGrantMessage()); + } + } + } + + /** + * Parses input describing a reward + * + * @param player

The player that specified the reward

+ * @param typeString

The string given to specify the reward type

+ * @param firstArgument

The first reward argument given by the player, or null

+ * @param secondArgument

The second reward argument given by the player, or null

+ * @param allArguments

A list of all the reward arguments, in case the reward is a command reward

+ * @return

The parsed reward, or null if some input was invalid

+ */ + public static @Nullable Reward parseRewardInput(@NotNull Player player, + @NotNull String typeString, + @Nullable String firstArgument, + @Nullable String secondArgument, + @NotNull String[] allArguments) { + RewardType rewardType = RewardType.getFromString(typeString); + if (rewardType == null) { + MiniGames.getInstance().getStringFormatter().displayErrorMessage(player, + MiniGameMessage.ERROR_REWARD_TYPE_INVALID); + return null; + } + + if (rewardType != RewardType.ITEM && firstArgument == null) { + return null; + } + + try { + return switch (rewardType) { + case COMMAND -> new CommandReward(getArrayAsString(allArguments)); + case ECONOMY -> new EconomyReward(getDouble(firstArgument)); + case PERMISSION -> new PermissionReward(getWorld(secondArgument), firstArgument); + case ITEM -> new ItemReward(getItem(player, firstArgument, secondArgument)); + }; + } catch (IllegalArgumentException exception) { + MiniGames.getInstance().getStringFormatter().displayErrorMessage(player, exception.getMessage()); + return null; + } + } + + /** + * Gets a double from the given input + * + * @param input

The input representing a double

+ * @return

The double specified

+ * @throws IllegalArgumentException

If the input is not a number or is not positive

+ */ + private static double getDouble(@NotNull String input) throws IllegalArgumentException { + IllegalArgumentException invalidException = new IllegalArgumentException( + MiniGames.getInstance().getTranslator().getTranslatedMessage(MiniGameMessage.ERROR_INVALID_NUMBER)); + try { + double number = Double.parseDouble(input); + if (number <= 0) { + throw invalidException; + } + return number; + } catch (NumberFormatException exception) { + throw invalidException; + } + } + + /** + * Gets the world specified in the given identifier + * + * @param worldIdentifier

A world UUID or name

+ * @return

The world, or null if no such world exists

+ */ + private static @Nullable World getWorld(@Nullable String worldIdentifier) { + if (worldIdentifier == null || worldIdentifier.isBlank()) { + return null; + } + World world; + try { + UUID worldId = UUID.fromString(worldIdentifier); + world = Bukkit.getWorld(worldId); + } catch (IllegalArgumentException exception) { + world = Bukkit.getWorld(worldIdentifier); + } + if (world != null) { + return world; + } else { + throw new IllegalArgumentException(MiniGames.getInstance().getTranslator().getTranslatedMessage( + MiniGameMessage.ERROR_INVALID_WORLD)); + } + } + + /** + * Gets an item stack according to the given input + * + * @param player

The player that caused this method to execute

+ * @param argument1

The first argument given by the player, or null

+ * @param argument2

The second argument given by the player, or null

+ * @return

An item stack as described, or the player's held item

+ * @throws IllegalArgumentException

If an invalid material was specified

+ */ + private static @NotNull ItemStack getItem(@NotNull Player player, @Nullable String argument1, + @Nullable String argument2) throws IllegalArgumentException { + if (argument1 != null) { + Material material = MaterialHelper.loadMaterialString(argument1, MiniGames.getInstance().getLogger()); + int amount; + try { + if (argument2 != null) { + amount = Integer.parseInt(argument2); + } else { + amount = 1; + } + } catch (NumberFormatException exception) { + amount = 1; + } + if (material == null || material.isAir()) { + throw new IllegalArgumentException(MiniGames.getInstance().getTranslator().getTranslatedMessage( + MiniGameMessage.ERROR_INVALID_MATERIAL)); + } + return new ItemStack(material, amount); + } else { + ItemStack inHand = player.getInventory().getItemInMainHand(); + if (!inHand.getType().isAir()) { + return inHand; + } else { + throw new IllegalArgumentException(MiniGames.getInstance().getTranslator().getTranslatedMessage( + MiniGameMessage.ERROR_INVALID_MATERIAL)); + } + } + } + + /** + * Gets the string array as a space separated string + * + * @param array

The array to get as a string

+ * @return

The array as a string

+ */ + private static String getArrayAsString(@NotNull String[] array) { + String output = String.join(" ", array); + if (output.isBlank()) { + throw new IllegalArgumentException(MiniGames.getInstance().getTranslator().getTranslatedMessage( + MiniGameMessage.ERROR_INVALID_COMMAND_STRING)); + } else { + return output; + } + } + +} diff --git a/src/main/java/net/knarcraft/minigames/util/RewardStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/RewardStorageHelper.java new file mode 100644 index 0000000..ae4a552 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/util/RewardStorageHelper.java @@ -0,0 +1,67 @@ +package net.knarcraft.minigames.util; + +import net.knarcraft.minigames.arena.Arena; +import net.knarcraft.minigames.arena.reward.Reward; +import net.knarcraft.minigames.arena.reward.RewardCondition; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A helper class for loading and storing rewards + */ +public class RewardStorageHelper { + + /** + * Loads the rewards contained at the given path + * + * @param configurationSection

The configuration section containing the reward

+ * @param key

The section key to search

+ * @return

The loaded rewards

+ */ + public static Map> loadRewards(@NotNull ConfigurationSection configurationSection, + @NotNull String key) { + Map> rewards = new HashMap<>(); + if (!configurationSection.contains(key)) { + return rewards; + } + for (RewardCondition condition : RewardCondition.values()) { + String section = key + "." + condition.name(); + if (!configurationSection.contains(section)) { + continue; + } + Set rewardSet = new HashSet<>(); + List rewardList = configurationSection.getList(section, new ArrayList<>()); + for (Object object : rewardList) { + if (object instanceof Reward reward) { + rewardSet.add(reward); + } + } + rewards.put(condition, rewardSet); + } + + return rewards; + } + + /** + * Saves rewards for the given arena + * + * @param arena

The arena to save rewards for

+ * @param configurationSection

The configuration section to save the rewards at

+ * @param key

The section key to save at

+ */ + public static void saveRewards(@NotNull Arena arena, @NotNull ConfigurationSection configurationSection, + @NotNull String key) { + for (RewardCondition condition : RewardCondition.values()) { + configurationSection.set(key + "." + condition.name(), + new ArrayList<>(arena.getRewards(condition))); + } + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 07c19c0..7d04a3c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -28,6 +28,12 @@ commands: - mmenu usage: / description: Used to display an actions menu while in an arena + miniGamesReward: + aliases: + - mreward + usage: / [value] [value] ... + description: Used to set rewards for arenas + permission: minigames.edit dropperGroupSet: aliases: - dgset diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml index 5613195..958e0a6 100644 --- a/src/main/resources/strings.yml +++ b/src/main/resources/strings.yml @@ -23,6 +23,12 @@ en: ERROR_SWAP_DIFFERENT_GROUPS: "You cannot swap arenas in different groups!" ERROR_ILLEGAL_COMMAND: "You cannot use that command while in an arena!" ERROR_HARDCORE_NO_CHECKPOINTS: "This arena cannot be played in hardcore mode as it has no checkpoints!" + ERROR_INVALID_MATERIAL: "You specified an invalid material" + ERROR_INVALID_WORLD: "You specified an invalid world" + ERROR_INVALID_NUMBER: "You specified an invalid number" + ERROR_INVALID_COMMAND_STRING: "You specified an invalid command. Make sure you don't add a slash in front of the command definition." + ERROR_REWARD_TYPE_INVALID: "You have specified an invalid reward type" + ERROR_REWARD_CONDITION_INVALID: "You have specified an invalid reward condition" SUCCESS_ARENA_GROUP_UPDATED: "The arena's group has been updated" SUCCESS_PLUGIN_RELOADED: "Plugin reloaded!" SUCCESS_ARENA_CREATED: "The arena was successfully created!" @@ -45,4 +51,6 @@ en: SUCCESS_PERMISSION_REWARDED: "You have been granted the permission: {permission}" SUCCESS_PERMISSION_REWARDED_WORLD: "You have been granted the permission: {permission} in world: {world}" SUCCESS_COMMAND_REWARDED: "The command `{command}` has been run as your reward." - SUCCESS_ECONOMY_REWARDED: "You have been granted {currency}" \ No newline at end of file + SUCCESS_ECONOMY_REWARDED: "You have been granted {currency}" + SUCCESS_REWARD_ADDED: "The reward was added." + SUCCESS_REWARDS_CLEARED: "Rewards cleared." \ No newline at end of file