From 38c957e0071cd6a04d06eec8cbb71d03d9f834e9 Mon Sep 17 00:00:00 2001 From: Eight Date: Sat, 8 Jun 2024 17:10:03 -0700 Subject: [PATCH 01/15] Added DEFAULT_HANDICAP to config --- .../cobblemonchallenge/config/ChallengeConfig.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java index 729bd73..62ba39a 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java @@ -9,6 +9,7 @@ public class ChallengeConfig { public static Boolean CHALLENGE_DISTANCE_RESTRICTION; public static int MAX_CHALLENGE_DISTANCE; public static int DEFAULT_CHALLENGE_LEVEL; + public static int DEFAULT_HANDICAP; public static int REQUEST_EXPIRATION_MILLIS; public static int CHALLENGE_COOLDOWN_MILLIS; @@ -23,6 +24,7 @@ private static void createConfigs() { configs.addKeyValuePair(new Pair<>("challengeDistanceRestriction", true)); configs.addKeyValuePair(new Pair<>("maxChallengeDistance", 50)); configs.addKeyValuePair(new Pair<>("defaultChallengeLevel", 50)); + configs.addKeyValuePair(new Pair<>("defaultHandicap", 0)); configs.addKeyValuePair(new Pair<>("challengeExpirationTime", 60000)); configs.addKeyValuePair(new Pair<>("challengeCooldownTime", 5000)); } @@ -31,6 +33,7 @@ private static void assignConfigs() { CHALLENGE_COOLDOWN_MILLIS = CONFIG.getOrDefault("challengeCooldownTime", 5000); CHALLENGE_DISTANCE_RESTRICTION = CONFIG.getOrDefault("challengeDistanceRestriction", true); DEFAULT_CHALLENGE_LEVEL = CONFIG.getOrDefault("defaultChallengeLevel", 50); + DEFAULT_HANDICAP = CONFIG.getOrDefault("defaultHandicap", 0); MAX_CHALLENGE_DISTANCE = CONFIG.getOrDefault("maxChallengeDistance", 50); REQUEST_EXPIRATION_MILLIS = CONFIG.getOrDefault("challengeExpirationTime", 60000); } From 72329fc456974721f6a797a8c62246bb4ccd4e62 Mon Sep 17 00:00:00 2001 From: Eight Date: Sat, 8 Jun 2024 18:08:35 -0700 Subject: [PATCH 02/15] Added min/maxLevel & handicap functionality: ChallengeCommand: - Refactored ChallengeRequest record with min/max level & handicap - Refactored ChallengeCommand.register to have 12 versions (TODO: Refactor to be more flexible & concise) - Refactored ChallengeCommand.challengePlayer to include min/max level & handicap - Added check in ChallengeCommand.challengePlayer to clamp minLevel to maxLevel - Refactored notification format in ChallengeCommand.challengePlayer to display level range & handicap to challengedPlayer ChallengeUtil: - Refactored ChallengeUtil.createChallengeRequest to intake min/maxLevel & handicap - Added method ChallengeUtil.getBattlePokemonAdjustedLevel to handle each individual pokemon level according to min/maxLevel, handicap, & clamp to (1,100) LeadPokemonSelectionSession: - Refactored LeadPokemonSelectionSession.beginBattle to use min/maxLevel & handicap to pokemon LeadPokemonMenuProvider: - Refactored LeadPokemonMenuProvider.setupPokemonRepresentation to apply min/maxLevel & handicap to pokemon ChallengeBattleBuilder: - Refactored ChallengeBattleBuilder.lvlxpvp to intake & pass min/maxLevel & handicap to ChallengeBattleBuilder.createBattleTeamFromParty - Refactored ChallengeBattleBuilder.createBattleTeamFromParty to apply min/maxLevel & handicap to pokemon --- .../battle/ChallengeBattleBuilder.java | 16 +- .../command/ChallengeCommand.java | 253 +++++++++++++++--- .../gui/LeadPokemonMenuProvider.java | 11 +- .../gui/LeadPokemonSelectionSession.java | 7 +- .../util/ChallengeUtil.java | 13 +- 5 files changed, 251 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java index 464e071..e01421d 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java @@ -24,14 +24,14 @@ public class ChallengeBattleBuilder { public static Vector clonedPokemonList = new Vector<>(); public static Vector challengeBattles = new Vector<>(); private ChallengeFormat format = ChallengeFormat.STANDARD_6V6; - public void lvlxpvp(ServerPlayer player1, ServerPlayer player2, BattleFormat battleFormat, int level, List player1Selection, List player2Selection) throws ChallengeBuilderException { + public void lvlxpvp(ServerPlayer player1, ServerPlayer player2, BattleFormat battleFormat, int minLevel, int maxLevel, int handicapP1, int handicapP2, List player1Selection, List player2Selection) throws ChallengeBuilderException { PartyStore p1Party = Cobblemon.INSTANCE.getStorage().getParty(player1); PartyStore p2Party = Cobblemon.INSTANCE.getStorage().getParty(player2); // Clone parties so original is not effected - List player1Team = createBattleTeamFromParty(p1Party, player1Selection, level); - List player2Team = createBattleTeamFromParty(p2Party, player2Selection, level); + List player1Team = createBattleTeamFromParty(p1Party, player1Selection, minLevel, maxLevel, handicapP1); + List player2Team = createBattleTeamFromParty(p2Party, player2Selection, minLevel, maxLevel, handicapP2); PlayerBattleActor player1Actor = new PlayerBattleActor(player1.getUUID(), player1Team); PlayerBattleActor player2Actor = new PlayerBattleActor(player2.getUUID(), player2Team); @@ -44,7 +44,8 @@ public void lvlxpvp(ServerPlayer player1, ServerPlayer player2, BattleFormat bat } // Method to create our own clones according to the format - private List createBattleTeamFromParty(PartyStore party, List selectedSlots, int level) throws ChallengeBuilderException { + private List createBattleTeamFromParty(PartyStore party, List selectedSlots, int minLevel, int maxLevel, int handicap) throws ChallengeBuilderException { + List battlePokemonList = new ArrayList(); if (format == ChallengeFormat.STANDARD_6V6) { int leadSlot = selectedSlots.get(0); @@ -53,13 +54,16 @@ private List createBattleTeamFromParty(PartyStore party, List CHALLENGE_REQUESTS = new HashMap<>(); public static final HashMap ACTIVE_SELECTIONS = new HashMap<>(); private static final HashMap LAST_SENT_CHALLENGE = new HashMap<>(); + public static void register(CommandDispatcher dispatcher) { - // Basic challenge command that initiates a challenge with the default challenge level - LiteralArgumentBuilder baseCommandBuilder = Commands.literal("challenge") + // Overview: + // > always player name with level or min/maxLevel are mutually exclusive + // > 12 command trees + // > further additions may need a UI implementation to refine/make more user friendly + + // (default everything) + // handicap + // no preview + // handicap + no preview + + // level + // level + handicap + // level + no preview + // level + handicap + no preview + + // min/max + // min/max + handicap + // min/max + no preview + // min/max + handicap + no preview + + // (default everything) + LiteralArgumentBuilder defaultChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, true))); + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) + ); + + // handicap + LiteralArgumentBuilder handicapChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("handicapP1") + .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("handicapP2") + .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), true)) + ) + + ) + ) + ) + ); - // Basic challenge command that initiates a challenge with the default challenge level - LiteralArgumentBuilder baseCommandBuilderNoPreview = Commands.literal("challenge") + // no preview + LiteralArgumentBuilder noPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("nopreview") - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, false)))); + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) + ) + ); + + // handicap + no preview + LiteralArgumentBuilder handicapNoPreviewChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("handicapP1") + .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("handicapP2") + .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("nopreview") + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), false)) + ) + ) + ) + ) + ) + ); + // level + LiteralArgumentBuilder levelChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("level") + .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) - // Challenge command that initiates a challenge with a given level - LiteralArgumentBuilder commandBuilderWithLevelOption = Commands.literal("challenge") + ) + ) + ); + + // level + handicap + LiteralArgumentBuilder levelHandicapChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), true) + .then(Commands.literal("handicapP1") + .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("handicapP2") + .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), true)) + ) + ) + ) ) + ) ) ); - // Challenge command that initiates a challenge with a given level - LiteralArgumentBuilder commandBuilderWithLevelOptionNoPreview = Commands.literal("challenge") + + // level + no preview + LiteralArgumentBuilder levelNoPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) .then(Commands.literal("nopreview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), false) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) + ) + + ) + ) + ); + + // level + handicap + no preview + LiteralArgumentBuilder levelHandicapNoPreviewChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("level") + .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("handicapP1") + .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("handicapP2") + .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("nopreview") + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), false)) + ) + ) + ) ) ) + ) ) ); - // Challenge command that initiates a challenge with a given level - LiteralArgumentBuilder commandBuilderWithLevelOptionNoPreviewBefore = Commands.literal("challenge") + // min/max + LiteralArgumentBuilder minMaxLevelChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("nopreview") - .then(Commands.literal("level") - .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), false) + .then(Commands.literal("minLevel") + .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("maxLevel") + .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) + ) + ) + + ) + ) + ); + + // min/max + handicap + LiteralArgumentBuilder minMaxLevelHandicapChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("minLevel") + .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("maxLevel") + .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("handicapP1") + .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("handicapP2") + .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), true)) + ) + ) + ) + ) + ) + ) + + ) + ) + ); + + // min/max + no preview + LiteralArgumentBuilder minMaxLevelNoPreviewChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("minLevel") + .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("maxLevel") + .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("nopreview") + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) + ) + ) + ) + + ) + ) + ); + + // min/max + handicap + no preview + LiteralArgumentBuilder minMaxLevelHandicapNoPreviewChallengeProperties = Commands.literal("challenge") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.literal("minLevel") + .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("maxLevel") + .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("handicapP1") + .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("handicapP2") + .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("nopreview") + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), false)) + ) + ) + ) + ) + ) ) ) + ) ) ); // Command called to accept challenges - LiteralArgumentBuilder commandBuilderAcceptChallenge = Commands.literal("acceptchallenge") + LiteralArgumentBuilder acceptChallengeAndProperties = Commands.literal("acceptchallenge") .then(Commands.argument("id", StringArgumentType.string()).executes(c -> acceptChallenge(c, StringArgumentType.getString(c, "id")))); // Command called to deny challenges - LiteralArgumentBuilder commandBuilderRejectChallenge = Commands.literal("rejectchallenge") + LiteralArgumentBuilder rejectChallengeAndProperties = Commands.literal("rejectchallenge") .then(Commands.argument("id", StringArgumentType.string()).executes(c -> rejectChallenge(c, StringArgumentType.getString(c, "id")))); - dispatcher.register(commandBuilderAcceptChallenge); - dispatcher.register(commandBuilderRejectChallenge); - dispatcher.register(commandBuilderWithLevelOption); - // Register nopreview section - dispatcher.register(commandBuilderWithLevelOptionNoPreview); - dispatcher.register(baseCommandBuilderNoPreview); - dispatcher.register(commandBuilderWithLevelOptionNoPreviewBefore); - dispatcher.register(baseCommandBuilder); + dispatcher.register(acceptChallengeAndProperties); + dispatcher.register(rejectChallengeAndProperties); + + // 12 possible Challenge Properties Commands + dispatcher.register(defaultChallengeProperties); + dispatcher.register(handicapChallengeProperties); + dispatcher.register(noPreviewChallengeProperties); + dispatcher.register(handicapNoPreviewChallengeProperties); + + dispatcher.register(levelChallengeProperties); + dispatcher.register(levelHandicapChallengeProperties); + dispatcher.register(levelNoPreviewChallengeProperties); + dispatcher.register(levelHandicapNoPreviewChallengeProperties); + + dispatcher.register(minMaxLevelChallengeProperties); + dispatcher.register(minMaxLevelHandicapChallengeProperties); + dispatcher.register(minMaxLevelNoPreviewChallengeProperties); + dispatcher.register(minMaxLevelHandicapNoPreviewChallengeProperties); } - public static int challengePlayer(CommandContext c, int level, boolean preview) { + public static int challengePlayer(CommandContext c, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview) { try { ServerPlayer challengerPlayer = c.getSource().getPlayer(); ServerPlayer challengedPlayer = c.getArgument("player", EntitySelector.class).findSinglePlayer(c.getSource()); @@ -147,14 +320,22 @@ public static int challengePlayer(CommandContext c, int leve return 0; } - ChallengeRequest request = ChallengeUtil.createChallengeRequest(challengerPlayer, challengedPlayer, level, preview); + // make sure the min max range contains at least one functional value + // > defaults to maxLevel + if (minLevel > maxLevel){ + minLevel = maxLevel; + } + + ChallengeRequest request = ChallengeUtil.createChallengeRequest(challengerPlayer, challengedPlayer, minLevel, maxLevel, handicapP1, handicapP2, preview); CHALLENGE_REQUESTS.put(request.id, request); - String options = ""; - if (!request.preview()) { - options = ChatFormatting.GOLD + " [NoTeamPreview]"; - } - MutableComponent notificationComponent = Component.literal(ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d Pokemon battle" + ChatFormatting.RESET + ChatFormatting.YELLOW + " by %s!" + options, level, challengerPlayer.getDisplayName().getString())); MutableComponent interactiveComponent = Component.literal("Click to accept or deny: "); + String levelComponent = (minLevel == maxLevel) ? ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d Pokemon battle", maxLevel) : ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d - %d Pokemon battle", minLevel, maxLevel); + String challengerComponent = ChatFormatting.YELLOW + " by " + challengerPlayer.getDisplayName().getString() + "!"; + String optionsComponent = request.preview() ? "" : ChatFormatting.RED + " [NoTeamPreview]"; + String handicapComponent = (handicapP1 == 0 && handicapP2 == 0) ? "" : ChatFormatting.BLUE + " [" + challengerPlayer.getDisplayName().getString() + " handicap of " + handicapP1 + "] [" + challengedPlayer.getDisplayName().getString() + " handicap of " + handicapP2 + "]"; + MutableComponent notificationComponent = Component.literal(levelComponent + challengerComponent + optionsComponent + handicapComponent); + + MutableComponent interactiveComponent = Component.literal("Click to accept or deny: "); interactiveComponent.append(Component.literal(ChatFormatting.GREEN + "Battle!").setStyle(Style.EMPTY.withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/acceptchallenge %s", request.id))))); interactiveComponent.append(Component.literal(" or ")); interactiveComponent.append(Component.literal(ChatFormatting.RED + "Reject").setStyle(Style.EMPTY.withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/rejectchallenge %s", request.id))))); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java index 78967c5..faf1348 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java @@ -70,15 +70,19 @@ private void setupPokemonRepresentation(LeadPokemonMenu leadPokemonMenu) { PartyStore p2Party = Cobblemon.INSTANCE.getStorage().getParty(rival); setupGlassFiller(leadPokemonMenu); + int handicapP1 = (this.selector == request.challengerPlayer()) ? request.handicapP1() : request.handicapP2(); + int handicapP2 = (this.selector == request.challengerPlayer()) ? request.handicapP2() : request.handicapP1(); + for (int x = 0; x < p1Party.size(); x ++) { int itemSlot = x * 9; // Lefthand column of the menu Pokemon pokemon = p1Party.get(x); if (pokemon == null) // Skip any empty slots in the pokemon team continue; BattlePokemon copy = BattlePokemon.Companion.safeCopyOf(pokemon); - pokemon = ChallengeUtil.applyFormatTransformations(ChallengeFormat.STANDARD_6V6, copy, request.level()).getEffectedPokemon(); // Apply battle transformations to each pokemon + int adjustedLevelP1 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.minLevel(), request.maxLevel(), handicapP1); + pokemon = ChallengeUtil.applyFormatTransformations(ChallengeFormat.STANDARD_6V6, copy, adjustedLevelP1).getEffectedPokemon(); // Apply battle transformations to each pokemon ItemStack pokemonItem = PokemonItem.from(pokemon, 1); - pokemonItem.setHoverName(Component.literal(ChatFormatting.AQUA + String.format("%s (lvl%d)", pokemon.getDisplayName().getString(), request.level()))); + pokemonItem.setHoverName(Component.literal(ChatFormatting.AQUA + String.format("%s (lvl%d)", pokemon.getDisplayName().getString(), adjustedLevelP1))); ListTag pokemonLoreTag = ChallengeUtil.generateLoreTagForPokemon(pokemon); pokemonItem.getOrCreateTagElement("display").put("Lore", pokemonLoreTag); leadPokemonMenu.setItem(itemSlot, leadPokemonMenu.getStateId(), pokemonItem); @@ -93,7 +97,8 @@ private void setupPokemonRepresentation(LeadPokemonMenu leadPokemonMenu) { } if (selectionSession.teamPreviewOn()) { ItemStack pokemonItem = PokemonItem.from(pokemon, 1); - pokemonItem.setHoverName(Component.literal(ChatFormatting.RED + String.format("%s's %s (lvl%d)", rival.getDisplayName().getString(), pokemon.getDisplayName().getString(), request.level()))); + int adjustedLevelP2 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.minLevel(), request.maxLevel(), handicapP2); + pokemonItem.setHoverName(Component.literal(ChatFormatting.RED + String.format("%s's %s (lvl%d)", rival.getDisplayName().getString(), pokemon.getDisplayName().getString(), adjustedLevelP2))); leadPokemonMenu.setItem(itemSlot, leadPokemonMenu.getStateId(), pokemonItem); } else { ItemStack pokemonItem = new ItemStack(CobblemonItems.POKE_BALL.asItem()); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java index 2d8aea5..59412df 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java @@ -61,13 +61,16 @@ public void onPokemonSelected(LeadPokemonMenuProvider menuProvider) { } private void beginBattle() { - int level = originRequest.level(); + int minLevel = originRequest.minLevel(); + int maxLevel = originRequest.maxLevel(); + int handicapP1 = originRequest.handicapP1(); + int handicapP2 = originRequest.handicapP2(); SESSIONS_TO_CANCEL.add(this); challengerMenuProvider.forceCloseMenu(); challengedMenuProvider.forceCloseMenu(); ChallengeBattleBuilder challengeBuilder = new ChallengeBattleBuilder(); try { - challengeBuilder.lvlxpvp(originRequest.challengerPlayer(), originRequest.challengedPlayer(), BattleFormat.Companion.getGEN_9_SINGLES(), level, challengerMenuProvider.selectedSlots, challengedMenuProvider.selectedSlots); + challengeBuilder.lvlxpvp(originRequest.challengerPlayer(), originRequest.challengedPlayer(), BattleFormat.Companion.getGEN_9_SINGLES(), minLevel, maxLevel, handicapP1, handicapP2, challengerMenuProvider.selectedSlots, challengedMenuProvider.selectedSlots); } catch (ChallengeBuilderException e) { e.printStackTrace(); } diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java index 14d5418..f193958 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java @@ -57,9 +57,9 @@ public static boolean isPlayerOnline(ServerPlayer player) { return player.getServer().getPlayerList().getPlayer(player.getUUID()) != null; } - public static ChallengeCommand.ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int level, boolean preview) { + public static ChallengeCommand.ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview) { String key = UUID.randomUUID().toString().replaceAll("-", ""); - ChallengeCommand.ChallengeRequest newRequest = new ChallengeCommand.ChallengeRequest(key, challengerPlayer, challengedPlayer, level, preview, System.currentTimeMillis()); + ChallengeCommand.ChallengeRequest newRequest = new ChallengeCommand.ChallengeRequest(key, challengerPlayer, challengedPlayer, minLevel, maxLevel, handicapP1, handicapP2, preview, System.currentTimeMillis()); return newRequest; } @@ -127,4 +127,13 @@ public static BattlePokemon applyFormatTransformations(ChallengeFormat format, B } return pokemon; } + + // Method for clamping Battle Pokemon to level range, between 1-100, & applying handicap + // > the handicap applied AFTER level clamp to range + // > A players level may be outside this range after the handicap is applied + // > But, the finalized handicap will be a hard clamped to (1,100) + public static int getBattlePokemonAdjustedLevel(int actualLevel, int minLevel, int maxLevel, int handicap) { + int adjustedLevel = (actualLevel < minLevel) ? minLevel + handicap : Math.min(actualLevel, maxLevel) + handicap; + return (adjustedLevel < 1) ? 1 : Math.min(adjustedLevel, 100); + } } \ No newline at end of file From c53c7a2d0e760c02e3def7b8f2abad4cb8a6ae71 Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 13:00:53 -0700 Subject: [PATCH 03/15] Update gradle properties, build, & wrapper: build.gradle: plugins: - updated fabric-loom version -> 1.6-snapshot - updated kotlin.jvm version -> 2.0.0 dependencies: - added modImplementation for kotlin -> "net.fabricmc:fabric-language-kotlin:1.11.0+kotlin.2.0.0" gradle.properties: - updated -> loader_version=0.15.11 - updated -> fabric_version=0.92.2+1.20.1 - updated -> cloth_config_version=11.1.118 gradle-wrapper.properties: - updated gradle to 8.7-> distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip --- build.gradle | 10 +++------- gradle.properties | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 0cec26b..ff51ebb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,13 @@ plugins { - id 'fabric-loom' version '1.2-SNAPSHOT' + id 'fabric-loom' version '1.6-SNAPSHOT' id 'maven-publish' - id 'org.jetbrains.kotlin.jvm' version "1.7.10" + id 'org.jetbrains.kotlin.jvm' version "2.0.0" } version = project.mod_version group = project.maven_group repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. maven {url = "https://maven.parchmentmc.org"} maven { url "https://cursemaven.com" @@ -46,6 +41,7 @@ dependencies { //modRuntimeOnly"curse.maven:cobblemon-687131:4468330" modImplementation"curse.maven:architectury-419699:4663010" + modImplementation("net.fabricmc:fabric-language-kotlin:1.11.0+kotlin.2.0.0") } base { diff --git a/gradle.properties b/gradle.properties index e78e6ff..e7ecc3d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.parallel=true # check these on https://fabricmc.net/develop minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 -loader_version=0.14.22 +loader_version=0.15.11 # Mod Properties mod_version = 1.1.7 @@ -14,5 +14,5 @@ maven_group = com.turtlehoarder.cobblemonchallenge archives_base_name = cobblemonchallenge # Dependencies -fabric_version=0.89.3+1.20.1 -cloth_config_version=11.1.106 \ No newline at end of file +fabric_version=0.92.2+1.20.1 +cloth_config_version=11.1.118 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index db9a6b8..a8382d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=wrapper/dists \ No newline at end of file From 0cf94b9c5b9f9d9744630a906bb228bcf814647e Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 13:39:35 -0700 Subject: [PATCH 04/15] Update README.md with new commands --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7c98ea..c81e3db 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,54 @@ Cobblemon Challenge is an extremely simple plugin for Cobblemon that makes chall easier! This is a server-side plugin that only needs to be installed on the Server. #### Commands +There are four possible challenge properties: +1. Username (required) +2. Level or min/maxLevel (optional & mutually exclusive) +3. Handicap (optional) +4. NoPreview (optional, true if excluded & false if specified) ```/challenge ``` - Challenges specified player to a lvl 50 pokemon battle. They may accept or deny this challenge. ```/challenge level ``` - Challenges specified player to a lvl X battle where X can be 1-100. -Example: ```/challenge TurtleHoarder level 100``` challenges TurtleHoarder to a level 100 battle. +```/challenge minLevel maxLevel ``` - Challenges specified player to a lvl - battle where & can be 1-100. + +```/challenge handicapP1 handicapP1 ``` - Challenges specified player to a battle where player1 (challenger) & player2 (challenged) are offset by the specified levels. can be from (-99)-(99). + +```/challenge noPreview``` - Challenges specified player to a default battle with no preview of the rivals pokemon during starter selection. + +##### Command Input Order: +There are 12 possible input combinations for challenge properties. The order of input must be username -> level or min/maxLevel -> handicap -> noPreview. If the challenge property is optional you may skip it when inputting the command. The default values will be used. + +##### All Possible Commands: + +```/challenge ``` + +```/challenge handicapP1 handicapP1 ``` + +```/challenge noPreview``` + +```/challenge handicapP1 handicapP1 noPreview``` + + +```/challenge level ``` + +```/challenge level handicapP1 handicapP1 ``` + +```/challenge level noPreview``` + +```/challenge level handicapP1 handicapP1 noPreview``` + + +```/challenge minLevel maxLevel ``` + +```/challenge minLevel maxLevel handicapP1 handicapP1 ``` + +```/challenge minLevel maxLevel noPreview``` + +```/challenge minLevel maxLevel handicapP1 handicapP1 noPreview``` + + #### Configurations There are numerous options that will allow you to customize the Challenge experience to your server's needs. These settings can be found in the Cobblemon Challenge config file: @@ -18,14 +60,17 @@ settings can be found in the Cobblemon Challenge config file: ```challengeDistanceRestriction``` - The value that determines if challenges are restricted by distance. Set to **false** if you would want no restrictions on distance. This value is set to **true** by default. -```maxChallengeDistance``` - If challengeDistanceRestriction is set to **true**, then this value defines the max distance +```maxChallengeDistance``` - If challengeDistanceRestriction is set to **true**, then this value defines the max distance that a challenge can be sent. This is set to 50 blocks by default. ```defaultChallengeLevel``` - The value that determines the level of a challenge if there is not level specified by the challenger. This is set to 50 by default for lvl 50 battles. +```defaultHandicap``` - The value that determines each players final level of each Pokemon if a handicap is not specified by the challenger. +This is set to 0 by default. + ```challengeExpirationTime``` - The value that determines how long a challenge should be pending before it expires. This is set to 60000 milliseconds / 1 minute by default. -```challengeCooldownTime``` - The value that determines how long a player must wait before sending a consecutive request. This value is +```challengeCooldownTime``` - The value that determines how long a player must wait before sending a consecutive request. This value is set to 5000 milliseconds / 5 seconds by default, though players will need to wait until their existing challenge expires before sending another one. From f543071a8eac6f01ef268075339f993825a6c609 Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 17:55:46 -0700 Subject: [PATCH 05/15] Refactor -> extracted ChallengeRequest & LeadPokemonSelection records Refactor -> Reformat of ChallengeCommand min/maxLevel & handicap inputs --- .../api/ChallengeRequest.java | 17 ++ .../api/LeadPokemonSelection.java | 10 + .../command/ChallengeCommand.java | 190 ++++++++---------- .../config/ChallengeConfigProvider.java | 1 + .../event/ChallengeEventHandler.java | 32 +-- .../gui/LeadPokemonMenu.java | 1 + .../gui/LeadPokemonMenuProvider.java | 14 +- .../gui/LeadPokemonSelectionSession.java | 10 +- .../util/ChallengeUtil.java | 7 +- 9 files changed, 149 insertions(+), 133 deletions(-) create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/api/LeadPokemonSelection.java diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java new file mode 100644 index 0000000..f930b02 --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java @@ -0,0 +1,17 @@ +package com.turtlehoarder.cobblemonchallenge.api; + +import net.minecraft.server.level.ServerPlayer; + +public record ChallengeRequest ( + String id, + ServerPlayer challengerPlayer, + ServerPlayer challengedPlayer, + int minLevel, + int maxLevel, + int handicapP1, + int handicapP2, + boolean preview, + long createdTime +) { + +} diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/LeadPokemonSelection.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/LeadPokemonSelection.java new file mode 100644 index 0000000..dc715a4 --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/LeadPokemonSelection.java @@ -0,0 +1,10 @@ +package com.turtlehoarder.cobblemonchallenge.api; + +import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonSelectionSession; + +public record LeadPokemonSelection( + LeadPokemonSelectionSession selectionWrapper, + long createdTime +) { + +} diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java index 2807409..946a6ef 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java @@ -1,17 +1,21 @@ package com.turtlehoarder.cobblemonchallenge.command; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; +import com.turtlehoarder.cobblemonchallenge.api.LeadPokemonSelection; +import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; +import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonSelectionSession; +import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; + import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.battles.BattleRegistry; + import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; -import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; -import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonMenuProvider; -import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; -import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonSelectionSession; + import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; @@ -28,8 +32,6 @@ public class ChallengeCommand { - public record ChallengeRequest(String id, ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview, long createdTime) {} - public record LeadPokemonSelection(LeadPokemonSelectionSession selectionWrapper, long createdTime) {} private static final float MAX_DISTANCE = ChallengeConfig.MAX_CHALLENGE_DISTANCE; private static final boolean USE_DISTANCE_RESTRICTION = ChallengeConfig.CHALLENGE_DISTANCE_RESTRICTION; private static final int DEFAULT_LEVEL = ChallengeConfig.DEFAULT_CHALLENGE_LEVEL; @@ -43,7 +45,7 @@ public static void register(CommandDispatcher dispatcher) { // Overview: // > always player name with level or min/maxLevel are mutually exclusive // > 12 command trees - // > further additions may need a UI implementation to refine/make more user friendly + // > further additions may need a UI implementation to refine/make more user-friendly // (default everything) // handicap @@ -69,13 +71,10 @@ public static void register(CommandDispatcher dispatcher) { // handicap LiteralArgumentBuilder handicapChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("handicapP1") - .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("handicapP2") - .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), true)) - ) - + .then(Commands.literal("handicap") + .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) + .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), true)) ) ) ) @@ -84,7 +83,7 @@ public static void register(CommandDispatcher dispatcher) { // no preview LiteralArgumentBuilder noPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("nopreview") + .then(Commands.literal("no-preview") .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) ) ); @@ -92,13 +91,11 @@ public static void register(CommandDispatcher dispatcher) { // handicap + no preview LiteralArgumentBuilder handicapNoPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("handicapP1") - .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("handicapP2") - .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("nopreview") - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), false)) - ) + .then(Commands.literal("handicap") + .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) + .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("no-preview") + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), false)) ) ) ) @@ -121,16 +118,13 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("handicapP1") - .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("handicapP2") - .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), true)) - ) + .then(Commands.literal("handicap") + .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) + .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), true)) ) ) ) - ) ) ); @@ -140,7 +134,7 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("nopreview") + .then(Commands.literal("no-preview") .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) ) @@ -153,18 +147,15 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("handicapP1") - .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("handicapP2") - .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("nopreview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), false)) - ) + .then(Commands.literal("handicap") + .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) + .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("no-preview") + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), false)) ) ) ) ) - ) ) ); @@ -172,14 +163,11 @@ public static void register(CommandDispatcher dispatcher) { // min/max LiteralArgumentBuilder minMaxLevelChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("minLevel") - .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("maxLevel") - .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) - ) + .then(Commands.literal("levelRange") + .then(Commands.argument("minLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.argument("maxLevel", IntegerArgumentType.integer(1,100)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) ) - ) ) ); @@ -187,22 +175,17 @@ public static void register(CommandDispatcher dispatcher) { // min/max + handicap LiteralArgumentBuilder minMaxLevelHandicapChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("minLevel") - .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("maxLevel") - .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("handicapP1") - .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("handicapP2") - .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), true)) - ) - ) + .then(Commands.literal("levelRange") + .then(Commands.argument("minLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.argument("maxLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("handicap") + .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) + .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), true)) ) ) ) ) - ) ) ); @@ -210,16 +193,13 @@ public static void register(CommandDispatcher dispatcher) { // min/max + no preview LiteralArgumentBuilder minMaxLevelNoPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("minLevel") - .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("maxLevel") - .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("nopreview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) - ) + .then(Commands.literal("levelRange") + .then(Commands.argument("minLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.argument("maxLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("no-preview") + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) ) ) - ) ) ); @@ -227,33 +207,28 @@ public static void register(CommandDispatcher dispatcher) { // min/max + handicap + no preview LiteralArgumentBuilder minMaxLevelHandicapNoPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("minLevel") - .then(Commands.argument("setMinLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("maxLevel") - .then(Commands.argument("setMaxLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("handicapP1") - .then(Commands.argument("setP1HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("handicapP2") - .then(Commands.argument("setP2HandicapTo", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("nopreview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setMinLevelTo"), IntegerArgumentType.getInteger(c, "setMaxLevelTo"), IntegerArgumentType.getInteger(c, "setP1HandicapTo"), IntegerArgumentType.getInteger(c, "setP2HandicapTo"), false)) - ) - ) + .then(Commands.literal("levelRange") + .then(Commands.argument("minLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.argument("maxLevel", IntegerArgumentType.integer(1,100)) + .then(Commands.literal("handicap") + .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) + .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) + .then(Commands.literal("no-preview") + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), false)) ) ) ) ) ) - ) ) ); // Command called to accept challenges - LiteralArgumentBuilder acceptChallengeAndProperties = Commands.literal("acceptchallenge") + LiteralArgumentBuilder acceptChallengeAndProperties = Commands.literal("accept-challenge") .then(Commands.argument("id", StringArgumentType.string()).executes(c -> acceptChallenge(c, StringArgumentType.getString(c, "id")))); // Command called to deny challenges - LiteralArgumentBuilder rejectChallengeAndProperties = Commands.literal("rejectchallenge") + LiteralArgumentBuilder rejectChallengeAndProperties = Commands.literal("reject-challenge") .then(Commands.argument("id", StringArgumentType.string()).executes(c -> rejectChallenge(c, StringArgumentType.getString(c, "id")))); @@ -281,18 +256,23 @@ public static void register(CommandDispatcher dispatcher) { public static int challengePlayer(CommandContext c, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview) { try { ServerPlayer challengerPlayer = c.getSource().getPlayer(); - ServerPlayer challengedPlayer = c.getArgument("player", EntitySelector.class).findSinglePlayer(c.getSource()); + if (challengerPlayer == null){ + c.getSource().sendFailure(Component.literal("Cannot send challenge, because ChallengerPlayer is null!")); + return 0; + } - if (LAST_SENT_CHALLENGE.containsKey(challengerPlayer.getUUID())) { - if (System.currentTimeMillis() - LAST_SENT_CHALLENGE.get(challengerPlayer.getUUID()) < CHALLENGE_COOLDOWN) { + ServerPlayer challengedPlayer = c.getArgument("player", EntitySelector.class).findSinglePlayer(c.getSource()); + UUID challengerUUID = challengerPlayer.getUUID(); + if (LAST_SENT_CHALLENGE.containsKey(challengerUUID)) { + if (System.currentTimeMillis() - LAST_SENT_CHALLENGE.get(challengerUUID) < CHALLENGE_COOLDOWN) { c.getSource().sendFailure(Component.literal(String.format("You must wait at least %d second(s) before sending another challenge", (int)Math.ceil(CHALLENGE_COOLDOWN / 1000f)))); return 0; } } for (ChallengeRequest request : CHALLENGE_REQUESTS.values()) { - if (request.challengerPlayer.getUUID().equals(challengerPlayer.getUUID())) { - c.getSource().sendFailure(Component.literal(String.format("You already have a pending challenge to %s", request.challengedPlayer.getDisplayName().getString()))); + if (request.challengerPlayer().getUUID().equals(challengerUUID)) { + c.getSource().sendFailure(Component.literal(String.format("You already have a pending challenge to %s", request.challengedPlayer().getDisplayName().getString()))); return 0; } } @@ -302,19 +282,18 @@ public static int challengePlayer(CommandContext c, int minL c.getSource().sendFailure(Component.literal("Cannot send challenge while in-battle")); return 0; } - if (Cobblemon.INSTANCE.getStorage().getParty(challengerPlayer).occupied() == 0) { c.getSource().sendFailure(Component.literal("Cannot send challenge while you have no pokemon!")); return 0; } - float distance = challengedPlayer.distanceTo(challengerPlayer); if (USE_DISTANCE_RESTRICTION && (distance > MAX_DISTANCE || challengedPlayer.level() != challengerPlayer.level())) { - c.getSource().sendFailure(Component.literal(String.format("Target must be less than %d blocks away to initiate a challenge", (int)MAX_DISTANCE))); + c.getSource().sendFailure(Component.literal(String.format("Target must be less than %d blocks away to initiate a challenge", (int) MAX_DISTANCE))); return 0; } + if (challengerPlayer == challengedPlayer) { c.getSource().sendFailure(Component.literal("You may not challenge yourself")); return 0; @@ -327,7 +306,7 @@ public static int challengePlayer(CommandContext c, int minL } ChallengeRequest request = ChallengeUtil.createChallengeRequest(challengerPlayer, challengedPlayer, minLevel, maxLevel, handicapP1, handicapP2, preview); - CHALLENGE_REQUESTS.put(request.id, request); + CHALLENGE_REQUESTS.put(request.id(), request); String levelComponent = (minLevel == maxLevel) ? ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d Pokemon battle", maxLevel) : ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d - %d Pokemon battle", minLevel, maxLevel); String challengerComponent = ChatFormatting.YELLOW + " by " + challengerPlayer.getDisplayName().getString() + "!"; @@ -336,9 +315,9 @@ public static int challengePlayer(CommandContext c, int minL MutableComponent notificationComponent = Component.literal(levelComponent + challengerComponent + optionsComponent + handicapComponent); MutableComponent interactiveComponent = Component.literal("Click to accept or deny: "); - interactiveComponent.append(Component.literal(ChatFormatting.GREEN + "Battle!").setStyle(Style.EMPTY.withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/acceptchallenge %s", request.id))))); + interactiveComponent.append(Component.literal(ChatFormatting.GREEN + "Battle!").setStyle(Style.EMPTY.withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/accept-challenge %s", request.id()))))); interactiveComponent.append(Component.literal(" or ")); - interactiveComponent.append(Component.literal(ChatFormatting.RED + "Reject").setStyle(Style.EMPTY.withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/rejectchallenge %s", request.id))))); + interactiveComponent.append(Component.literal(ChatFormatting.RED + "Reject").setStyle(Style.EMPTY.withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/reject-challenge %s", request.id()))))); challengedPlayer.displayClientMessage(notificationComponent, false); challengedPlayer.displayClientMessage(interactiveComponent, false); challengerPlayer.displayClientMessage(Component.literal(ChatFormatting.YELLOW + String.format("Challenge has been sent to %s", challengedPlayer.getDisplayName().getString())), false); @@ -346,6 +325,7 @@ public static int challengePlayer(CommandContext c, int minL return Command.SINGLE_SUCCESS; } catch (Exception e) { c.getSource().sendFailure(Component.literal("An unexpected error has occurred: " + e.getMessage())); + //noinspection CallToPrintStackTrace e.printStackTrace(); return 0; } @@ -358,16 +338,17 @@ public static int rejectChallenge(CommandContext c, String c c.getSource().sendFailure(Component.literal("Challenge request is not valid")); return 0; } - CHALLENGE_REQUESTS.remove(request.id); - request.challengedPlayer.displayClientMessage(Component.literal(ChatFormatting.RED + "Challenge has been rejected"), false); + CHALLENGE_REQUESTS.remove(request.id()); + request.challengedPlayer().displayClientMessage(Component.literal(ChatFormatting.RED + "Challenge has been rejected"), false); - if (ChallengeUtil.isPlayerOnline(request.challengerPlayer)) { - request.challengerPlayer.displayClientMessage(Component.literal(ChatFormatting.RED + String.format("%s has rejected your challenge.", request.challengedPlayer.getDisplayName().getString())), false); + if (ChallengeUtil.isPlayerOnline(request.challengerPlayer())) { + request.challengerPlayer().displayClientMessage(Component.literal(ChatFormatting.RED + String.format("%s has rejected your challenge.", request.challengedPlayer().getDisplayName().getString())), false); } return Command.SINGLE_SUCCESS; } catch (Exception e) { c.getSource().sendFailure(Component.literal("An unexpected error has occurred: " + e.getMessage())); + //noinspection CallToPrintStackTrace e.printStackTrace(); return 0; } @@ -382,32 +363,32 @@ public static int acceptChallenge(CommandContext c, String c } BattleRegistry br = Cobblemon.INSTANCE.getBattleRegistry(); - if (br.getBattleByParticipatingPlayer(request.challengedPlayer) != null) { + if (br.getBattleByParticipatingPlayer(request.challengedPlayer()) != null) { c.getSource().sendFailure(Component.literal("Cannot accept challenge: you are already in a battle")); return 0; } - else if (br.getBattleByParticipatingPlayer(request.challengerPlayer) != null) { - c.getSource().sendFailure(Component.literal(String.format("Cannot accept challenge: %s is already in a battle", request.challengerPlayer.getDisplayName().getString()))); + else if (br.getBattleByParticipatingPlayer(request.challengerPlayer()) != null) { + c.getSource().sendFailure(Component.literal(String.format("Cannot accept challenge: %s is already in a battle", request.challengerPlayer().getDisplayName().getString()))); return 0; } - if (Cobblemon.INSTANCE.getStorage().getParty(request.challengedPlayer).occupied() == 0) { + if (Cobblemon.INSTANCE.getStorage().getParty(request.challengedPlayer()).occupied() == 0) { c.getSource().sendFailure(Component.literal("Cannot accept challenge: You have no pokemon!")); return 0; } - if (Cobblemon.INSTANCE.getStorage().getParty(request.challengerPlayer).occupied() == 0) { - c.getSource().sendFailure(Component.literal(String.format("Cannot accept challenge: %s has no pokemon... somehow!", request.challengerPlayer.getDisplayName().getString()))); + if (Cobblemon.INSTANCE.getStorage().getParty(request.challengerPlayer()).occupied() == 0) { + c.getSource().sendFailure(Component.literal(String.format("Cannot accept challenge: %s has no pokemon... somehow!", request.challengerPlayer().getDisplayName().getString()))); return 0; } - float distance = request.challengerPlayer.distanceTo(request.challengedPlayer); - if (USE_DISTANCE_RESTRICTION && (distance > MAX_DISTANCE || request.challengerPlayer.level() != request.challengedPlayer.level())) { + float distance = request.challengerPlayer().distanceTo(request.challengedPlayer()); + if (USE_DISTANCE_RESTRICTION && (distance > MAX_DISTANCE || request.challengerPlayer().level() != request.challengedPlayer().level())) { c.getSource().sendFailure(Component.literal(String.format("Target must be less than %d blocks away to accept a challenge", (int)MAX_DISTANCE))); return 0; } ChallengeRequest challengeRequestRemoved = CHALLENGE_REQUESTS.remove(challengeId); - ServerPlayer challengerPlayer = request.challengerPlayer; + ServerPlayer challengerPlayer = request.challengerPlayer(); if (!ChallengeUtil.isPlayerOnline(challengerPlayer)) { c.getSource().sendFailure(Component.literal(String.format("%s is no longer online", challengerPlayer.getDisplayName().getString()))); @@ -417,6 +398,7 @@ else if (br.getBattleByParticipatingPlayer(request.challengerPlayer) != null) { return Command.SINGLE_SUCCESS; } catch (Exception exc) { c.getSource().sendFailure(Component.literal("Unexpected exception when accepting challenge: " + exc.getMessage())); + //noinspection CallToPrintStackTrace exc.printStackTrace(); return 1; } diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java index cd45669..3ab78e3 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; + public class ChallengeConfigProvider implements SimpleConfig.DefaultConfig { private String configContents = ""; diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java index cf8db7e..5264960 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java @@ -1,5 +1,16 @@ package com.turtlehoarder.cobblemonchallenge.event; +import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; +import com.turtlehoarder.cobblemonchallenge.api.LeadPokemonSelection; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; +import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; +import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; +import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonSelectionSession; +import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; +import com.turtlehoarder.cobblemonchallenge.util.FakeStore; +import com.turtlehoarder.cobblemonchallenge.util.FakeStorePosition; + import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.CobblemonNetwork; import com.cobblemon.mod.common.api.Priority; @@ -9,16 +20,7 @@ import com.cobblemon.mod.common.api.storage.*; import com.cobblemon.mod.common.entity.pokemon.PokemonEntity; import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket; -import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; -import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; -import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; -import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; -import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonSelectionSession; -import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; -import com.turtlehoarder.cobblemonchallenge.util.FakeStore; -import com.turtlehoarder.cobblemonchallenge.util.FakeStorePosition; -import kotlin.Unit; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; @@ -30,6 +32,7 @@ import net.minecraft.world.entity.Entity; import java.util.*; +import kotlin.Unit; public class ChallengeEventHandler { @@ -56,7 +59,7 @@ public static void registerEvents() { Since this plugin uses cloned pokemon in its battles, there will be a *cloned* pokemon left behind after the battle is complete. These events ensure that these cloned entities are tracked and removed when a battle ends via Victory, disconnect, or server shutdown */ - public static boolean registerPostVictoryEvent() { + public static void registerPostVictoryEvent() { CobblemonEvents.BATTLE_VICTORY.subscribe(Priority.NORMAL, (battleVictoryEvent) -> { CobblemonChallenge.LOGGER.debug("Battle victory!"); UUID battleId = battleVictoryEvent.getBattle().getBattleId(); @@ -103,7 +106,6 @@ public static boolean registerPostVictoryEvent() { } return Unit.INSTANCE; }); - return true; } // Prevent Challenge-mons from being saved to the world to prevent odd scenarios where duplicates can be spawned and re-caught @@ -197,10 +199,10 @@ public static void onServerTick(MinecraftServer server) { int tickCount = server.getTickCount(); if (tickCount % 20 == 0) { long nowTime = System.currentTimeMillis(); - Iterator> requestIterator = ChallengeCommand.CHALLENGE_REQUESTS.entrySet().iterator(); + Iterator> requestIterator = ChallengeCommand.CHALLENGE_REQUESTS.entrySet().iterator(); while (requestIterator.hasNext()) { - Map.Entry requestMap = requestIterator.next(); - ChallengeCommand.ChallengeRequest request = requestMap.getValue(); + Map.Entry requestMap = requestIterator.next(); + ChallengeRequest request = requestMap.getValue(); if (request.createdTime() + ChallengeConfig.REQUEST_EXPIRATION_MILLIS < nowTime) { if (ChallengeUtil.isPlayerOnline(request.challengedPlayer())) { request.challengedPlayer().displayClientMessage(Component.literal(ChatFormatting.RED + String.format("Challenge from %s has expired", request.challengerPlayer().getDisplayName().getString())), false); @@ -211,7 +213,7 @@ public static void onServerTick(MinecraftServer server) { requestIterator.remove(); } } - Iterator> selectionIterator = ChallengeCommand.ACTIVE_SELECTIONS.entrySet().iterator(); + Iterator> selectionIterator = ChallengeCommand.ACTIVE_SELECTIONS.entrySet().iterator(); while (selectionIterator.hasNext()) { LeadPokemonSelectionSession selectionSession = selectionIterator.next().getValue().selectionWrapper(); selectionSession.doTick(); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenu.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenu.java index 490a915..98d2d40 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenu.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenu.java @@ -6,6 +6,7 @@ import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.inventory.ClickType; import net.minecraft.world.inventory.MenuType; + import org.jetbrains.annotations.NotNull; public class LeadPokemonMenu extends ChestMenu { diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java index faf1348..e3a58b9 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java @@ -1,15 +1,16 @@ package com.turtlehoarder.cobblemonchallenge.gui; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; + import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.CobblemonItems; import com.cobblemon.mod.common.api.storage.party.PartyStore; import com.cobblemon.mod.common.battles.pokemon.BattlePokemon; import com.cobblemon.mod.common.item.PokemonItem; import com.cobblemon.mod.common.pokemon.Pokemon; -import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; -import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; -import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; -import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; + import net.minecraft.ChatFormatting; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringTag; @@ -22,6 +23,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,9 +45,9 @@ private enum MenuState {WAITING_FOR_BOTH, WAITING_FOR_RIVAL, WAITING_FOR_PLAYER} private LeadPokemonMenu openedMenu; public List selectedSlots = new ArrayList(); - private ChallengeCommand.ChallengeRequest request; + private ChallengeRequest request; - public LeadPokemonMenuProvider(LeadPokemonSelectionSession wrapper, ServerPlayer selector, ServerPlayer rivalPlayer, ChallengeCommand.ChallengeRequest request) { + public LeadPokemonMenuProvider(LeadPokemonSelectionSession wrapper, ServerPlayer selector, ServerPlayer rivalPlayer, ChallengeRequest request) { this.selector = selector; this.rival = rivalPlayer; this.selectionSession = wrapper; diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java index 59412df..326333e 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java @@ -1,11 +1,13 @@ package com.turtlehoarder.cobblemonchallenge.gui; -import com.cobblemon.mod.common.battles.BattleFormat; import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBuilderException; -import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; + +import com.cobblemon.mod.common.battles.BattleFormat; + import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -16,7 +18,7 @@ public class LeadPokemonSelectionSession { private final LeadPokemonMenuProvider challengerMenuProvider; private final LeadPokemonMenuProvider challengedMenuProvider; - private final ChallengeCommand.ChallengeRequest originRequest; + private final ChallengeRequest originRequest; private final UUID uuid; public long creationTime; private int pokemonToSelect = 1; @@ -28,7 +30,7 @@ public class LeadPokemonSelectionSession { public static int LEAD_TIMEOUT_MILLIS = 90000; - public LeadPokemonSelectionSession(UUID uuid, long creationTime, ChallengeCommand.ChallengeRequest request) { + public LeadPokemonSelectionSession(UUID uuid, long creationTime, ChallengeRequest request) { this.originRequest = request; this.uuid = uuid; this.creationTime = creationTime; diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java index f193958..1fb8075 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java @@ -7,7 +7,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity; import com.cobblemon.mod.common.pokemon.Pokemon; import com.cobblemon.mod.common.util.LocalizationUtilsKt; -import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; @@ -57,10 +57,9 @@ public static boolean isPlayerOnline(ServerPlayer player) { return player.getServer().getPlayerList().getPlayer(player.getUUID()) != null; } - public static ChallengeCommand.ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview) { + public static ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview) { String key = UUID.randomUUID().toString().replaceAll("-", ""); - ChallengeCommand.ChallengeRequest newRequest = new ChallengeCommand.ChallengeRequest(key, challengerPlayer, challengedPlayer, minLevel, maxLevel, handicapP1, handicapP2, preview, System.currentTimeMillis()); - return newRequest; + return new ChallengeRequest(key, challengerPlayer, challengedPlayer, minLevel, maxLevel, handicapP1, handicapP2, preview, System.currentTimeMillis()); } public static ItemLike getDisplayBlockForPokemon(Pokemon pokemon) { From 62696e95e3094209d146f78eeefc789f2faa9258 Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 18:59:48 -0700 Subject: [PATCH 06/15] Refactor -> Added Bool DEFAULT_SHOW_PREVIEW & implemented with all ChallengeCommand --- .../command/ChallengeCommand.java | 55 ++++++++++++------- .../config/ChallengeConfig.java | 3 + 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java index 946a6ef..a6b7a98 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java @@ -9,6 +9,7 @@ import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.battles.BattleRegistry; +import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.Command; @@ -36,6 +37,7 @@ public class ChallengeCommand { private static final boolean USE_DISTANCE_RESTRICTION = ChallengeConfig.CHALLENGE_DISTANCE_RESTRICTION; private static final int DEFAULT_LEVEL = ChallengeConfig.DEFAULT_CHALLENGE_LEVEL; private static final int DEFAULT_HANDICAP = ChallengeConfig.DEFAULT_HANDICAP; + private static final boolean DEFAULT_SHOW_PREVIEW = ChallengeConfig.DEFAULT_SHOW_PREVIEW; private static final int CHALLENGE_COOLDOWN = ChallengeConfig.CHALLENGE_COOLDOWN_MILLIS; public static HashMap CHALLENGE_REQUESTS = new HashMap<>(); public static final HashMap ACTIVE_SELECTIONS = new HashMap<>(); @@ -65,7 +67,7 @@ public static void register(CommandDispatcher dispatcher) { // (default everything) LiteralArgumentBuilder defaultChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, DEFAULT_SHOW_PREVIEW)) ); // handicap @@ -74,7 +76,7 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("handicap") .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), true)) + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), DEFAULT_SHOW_PREVIEW)) ) ) ) @@ -83,8 +85,10 @@ public static void register(CommandDispatcher dispatcher) { // no preview LiteralArgumentBuilder noPreviewChallengeProperties = Commands.literal("challenge") .then(Commands.argument("player", EntityArgument.player()) - .then(Commands.literal("no-preview") - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) + .then(Commands.literal("showPreview") + .then(Commands.argument("show",BoolArgumentType.bool()) + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, DEFAULT_HANDICAP, DEFAULT_HANDICAP, BoolArgumentType.getBool(c, "show"))) + ) ) ); @@ -94,8 +98,10 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("handicap") .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("no-preview") - .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), false)) + .then(Commands.literal("showPreview") + .then(Commands.argument("show",BoolArgumentType.bool()) + .executes(c -> challengePlayer(c, DEFAULT_LEVEL, DEFAULT_LEVEL, IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), BoolArgumentType.getBool(c, "show"))) + ) ) ) ) @@ -107,7 +113,7 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, DEFAULT_SHOW_PREVIEW)) ) ) @@ -121,7 +127,7 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("handicap") .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), true)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), DEFAULT_SHOW_PREVIEW)) ) ) ) @@ -134,10 +140,11 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.argument("player", EntityArgument.player()) .then(Commands.literal("level") .then(Commands.argument("setLevelTo", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("no-preview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) + .then(Commands.literal("showPreview") + .then(Commands.argument("show",BoolArgumentType.bool()) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, BoolArgumentType.getBool(c, "show"))) + ) ) - ) ) ); @@ -150,8 +157,10 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("handicap") .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("no-preview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), false)) + .then(Commands.literal("showPreview") + .then(Commands.argument("show",BoolArgumentType.bool()) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "setLevelTo"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), BoolArgumentType.getBool(c, "show"))) + ) ) ) ) @@ -166,7 +175,7 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("levelRange") .then(Commands.argument("minLevel", IntegerArgumentType.integer(1,100)) .then(Commands.argument("maxLevel", IntegerArgumentType.integer(1,100)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, true)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, DEFAULT_SHOW_PREVIEW)) ) ) ) @@ -181,7 +190,7 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("handicap") .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), true)) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), DEFAULT_SHOW_PREVIEW)) ) ) ) @@ -196,8 +205,10 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("levelRange") .then(Commands.argument("minLevel", IntegerArgumentType.integer(1,100)) .then(Commands.argument("maxLevel", IntegerArgumentType.integer(1,100)) - .then(Commands.literal("no-preview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, false)) + .then(Commands.literal("showPreview") + .then(Commands.argument("show",BoolArgumentType.bool()) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), DEFAULT_HANDICAP, DEFAULT_HANDICAP, BoolArgumentType.getBool(c, "show"))) + ) ) ) ) @@ -213,8 +224,10 @@ public static void register(CommandDispatcher dispatcher) { .then(Commands.literal("handicap") .then(Commands.argument("self", IntegerArgumentType.integer(-99,99)) .then(Commands.argument("rival", IntegerArgumentType.integer(-99,99)) - .then(Commands.literal("no-preview") - .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), false)) + .then(Commands.literal("showPreview") + .then(Commands.argument("show",BoolArgumentType.bool()) + .executes(c -> challengePlayer(c, IntegerArgumentType.getInteger(c, "minLevel"), IntegerArgumentType.getInteger(c, "maxLevel"), IntegerArgumentType.getInteger(c, "self"), IntegerArgumentType.getInteger(c, "rival"), BoolArgumentType.getBool(c, "show"))) + ) ) ) ) @@ -310,8 +323,8 @@ public static int challengePlayer(CommandContext c, int minL String levelComponent = (minLevel == maxLevel) ? ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d Pokemon battle", maxLevel) : ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d - %d Pokemon battle", minLevel, maxLevel); String challengerComponent = ChatFormatting.YELLOW + " by " + challengerPlayer.getDisplayName().getString() + "!"; - String optionsComponent = request.preview() ? "" : ChatFormatting.RED + " [NoTeamPreview]"; - String handicapComponent = (handicapP1 == 0 && handicapP2 == 0) ? "" : ChatFormatting.BLUE + " [" + challengerPlayer.getDisplayName().getString() + " handicap of " + handicapP1 + "] [" + challengedPlayer.getDisplayName().getString() + " handicap of " + handicapP2 + "]"; + String optionsComponent = request.preview() ? "" : ChatFormatting.GOLD + " [NoTeamPreview]"; + String handicapComponent = (handicapP1 == 0 && handicapP2 == 0) ? "" : ChatFormatting.GREEN + " [" + challengerPlayer.getDisplayName().getString() + " handicap of " + handicapP1 + "] [" + challengedPlayer.getDisplayName().getString() + " handicap of " + handicapP2 + "]"; MutableComponent notificationComponent = Component.literal(levelComponent + challengerComponent + optionsComponent + handicapComponent); MutableComponent interactiveComponent = Component.literal("Click to accept or deny: "); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java index 62ba39a..7312668 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java @@ -10,6 +10,7 @@ public class ChallengeConfig { public static int MAX_CHALLENGE_DISTANCE; public static int DEFAULT_CHALLENGE_LEVEL; public static int DEFAULT_HANDICAP; + public static boolean DEFAULT_SHOW_PREVIEW; public static int REQUEST_EXPIRATION_MILLIS; public static int CHALLENGE_COOLDOWN_MILLIS; @@ -25,6 +26,7 @@ private static void createConfigs() { configs.addKeyValuePair(new Pair<>("maxChallengeDistance", 50)); configs.addKeyValuePair(new Pair<>("defaultChallengeLevel", 50)); configs.addKeyValuePair(new Pair<>("defaultHandicap", 0)); + configs.addKeyValuePair(new Pair<>("defaultShowPreview", true)); configs.addKeyValuePair(new Pair<>("challengeExpirationTime", 60000)); configs.addKeyValuePair(new Pair<>("challengeCooldownTime", 5000)); } @@ -34,6 +36,7 @@ private static void assignConfigs() { CHALLENGE_DISTANCE_RESTRICTION = CONFIG.getOrDefault("challengeDistanceRestriction", true); DEFAULT_CHALLENGE_LEVEL = CONFIG.getOrDefault("defaultChallengeLevel", 50); DEFAULT_HANDICAP = CONFIG.getOrDefault("defaultHandicap", 0); + DEFAULT_SHOW_PREVIEW = CONFIG.getOrDefault("defaultShowPreview", true); MAX_CHALLENGE_DISTANCE = CONFIG.getOrDefault("maxChallengeDistance", 50); REQUEST_EXPIRATION_MILLIS = CONFIG.getOrDefault("challengeExpirationTime", 60000); } From 2a5b4284268b165c4d466dbbe140ed8f18db8ca2 Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 19:36:43 -0700 Subject: [PATCH 07/15] Fix - switched class from boolean -> Boolean --- .../cobblemonchallenge/config/ChallengeConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java index 7312668..03189c6 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfig.java @@ -10,7 +10,7 @@ public class ChallengeConfig { public static int MAX_CHALLENGE_DISTANCE; public static int DEFAULT_CHALLENGE_LEVEL; public static int DEFAULT_HANDICAP; - public static boolean DEFAULT_SHOW_PREVIEW; + public static Boolean DEFAULT_SHOW_PREVIEW; public static int REQUEST_EXPIRATION_MILLIS; public static int CHALLENGE_COOLDOWN_MILLIS; From f7bd5fa40a0af7a79b9a1dcd49e533da03937845 Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 19:37:18 -0700 Subject: [PATCH 08/15] Updated README to accurately reflect the new commands & parameters --- README.md | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index c81e3db..875e803 100644 --- a/README.md +++ b/README.md @@ -4,73 +4,71 @@ Cobblemon Challenge is an extremely simple plugin for Cobblemon that makes challenging your friends and rivals to flat-level Pokemon battles easier! This is a server-side plugin that only needs to be installed on the Server. -#### Commands -There are four possible challenge properties: -1. Username (required) -2. Level or min/maxLevel (optional & mutually exclusive) -3. Handicap (optional) -4. NoPreview (optional, true if excluded & false if specified) - +#### Command Parameters ```/challenge ``` - Challenges specified player to a lvl 50 pokemon battle. They may accept or deny this challenge. -```/challenge level ``` - Challenges specified player to a lvl X battle where X can be 1-100. +```/challenge level ``` - Challenges specified player to a lvl `````` battle where `````` can be **1 min** & **100 max**. -```/challenge minLevel maxLevel ``` - Challenges specified player to a lvl - battle where & can be 1-100. +```/challenge levelRange ``` - Challenges specified player to a lvl where the Pokemon's levels are clamped between the ``````&`````` where ``````&`````` can be **1 min** & **100 max**. -```/challenge handicapP1 handicapP1 ``` - Challenges specified player to a battle where player1 (challenger) & player2 (challenged) are offset by the specified levels. can be from (-99)-(99). +```/challenge handicap ``` - Challenges specified player to a battle where `````` (the challenger) & `````` (the challenged) Pokemon level is offset by the input level. ``````&`````` can be from **-99 min** & **99 max**. -```/challenge noPreview``` - Challenges specified player to a default battle with no preview of the rivals pokemon during starter selection. +```/challenge showPreview ``` - Challenges specified player to a default battle with no preview of the rivals pokemon during starter selection. -##### Command Input Order: -There are 12 possible input combinations for challenge properties. The order of input must be username -> level or min/maxLevel -> handicap -> noPreview. If the challenge property is optional you may skip it when inputting the command. The default values will be used. +##### Notes About Command Parameters: +1. The order of parameter input must be ```username``` -> ```level/levelRange``` -> ```handicap``` -> ```showPreview```. +2. If the challenge property is optional & not specified in the command. The default values from the config file will be used. -##### All Possible Commands: +#### Configurations +There are numerous options that will allow you to customize the Challenge experience to your server's needs. These +settings can be found in the Cobblemon Challenge config file: -```/challenge ``` +```challengeDistanceRestriction``` - The value that determines if challenges are restricted by distance. Set to **false** if you would +want no restrictions on distance. This value is set to **true** by default. -```/challenge handicapP1 handicapP1 ``` +```maxChallengeDistance``` - If challengeDistanceRestriction is set to **true**, then this value defines the max distance +that a challenge can be sent. This is set to 50 blocks by default. -```/challenge noPreview``` +```defaultChallengeLevel``` - The value that determines the level of a challenge if there is no level specified by the challenger. +This is set to 50 by default for lvl 50 battles. -```/challenge handicapP1 handicapP1 noPreview``` +```defaultHandicap``` - The value that determines each player's final level of each Pokemon if a handicap is not specified by the challenger. +This is set to 0 by default. +```defaultShowPreview``` - If defaultShowPreview is set to **true** both players will see which pokemon the opponent is bringing to battle. If defaultShowPreview is set to **false** both players parties will be hidden from their opponent. +(Note: Even when defaultShowPreview is true the player will never see the opponents chosen lead pokemon) +This is set to true by default. -```/challenge level ``` +```challengeExpirationTime``` - The value that determines how long a challenge should be pending before it expires. This is +set to 60000 milliseconds / 1 minute by default. -```/challenge level handicapP1 handicapP1 ``` +```challengeCooldownTime``` - The value that determines how long a player must wait before sending a consecutive request. This value is +set to 5000 milliseconds / 5 seconds by default, though players will need to wait until their existing challenge expires before sending another one. -```/challenge level noPreview``` +### All Possible Commands: -```/challenge level handicapP1 handicapP1 noPreview``` +```/challenge ``` +```/challenge handicap ``` -```/challenge minLevel maxLevel ``` +```/challenge showPreview ``` -```/challenge minLevel maxLevel handicapP1 handicapP1 ``` +```/challenge handicap showPreview ``` -```/challenge minLevel maxLevel noPreview``` -```/challenge minLevel maxLevel handicapP1 handicapP1 noPreview``` +```/challenge level ``` +```/challenge level handicap ``` -#### Configurations -There are numerous options that will allow you to customize the Challenge experience to your server's needs. These -settings can be found in the Cobblemon Challenge config file: +```/challenge level showPreview ``` -```challengeDistanceRestriction``` - The value that determines if challenges are restricted by distance. Set to **false** if you would -want no restrictions on distance. This value is set to **true** by default. +```/challenge level handicap showPreview ``` -```maxChallengeDistance``` - If challengeDistanceRestriction is set to **true**, then this value defines the max distance -that a challenge can be sent. This is set to 50 blocks by default. -```defaultChallengeLevel``` - The value that determines the level of a challenge if there is not level specified by the challenger. -This is set to 50 by default for lvl 50 battles. +```/challenge levelRange ``` -```defaultHandicap``` - The value that determines each players final level of each Pokemon if a handicap is not specified by the challenger. -This is set to 0 by default. +```/challenge levelRange handicap ``` -```challengeExpirationTime``` - The value that determines how long a challenge should be pending before it expires. This is -set to 60000 milliseconds / 1 minute by default. +```/challenge levelRange showPreview ``` -```challengeCooldownTime``` - The value that determines how long a player must wait before sending a consecutive request. This value is -set to 5000 milliseconds / 5 seconds by default, though players will need to wait until their existing challenge expires before sending another one. +```/challenge levelRange handicap showPreview ``` \ No newline at end of file From 2c435baa90abfe9396ad64b422a3bcbbcde60e18 Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 21:56:09 -0700 Subject: [PATCH 09/15] Refactor -> added kotlin classes with the same functionality as FakeStore & FakeStorePosition without casting. - Note: This should work unless some of the functions need to be called in the FakeStore. - Possible refinement: We could trim out the playerUUID completely if it does not matter. The FakePlayerStore has a default value, but I am passing in the playerUUID if one is found b/c that is how the first iteration did it. I tested this with some basic battles including disconnecting & didn't run into any problems --- .../event/ChallengeEventHandler.java | 37 ++----- .../api/storage/FakePokemonStore.kt | 100 ++++++++++++++++++ .../api/storage/party/FakePartyPosition.kt | 17 +++ .../api/storage/party/FakePlayerPartyStore.kt | 15 +++ 4 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt create mode 100644 src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt create mode 100644 src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePlayerPartyStore.kt diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java index 5264960..7388151 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java @@ -3,13 +3,14 @@ import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; import com.turtlehoarder.cobblemonchallenge.api.LeadPokemonSelection; +import com.turtlehoarder.cobblemonchallenge.api.storage.FakePokemonStore; +import com.turtlehoarder.cobblemonchallenge.api.storage.party.FakePartyPosition; +import com.turtlehoarder.cobblemonchallenge.api.storage.party.FakePlayerPartyStore; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; import com.turtlehoarder.cobblemonchallenge.gui.LeadPokemonSelectionSession; import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; -import com.turtlehoarder.cobblemonchallenge.util.FakeStore; -import com.turtlehoarder.cobblemonchallenge.util.FakeStorePosition; import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.CobblemonNetwork; @@ -40,19 +41,10 @@ public static void registerEvents() { registerPostVictoryEvent(); registerChallengeLootPrevention(); registerCobblemonSavePrevention(); - ServerEntityEvents.ENTITY_LOAD.register((entity, server) -> { - checkSpawn(entity); - }); - ServerPlayConnectionEvents.DISCONNECT.register((event, server) -> { - onPlayerLoggedOut(event.getPlayer()); - }); - - ServerLifecycleEvents.SERVER_STOPPING.register((server) -> { - onServerShutdown(); - }); - + ServerEntityEvents.ENTITY_LOAD.register((entity, server) -> checkSpawn(entity)); + ServerPlayConnectionEvents.DISCONNECT.register((event, server) -> onPlayerLoggedOut(event.getPlayer())); + ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onServerShutdown()); ServerTickEvents.END_SERVER_TICK.register(ChallengeEventHandler::onServerTick); - } /* @@ -164,22 +156,15 @@ public static void checkSpawn(Entity entity) { CobblemonChallenge.LOGGER.debug(String.format("Entity Joined already in battle: %s | Battle id %s", entity.getDisplayName().getString(), pokemonEntity.getBattleId())); ChallengeBattleBuilder.clonedPokemonList.add(pokemonEntity); // Trick Cobblemon into thinking the clones are *not* wild pokemon. This will prevent duplicates being caught if something unexpected happens to the battle, like /stopbattle or a server crash - UUID foundplayerUUID = null; PokemonBattle pb = ChallengeUtil.getAssociatedBattle(pokemonEntity); if (pb != null) { - foundplayerUUID = ChallengeUtil.getOwnerUuidOfClonedPokemon(pb, pokemonEntity); - } - if (pb != null) { + UUID foundplayerUUID = ChallengeUtil.getOwnerUuidOfClonedPokemon(pb, pokemonEntity); UUID playerUUID = (foundplayerUUID != null ? foundplayerUUID : new UUID(0,0)); - FakeStore fakeStore = new FakeStore(playerUUID); - // World's worst casting. Don't do this at home. - PokemonStore fakePartyStore = (PokemonStore)(PokemonStore) fakeStore; - pokemonEntity.getPokemon().getStoreCoordinates().set(new StoreCoordinates<>(fakePartyStore, new FakeStorePosition())); + FakePlayerPartyStore fakePlayerStore = new FakePlayerPartyStore(playerUUID); + PokemonStore fakePokemonStore = new FakePokemonStore(fakePlayerStore,playerUUID); + pokemonEntity.getPokemon().getStoreCoordinates().set(new StoreCoordinates<>(fakePokemonStore, new FakePartyPosition())); pokemonEntity.getBusyLocks().add("Cloned_Pokemon"); // Busy lock prevents others from interacting with cloned pokemon } - - - } } } @@ -188,7 +173,7 @@ public static void onServerShutdown() { CobblemonChallenge.LOGGER.debug("Performing Server Shutdown tasks for Cobblemon Challenge"); if (!ChallengeBattleBuilder.clonedPokemonList.isEmpty()) { CobblemonChallenge.LOGGER.debug(String.format("Cloned pokemon (%d) from challenges detected. Removing all before server shuts down", ChallengeBattleBuilder.clonedPokemonList.size())); - ArrayList clonedPokemonCopyList = new ArrayList(ChallengeBattleBuilder.clonedPokemonList); // Create a copy since other list may be altered by despawn events + ArrayList clonedPokemonCopyList = new ArrayList<>(ChallengeBattleBuilder.clonedPokemonList); // Create a copy since other list may be altered by despawn events clonedPokemonCopyList.forEach(pokemonEntity -> pokemonEntity.remove(Entity.RemovalReason.DISCARDED)); clonedPokemonCopyList.clear(); } diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt new file mode 100644 index 0000000..48523a4 --- /dev/null +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt @@ -0,0 +1,100 @@ +package com.turtlehoarder.cobblemonchallenge.api.storage + +import com.turtlehoarder.cobblemonchallenge.api.storage.party.FakePlayerPartyStore + +import com.cobblemon.mod.common.api.reactive.Observable +import com.cobblemon.mod.common.api.storage.PokemonStore +import com.cobblemon.mod.common.api.storage.StoreCoordinates +import com.cobblemon.mod.common.api.storage.StorePosition +import com.cobblemon.mod.common.api.storage.factory.PokemonStoreFactory +import com.cobblemon.mod.common.pokemon.Pokemon +import com.google.gson.JsonObject +import net.minecraft.server.level.ServerPlayer +import net.minecraft.nbt.CompoundTag +import java.util.* + +class FakePokemonStore( + private val playerStore : FakePlayerPartyStore, + /** The UUID of the store. The exact uniqueness requirements depend on the method used for saving. */ + override val uuid: UUID = playerStore.playerUUID +): PokemonStore() { + + /** Gets the [Pokemon] at the given position. */ + override operator fun get(position: StorePosition): Pokemon?{ + return null + } + + /** Gets the first empty position that a [Pokemon] might be put. */ + override fun getFirstAvailablePosition(): StorePosition? { + return null + } + + /** Gets an iterable of all [ServerPlayer]s that should be notified of any changes to the Pokémon in this store. */ + override fun getObservingPlayers(): Iterable { + return emptyList() + } + + /** Sends the contents of this store to a player as if they've never seen it before. This initializes the store then sends each contained Pokémon. */ + override fun sendTo(player: ServerPlayer) { } + + /** + * Runs initialization logic for this store, knowing that it has just been constructed in a [PokemonStoreFactory]. + * + * The minimum of what this function should do is iterate over all the Pokémon in this store and set their store + * coordinates. + * + * If this does not get called, or it does not do its job properly, serious de-sync issues may follow. + */ + override fun initialize() { } + + override fun iterator(): Iterator { + TODO("Not yet implemented") + } + + /** + * Sets the given position with the given [Pokemon], which can be null. This is for internal use only because + * other, more public methods will additionally send updates to the client, and for logical reasons this means + * there must be an internal and external set method. + */ + override fun setAtPosition(position: StorePosition, pokemon: Pokemon?) { + + } + + /** Returns true if the given position is pointing to a legitimate location in this store. */ + override fun isValidPosition(position: StorePosition): Boolean { + return true + } + + override fun saveToNBT(nbt: CompoundTag): CompoundTag { + return CompoundTag() + } + + override fun loadFromNBT(nbt: CompoundTag): PokemonStore { + return this + } + + override fun saveToJSON(json: JsonObject): JsonObject { + return JsonObject() + } + + override fun loadFromJSON(json: JsonObject): PokemonStore { + return this + } + + override fun savePositionToNBT(position: StorePosition, nbt: CompoundTag) { + + } + + override fun loadPositionFromNBT(nbt: CompoundTag): StoreCoordinates { + TODO("Not yet implemented") + } + + /** + * Returns an [Observable] that emits Unit whenever there is a change to this store. This includes any save-worthy + * change to a [Pokemon] contained in the store. You can access an [Observable] in each [Pokemon] that emits Unit for + * each change, accessed by [Pokemon.getChangeObservable]. + */ + override fun getAnyChangeObservable(): Observable { + return Observable.just(Unit) // @Eric: I think this should work but... I'll defer to you lol ;) - David + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt new file mode 100644 index 0000000..b42e29e --- /dev/null +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt @@ -0,0 +1,17 @@ +package com.turtlehoarder.cobblemonchallenge.api.storage.party + +import com.cobblemon.mod.common.api.storage.StorePosition +import com.cobblemon.mod.common.api.storage.party.PartyPosition +import com.cobblemon.mod.common.net.IntSize +import com.cobblemon.mod.common.util.readSizedInt +import com.cobblemon.mod.common.util.writeSizedInt +import net.minecraft.network.FriendlyByteBuf + +class FakePartyPosition(val slot: Int = 0) : StorePosition { + companion object { + fun FriendlyByteBuf.writePartyPosition(position: FakePartyPosition) { + writeSizedInt(IntSize.U_BYTE, position.slot) + } + fun FriendlyByteBuf.readPartyPosition() = FakePartyPosition(readSizedInt(IntSize.U_BYTE)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePlayerPartyStore.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePlayerPartyStore.kt new file mode 100644 index 0000000..b85397c --- /dev/null +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePlayerPartyStore.kt @@ -0,0 +1,15 @@ +package com.turtlehoarder.cobblemonchallenge.api.storage.party + +import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore +import com.cobblemon.mod.common.pokemon.Pokemon +import java.util.* + +class FakePlayerPartyStore( + private val fakeUUID: UUID = UUID(0, 0) +) : PlayerPartyStore(fakeUUID, fakeUUID) { + + // override function to serve the same purpose as the previous FakeStore + override fun add(pokemon: Pokemon): Boolean { + return true + } +} \ No newline at end of file From 7dfd4141a8b98d1135a656c4f21bd3d12bb8945f Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 22:51:14 -0700 Subject: [PATCH 10/15] Clean up Type parameter for FakePokemonStore --- .../event/ChallengeEventHandler.java | 4 ++-- .../api/storage/FakePokemonStore.kt | 2 +- .../api/storage/party/FakePartyPosition.kt | 15 +++------------ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java index 7388151..3b8982c 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/event/ChallengeEventHandler.java @@ -161,7 +161,7 @@ public static void checkSpawn(Entity entity) { UUID foundplayerUUID = ChallengeUtil.getOwnerUuidOfClonedPokemon(pb, pokemonEntity); UUID playerUUID = (foundplayerUUID != null ? foundplayerUUID : new UUID(0,0)); FakePlayerPartyStore fakePlayerStore = new FakePlayerPartyStore(playerUUID); - PokemonStore fakePokemonStore = new FakePokemonStore(fakePlayerStore,playerUUID); + PokemonStore fakePokemonStore = new FakePokemonStore(fakePlayerStore,playerUUID); pokemonEntity.getPokemon().getStoreCoordinates().set(new StoreCoordinates<>(fakePokemonStore, new FakePartyPosition())); pokemonEntity.getBusyLocks().add("Cloned_Pokemon"); // Busy lock prevents others from interacting with cloned pokemon } @@ -173,7 +173,7 @@ public static void onServerShutdown() { CobblemonChallenge.LOGGER.debug("Performing Server Shutdown tasks for Cobblemon Challenge"); if (!ChallengeBattleBuilder.clonedPokemonList.isEmpty()) { CobblemonChallenge.LOGGER.debug(String.format("Cloned pokemon (%d) from challenges detected. Removing all before server shuts down", ChallengeBattleBuilder.clonedPokemonList.size())); - ArrayList clonedPokemonCopyList = new ArrayList<>(ChallengeBattleBuilder.clonedPokemonList); // Create a copy since other list may be altered by despawn events + ArrayList clonedPokemonCopyList = new ArrayList(ChallengeBattleBuilder.clonedPokemonList); // Create a copy since other list may be altered by despawn events clonedPokemonCopyList.forEach(pokemonEntity -> pokemonEntity.remove(Entity.RemovalReason.DISCARDED)); clonedPokemonCopyList.clear(); } diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt index 48523a4..95745d6 100644 --- a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/FakePokemonStore.kt @@ -13,7 +13,7 @@ import net.minecraft.server.level.ServerPlayer import net.minecraft.nbt.CompoundTag import java.util.* -class FakePokemonStore( +class FakePokemonStore( private val playerStore : FakePlayerPartyStore, /** The UUID of the store. The exact uniqueness requirements depend on the method used for saving. */ override val uuid: UUID = playerStore.playerUUID diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt index b42e29e..cc92d83 100644 --- a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/api/storage/party/FakePartyPosition.kt @@ -1,17 +1,8 @@ package com.turtlehoarder.cobblemonchallenge.api.storage.party import com.cobblemon.mod.common.api.storage.StorePosition -import com.cobblemon.mod.common.api.storage.party.PartyPosition -import com.cobblemon.mod.common.net.IntSize -import com.cobblemon.mod.common.util.readSizedInt -import com.cobblemon.mod.common.util.writeSizedInt -import net.minecraft.network.FriendlyByteBuf -class FakePartyPosition(val slot: Int = 0) : StorePosition { - companion object { - fun FriendlyByteBuf.writePartyPosition(position: FakePartyPosition) { - writeSizedInt(IntSize.U_BYTE, position.slot) - } - fun FriendlyByteBuf.readPartyPosition() = FakePartyPosition(readSizedInt(IntSize.U_BYTE)) - } +class FakePartyPosition(private var slot: Int = 0) : StorePosition { + fun set(newSlot: Int) { slot = newSlot } + fun get(): Int { return slot } } \ No newline at end of file From 9be382716386f92d743a3c4eaafd2d8bcf52167b Mon Sep 17 00:00:00 2001 From: Eight Date: Wed, 12 Jun 2024 23:48:43 -0700 Subject: [PATCH 11/15] Added WIP ChallengeBattlePokemon subclass of BattlePokemon --- .../battle/pokemon/ChallengeBattlePokemon.kt | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt new file mode 100644 index 0000000..d785504 --- /dev/null +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt @@ -0,0 +1,75 @@ +package com.turtlehoarder.cobblemonchallenge.battle.pokemon + +import com.cobblemon.mod.common.Cobblemon +import com.cobblemon.mod.common.api.battles.model.actor.ActorType +import com.cobblemon.mod.common.api.tags.CobblemonItemTags +import com.cobblemon.mod.common.battles.pokemon.BattlePokemon +import com.cobblemon.mod.common.entity.pokemon.PokemonEntity +import com.cobblemon.mod.common.pokemon.evolution.requirements.LevelRequirement +import com.cobblemon.mod.common.pokemon.Pokemon + +import kotlin.math.pow +import kotlin.math.roundToInt + +class ChallengeBattlePokemon( + originalPokemon: Pokemon, + effectedPokemon: Pokemon, + postBattleEntityOperation: (PokemonEntity) -> Unit +) : BattlePokemon(originalPokemon) { + + companion object { + fun safeCopyOf(pokemon: Pokemon): ChallengeBattlePokemon = ChallengeBattlePokemon( + originalPokemon = pokemon, + // TODO Need to apply changes to the effected pokemon as it is the one shown in battle + // > See com.cobblemon.mod.common.battles.pokemon.BattlePokemon + // > all val are based off of effectedPokemon in base class + effectedPokemon = pokemon.clone(), + postBattleEntityOperation = { + // TODO inject post battle logic here + entity -> entity.discard() + } + ) + } + + // TODO: call from mixin -> if BattlePokemon is ChallengeBattlePokemon in PokemonBattle.end() -> + // redirect here else -> + // call ExperienceCalculator.Calculate as normally done + // original call -> + // com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator.Calculate + fun calculateExperience(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon, participationMultiplier: Double): Int { + // This is meant to be a division but this is due to the intended behavior of handling the 2.0 sent over from Exp. All in modern Pokémon + + // Tweaked method to get exp gain that reflects that original pokemon's unaltered levels + val term2 = 1 * participationMultiplier + val victorPokemon = opponentPokemon.effectedPokemon + val victorLevel = victorPokemon.level + val baseExp = opponentPokemon.originalPokemon.form.baseExperienceYield + val opponentLevel = victorLevel + (opponentPokemon.effectedPokemon.level - battlePokemon.effectedPokemon.level) + val term1 = (baseExp * opponentLevel) / 5.0 + val term3 = (((2.0 * opponentLevel) + 10) / (opponentLevel + victorLevel + 10)).pow(2.5) + + // eight addition not yet in base mod -> can use ternary, but it is super wide, so I tried to keep it readable + val validOriginalTrainer = victorPokemon.originalTrainer != null && battlePokemon.actor.type == ActorType.PLAYER + val differentUuid = victorPokemon.originalTrainer != battlePokemon.actor.uuid.toString() + val validName = victorPokemon.originalTrainerName != null + val differentName = victorPokemon.originalTrainerName != battlePokemon.actor.getName().toString() + val nonOtBonus = when { + validOriginalTrainer && differentUuid && (!validName || differentName) -> 1.7 + validName && differentName && !validOriginalTrainer -> 1.7 + else -> 1.0 + } + + val luckyEggMultiplier = if (battlePokemon.effectedPokemon.heldItem().tags.anyMatch { tag -> + tag == CobblemonItemTags.LUCKY_EGG + }) Cobblemon.config.luckyEggMultiplier else 1.0 + val evolutionMultiplier = if (battlePokemon.effectedPokemon.evolutionProxy.server().any { evolution -> + val requirements = evolution.requirements.asSequence() + requirements.any { it is LevelRequirement } && requirements.all { it.check(battlePokemon.effectedPokemon) } + }) 1.2 else 1.0 + val affectionMultiplier = if (battlePokemon.effectedPokemon.friendship >= 220) 1.2 else 1.0 + val gimmickBoost = Cobblemon.config.experienceMultiplier + + val term4 = term1 * term2 * term3 + 1 + return (term4 * nonOtBonus * luckyEggMultiplier * evolutionMultiplier * affectionMultiplier * gimmickBoost).roundToInt() + } +} \ No newline at end of file From 9190ec0191922aec7db1aaff09cc850d4d5fc0cb Mon Sep 17 00:00:00 2001 From: Eight Date: Thu, 13 Jun 2024 00:40:49 -0700 Subject: [PATCH 12/15] WIP addition & extract ChallengeProperties: - Added files for mixins & 'Universal Difficulty' config - Extracted ChallengeProperties to class --- .../CobblemonChallenge.java | 2 + .../api/ChallengeProperties.java | 22 ++++++ .../api/ChallengeRequest.java | 6 +- .../command/ChallengeCommand.java | 2 +- .../config/DifficultyConfig.java | 48 ++++++++++++ .../gui/LeadPokemonMenuProvider.java | 12 +-- .../gui/LeadPokemonSelectionSession.java | 12 +-- .../mixin/pvePreBattleBuilderMixin.java | 14 ++++ .../mixin/pvp1v1PreBattleBuilderMixin.java | 75 +++++++++++++++++++ .../util/ChallengeUtil.java | 5 +- src/main/resources/mixins.common.json | 15 ++++ 11 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java create mode 100644 src/main/resources/mixins.common.json diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java index e5f3708..ae3fcb8 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java @@ -2,6 +2,7 @@ import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; +import com.turtlehoarder.cobblemonchallenge.config.DifficultyConfig; import com.turtlehoarder.cobblemonchallenge.event.ChallengeEventHandler; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -17,6 +18,7 @@ public class CobblemonChallenge implements ModInitializer { @Override public void onInitialize() { ChallengeConfig.registerConfigs(); + DifficultyConfig.registerConfigs(); ChallengeEventHandler.registerEvents(); CommandRegistrationCallback.EVENT.register((commandDispatcher, commandBuildContext, commandSelection) -> ChallengeCommand.register(commandDispatcher)); } diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java new file mode 100644 index 0000000..374ca71 --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java @@ -0,0 +1,22 @@ +package com.turtlehoarder.cobblemonchallenge.api; + +public class ChallengeProperties { + public ChallengeProperties(int minLevel,int maxLevel,int handicapP1,int handicapP2,boolean showPreview) { + _minLevel = minLevel; + _maxLevel = maxLevel; + _handicapP1 = handicapP1; + _handicapP2 = handicapP2; + _showPreview = showPreview; + } + private int _minLevel; + private int _maxLevel; + private int _handicapP1; + private int _handicapP2; + private boolean _showPreview; + + public int getMinLevel () { return _minLevel; } + public int getMaxLevel () { return _maxLevel; } + public int getHandicapP1 () { return _handicapP1; } + public int getHandicapP2 () { return _handicapP2; } + public boolean getShowPreview () { return _showPreview; } +} diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java index f930b02..b348e82 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeRequest.java @@ -6,11 +6,7 @@ public record ChallengeRequest ( String id, ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, - int minLevel, - int maxLevel, - int handicapP1, - int handicapP2, - boolean preview, + ChallengeProperties properties, long createdTime ) { diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java index a6b7a98..d8554e2 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/command/ChallengeCommand.java @@ -323,7 +323,7 @@ public static int challengePlayer(CommandContext c, int minL String levelComponent = (minLevel == maxLevel) ? ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d Pokemon battle", maxLevel) : ChatFormatting.YELLOW + String.format("You have been challenged to a " + ChatFormatting.BOLD + "level %d - %d Pokemon battle", minLevel, maxLevel); String challengerComponent = ChatFormatting.YELLOW + " by " + challengerPlayer.getDisplayName().getString() + "!"; - String optionsComponent = request.preview() ? "" : ChatFormatting.GOLD + " [NoTeamPreview]"; + String optionsComponent = request.properties().getShowPreview() ? "" : ChatFormatting.GOLD + " [NoTeamPreview]"; String handicapComponent = (handicapP1 == 0 && handicapP2 == 0) ? "" : ChatFormatting.GREEN + " [" + challengerPlayer.getDisplayName().getString() + " handicap of " + handicapP1 + "] [" + challengedPlayer.getDisplayName().getString() + " handicap of " + handicapP2 + "]"; MutableComponent notificationComponent = Component.literal(levelComponent + challengerComponent + optionsComponent + handicapComponent); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java new file mode 100644 index 0000000..2796692 --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java @@ -0,0 +1,48 @@ +package com.turtlehoarder.cobblemonchallenge.config; + +import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; +import com.mojang.datafixers.util.Pair; + +// temp class for possible configs while testing new features +public class DifficultyConfig { + + public static SimpleConfig CONFIG; + private static ChallengeConfigProvider configs; + public static Boolean USE_UNIVERSAL_LEVEL; + public static Boolean USE_UNIVERSAL_LEVEL_RANGE; + public static Boolean USE_UNIVERSAL_HANDICAP; + public static int DEFAULT_UNIVERSAL_LEVEL; + public static int DEFAULT_UNIVERSAL_MIN_LEVEL; + public static int DEFAULT_UNIVERSAL_MAX_LEVEL; + public static int DEFAULT_UNIVERSAL_HANDICAP; + public static Boolean DEFAULT_SHOW_PREVIEW; + + public static void registerConfigs() { + CobblemonChallenge.LOGGER.info("Loading Universal Difficulty Configs"); + configs = new ChallengeConfigProvider(); + createConfigs(); + CONFIG = SimpleConfig.of(CobblemonChallenge.MODID + "-universal-difficulty-config").provider(configs).request(); + assignConfigs(); + } + private static void createConfigs() { + configs.addKeyValuePair(new Pair<>("useUniversalLevel", false)); + configs.addKeyValuePair(new Pair<>("useUniversalLevelRange", true)); + configs.addKeyValuePair(new Pair<>("useUniversalHandicap", true)); + configs.addKeyValuePair(new Pair<>("defaultUniversalLevel", 50)); + configs.addKeyValuePair(new Pair<>("defaultUniversalMinLevel", 50)); + configs.addKeyValuePair(new Pair<>("defaultUniversalMinLevel", 0)); + configs.addKeyValuePair(new Pair<>("defaultUniversalHandicap", 0)); + configs.addKeyValuePair(new Pair<>("defaultShowPreview", true)); + } + + private static void assignConfigs() { + USE_UNIVERSAL_LEVEL = CONFIG.getOrDefault("useUniversalLevel", false); + USE_UNIVERSAL_LEVEL_RANGE = CONFIG.getOrDefault("useUniversalLevelRange", true); + USE_UNIVERSAL_HANDICAP = CONFIG.getOrDefault("useUniversalHandicap", true); + DEFAULT_UNIVERSAL_LEVEL = CONFIG.getOrDefault("defaultUniversalLevel", 50); + DEFAULT_UNIVERSAL_MIN_LEVEL = CONFIG.getOrDefault("defaultUniversalMinLevel", 50); + DEFAULT_UNIVERSAL_MAX_LEVEL = CONFIG.getOrDefault("defaultUniversalMinLevel", 0); + DEFAULT_UNIVERSAL_HANDICAP = CONFIG.getOrDefault("defaultUniversalHandicap", 0); + DEFAULT_SHOW_PREVIEW = CONFIG.getOrDefault("defaultShowPreview", true); + } +} diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java index e3a58b9..1ed4177 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java @@ -45,7 +45,7 @@ private enum MenuState {WAITING_FOR_BOTH, WAITING_FOR_RIVAL, WAITING_FOR_PLAYER} private LeadPokemonMenu openedMenu; public List selectedSlots = new ArrayList(); - private ChallengeRequest request; + private final ChallengeRequest request; public LeadPokemonMenuProvider(LeadPokemonSelectionSession wrapper, ServerPlayer selector, ServerPlayer rivalPlayer, ChallengeRequest request) { this.selector = selector; @@ -72,8 +72,8 @@ private void setupPokemonRepresentation(LeadPokemonMenu leadPokemonMenu) { PartyStore p2Party = Cobblemon.INSTANCE.getStorage().getParty(rival); setupGlassFiller(leadPokemonMenu); - int handicapP1 = (this.selector == request.challengerPlayer()) ? request.handicapP1() : request.handicapP2(); - int handicapP2 = (this.selector == request.challengerPlayer()) ? request.handicapP2() : request.handicapP1(); + int handicapP1 = (this.selector == request.challengerPlayer()) ? request.properties().getHandicapP1() : request.properties().getHandicapP2(); + int handicapP2 = (this.selector == request.challengerPlayer()) ? request.properties().getHandicapP2() : request.properties().getHandicapP1(); for (int x = 0; x < p1Party.size(); x ++) { int itemSlot = x * 9; // Lefthand column of the menu @@ -81,7 +81,7 @@ private void setupPokemonRepresentation(LeadPokemonMenu leadPokemonMenu) { if (pokemon == null) // Skip any empty slots in the pokemon team continue; BattlePokemon copy = BattlePokemon.Companion.safeCopyOf(pokemon); - int adjustedLevelP1 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.minLevel(), request.maxLevel(), handicapP1); + int adjustedLevelP1 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.properties().getMinLevel(), request.properties().getMaxLevel(), handicapP1); pokemon = ChallengeUtil.applyFormatTransformations(ChallengeFormat.STANDARD_6V6, copy, adjustedLevelP1).getEffectedPokemon(); // Apply battle transformations to each pokemon ItemStack pokemonItem = PokemonItem.from(pokemon, 1); pokemonItem.setHoverName(Component.literal(ChatFormatting.AQUA + String.format("%s (lvl%d)", pokemon.getDisplayName().getString(), adjustedLevelP1))); @@ -97,9 +97,9 @@ private void setupPokemonRepresentation(LeadPokemonMenu leadPokemonMenu) { if (pokemon == null) { continue; } - if (selectionSession.teamPreviewOn()) { + if (selectionSession.isShowTeamPreview()) { ItemStack pokemonItem = PokemonItem.from(pokemon, 1); - int adjustedLevelP2 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.minLevel(), request.maxLevel(), handicapP2); + int adjustedLevelP2 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.properties().getMinLevel(), request.properties().getMaxLevel(), handicapP2); pokemonItem.setHoverName(Component.literal(ChatFormatting.RED + String.format("%s's %s (lvl%d)", rival.getDisplayName().getString(), pokemon.getDisplayName().getString(), adjustedLevelP2))); leadPokemonMenu.setItem(itemSlot, leadPokemonMenu.getStateId(), pokemonItem); } else { diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java index 326333e..f1e05c8 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java @@ -63,10 +63,10 @@ public void onPokemonSelected(LeadPokemonMenuProvider menuProvider) { } private void beginBattle() { - int minLevel = originRequest.minLevel(); - int maxLevel = originRequest.maxLevel(); - int handicapP1 = originRequest.handicapP1(); - int handicapP2 = originRequest.handicapP2(); + int minLevel = originRequest.properties().getMinLevel(); + int maxLevel = originRequest.properties().getMaxLevel(); + int handicapP1 = originRequest.properties().getHandicapP1(); + int handicapP2 = originRequest.properties().getHandicapP2(); SESSIONS_TO_CANCEL.add(this); challengerMenuProvider.forceCloseMenu(); challengedMenuProvider.forceCloseMenu(); @@ -78,8 +78,8 @@ private void beginBattle() { } } - public boolean teamPreviewOn() { - return originRequest.preview(); + public boolean isShowTeamPreview() { + return originRequest.properties().getShowPreview(); } private boolean isBattleReady() { diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java new file mode 100644 index 0000000..0fcd70a --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java @@ -0,0 +1,14 @@ +package com.turtlehoarder.cobblemonchallenge.mixin; + +import com.cobblemon.mod.common.battles.BattleBuilder; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; + +@Mixin(BattleBuilder.class) +public class pvePreBattleBuilderMixin { + @Inject(method = "pve*", at = @At(value = "HEAD")) + public void method() { + + } +} \ No newline at end of file diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java new file mode 100644 index 0000000..6f51af6 --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java @@ -0,0 +1,75 @@ +package com.turtlehoarder.cobblemonchallenge.mixin; + +import com.turtlehoarder.cobblemonchallenge.config.DifficultyConfig; +import com.cobblemon.mod.common.battles.BattleBuilder; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; + +@Mixin(BattleBuilder.class) +public class pvp1v1PreBattleBuilderMixin { + + @Inject(method = "pvp1v1*", at = @At(value = "HEAD")) + public void handlePreBattleCheck() { + /* + * Parameters for main method: + * + * player1: ServerPlayerEntity, + * player2: ServerPlayerEntity, + * leadingPokemonPlayer1: UUID? = null, + * leadingPokemonPlayer2: UUID? = null, + * battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES, + * cloneParties: Boolean = false, + * healFirst: Boolean = false, + * partyAccessor: (ServerPlayerEntity) -> PartyStore = { it.party() } + * + * gate -> Are there configs that apply? -> check cached value for pvp configs + * > true -> progress to #2 + * > false -> do nothing + * 2. Do the configs apply? + * > true -> register battle & apply adjustments to parameters -> progress to #3 + * > false -> do nothing + * 3. for now, nothing, b/c second mixin + * > second mixin: + * > path: 'com.cobblemon.mod.common.battles.BattleRegistry.startBattle' + * > when: 'tail' + * > purpose: get PokemonBattle && pokemon battle start result + * > class path: 'com.cobblemon.mod.common.api.battles.model.PokemonBattle' + * > target UUID: 'PokemonBattle.battleId' -> val battleId = UUID.randomUUID() + * > !! A LOT of uses for PokemonBattle !! + * >> Be thorough, so nothing slips through + * >> Find the single exit if possible + * After: Use override val battle: PokemonBattle in events to process each battle. + * com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent + * com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent + * com.cobblemon.mod.common.api.events.battles.BattleFledEvent + * + * ?! Are there any battles that can escape outside of these three events... ?! + * + * TODO: Handle application of status & post battle impact on actual pokemon + * > still planning... + * > ?? store reference to original pokemon & use mixin to catch battle at conclusion ?? + * > this is the only entry -> find the only exit... + * > caveat: need to catch ErroredBattleStart if start battle called & error is thrown + * > need to update battle registry + */ + + + if (DifficultyConfig.USE_UNIVERSAL_LEVEL || DifficultyConfig.USE_UNIVERSAL_LEVEL_RANGE || DifficultyConfig.USE_UNIVERSAL_HANDICAP) { + /* + * 1. Apply Pre Battle Configs + * + * 2. Make sure clones are used + * > ?? should be thrown out after battle ?? + * > if so -> worst case is statuses/exp is not applied to actual pokemon + * > make sure clones that are cloned are also destroyed + * > check battles periodically to confirm any registered active battles are actually active + * > clear clones if any battles are left hanging + * 3. Cache HashMap of UUID (original,clone) + * > need to figure out how to get clones-clone to apply changes to original pokemon + */ + } + } +} + diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java index 1fb8075..5ffbdef 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java @@ -7,6 +7,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity; import com.cobblemon.mod.common.pokemon.Pokemon; import com.cobblemon.mod.common.util.LocalizationUtilsKt; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeProperties; import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; @@ -57,9 +58,9 @@ public static boolean isPlayerOnline(ServerPlayer player) { return player.getServer().getPlayerList().getPlayer(player.getUUID()) != null; } - public static ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean preview) { + public static ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean showPreview) { String key = UUID.randomUUID().toString().replaceAll("-", ""); - return new ChallengeRequest(key, challengerPlayer, challengedPlayer, minLevel, maxLevel, handicapP1, handicapP2, preview, System.currentTimeMillis()); + return new ChallengeRequest(key, challengerPlayer, challengedPlayer, new ChallengeProperties(minLevel, maxLevel, handicapP1, handicapP2, showPreview), System.currentTimeMillis()); } public static ItemLike getDisplayBlockForPokemon(Pokemon pokemon) { diff --git a/src/main/resources/mixins.common.json b/src/main/resources/mixins.common.json new file mode 100644 index 0000000..1f5f5ac --- /dev/null +++ b/src/main/resources/mixins.common.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.turtlehoarder.cobblemonchallenge.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "pvp1v1PreBattleBuilderMixin", + "pvePreBattleBuilderMixin" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file From c1a1ea17a96e3f8a991428b8387ce34f553f2839 Mon Sep 17 00:00:00 2001 From: Eight Date: Sun, 16 Jun 2024 21:10:57 -0700 Subject: [PATCH 13/15] WIP: The foundational structure for building tournaments. - current functionality is limited to: 1. Generating a seeded single elimination bracket (no player limit, just your ram ;p ) 2. Fill fake players organized appropriately based on their seed 3. Initialize & fill all the rounds & matches with players for the entire tournament 4. Update all the matches status 5. Assign rounds, matches, & tournament with a UUID for storage & queries with the tournament --- .../CobblemonTournament.java | 24 ++ .../api/builder/TournamentBuilder.java | 301 ++++++++++++++++++ .../builder/TournamentPropertiesBuilder.java | 114 +++++++ .../api/match/MatchStatus.java | 12 + .../api/match/TournamentMatch.java | 171 ++++++++++ .../api/player/SeededPlayer.java | 5 + .../api/player/TournamentPlayer.java | 50 +++ .../api/pokemon/PokemonEntry.java | 13 + .../api/pokemon/PokemonTeam.java | 6 + .../api/round/TournamentRound.java | 35 ++ .../api/round/TournamentRoundType.java | 10 + .../api/tournament/Tournament.java | 50 +++ .../api/tournament/TournamentProperties.java | 60 ++++ .../api/tournament/TournamentType.java | 10 + .../command/TournamentCommand.java | 6 + .../config/TournamentConfig.java | 51 +++ .../testing/TournamentBuilderTest.java | 93 ++++++ .../command/TournamentCommandTest.java | 49 +++ .../cobblemontournament/util/IndexedSeed.java | 3 + .../util/IndexedSeedArray.java | 62 ++++ .../util/IndexedSeedSortType.java | 10 + .../cobblemontournament/util/SeedUtil.java | 239 ++++++++++++++ .../CobblemonChallenge.java | 4 +- .../api/ChallengeProperties.java | 59 +++- .../battle/ChallengeBattleBuilder.java | 31 +- .../config/ChallengeConfig.java | 11 +- ...onfigProvider.java => ConfigProvider.java} | 2 +- .../config/SimpleConfig.java | 33 ++ .../util/ChallengeUtil.java | 19 +- .../battle/pokemon/ChallengeBattlePokemon.kt | 39 ++- src/main/resources/fabric.mod.json | 3 +- 31 files changed, 1513 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/cobblemontournament/CobblemonTournament.java create mode 100644 src/main/java/com/cobblemontournament/api/builder/TournamentBuilder.java create mode 100644 src/main/java/com/cobblemontournament/api/builder/TournamentPropertiesBuilder.java create mode 100644 src/main/java/com/cobblemontournament/api/match/MatchStatus.java create mode 100644 src/main/java/com/cobblemontournament/api/match/TournamentMatch.java create mode 100644 src/main/java/com/cobblemontournament/api/player/SeededPlayer.java create mode 100644 src/main/java/com/cobblemontournament/api/player/TournamentPlayer.java create mode 100644 src/main/java/com/cobblemontournament/api/pokemon/PokemonEntry.java create mode 100644 src/main/java/com/cobblemontournament/api/pokemon/PokemonTeam.java create mode 100644 src/main/java/com/cobblemontournament/api/round/TournamentRound.java create mode 100644 src/main/java/com/cobblemontournament/api/round/TournamentRoundType.java create mode 100644 src/main/java/com/cobblemontournament/api/tournament/Tournament.java create mode 100644 src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java create mode 100644 src/main/java/com/cobblemontournament/api/tournament/TournamentType.java create mode 100644 src/main/java/com/cobblemontournament/command/TournamentCommand.java create mode 100644 src/main/java/com/cobblemontournament/config/TournamentConfig.java create mode 100644 src/main/java/com/cobblemontournament/testing/TournamentBuilderTest.java create mode 100644 src/main/java/com/cobblemontournament/testing/command/TournamentCommandTest.java create mode 100644 src/main/java/com/cobblemontournament/util/IndexedSeed.java create mode 100644 src/main/java/com/cobblemontournament/util/IndexedSeedArray.java create mode 100644 src/main/java/com/cobblemontournament/util/IndexedSeedSortType.java create mode 100644 src/main/java/com/cobblemontournament/util/SeedUtil.java rename src/main/java/com/turtlehoarder/cobblemonchallenge/config/{ChallengeConfigProvider.java => ConfigProvider.java} (87%) diff --git a/src/main/java/com/cobblemontournament/CobblemonTournament.java b/src/main/java/com/cobblemontournament/CobblemonTournament.java new file mode 100644 index 0000000..27b6cb5 --- /dev/null +++ b/src/main/java/com/cobblemontournament/CobblemonTournament.java @@ -0,0 +1,24 @@ +package com.cobblemontournament; + +import com.cobblemontournament.config.TournamentConfig; +import com.cobblemontournament.testing.command.TournamentCommandTest; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CobblemonTournament implements ModInitializer +{ + public static String MODID = "cobblemontournament"; + public static final Logger LOGGER = LoggerFactory.getLogger("cobblemontournament"); + + @Override + public void onInitialize() + { + TournamentConfig.registerConfigs(); + + // for testing + CommandRegistrationCallback.EVENT.register((commandDispatcher, commandBuildContext, commandSelection) -> + TournamentCommandTest.register(commandDispatcher)); + } +} \ No newline at end of file diff --git a/src/main/java/com/cobblemontournament/api/builder/TournamentBuilder.java b/src/main/java/com/cobblemontournament/api/builder/TournamentBuilder.java new file mode 100644 index 0000000..6c93347 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/builder/TournamentBuilder.java @@ -0,0 +1,301 @@ +package com.cobblemontournament.api.builder; + +import com.cobblemontournament.api.round.TournamentRoundType; +import com.cobblemontournament.api.tournament.Tournament; +import com.cobblemontournament.api.match.TournamentMatch; +import com.cobblemontournament.api.player.SeededPlayer; +import com.cobblemontournament.api.round.TournamentRound; +import com.cobblemontournament.util.IndexedSeedArray; +import com.cobblemontournament.util.IndexedSeedSortType; +import com.cobblemontournament.util.SeedUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Predicate; + +public final class TournamentBuilder +{ + public TournamentBuilder(TournamentPropertiesBuilder builder) + { + propertiesBuilder = builder != null ? builder : new TournamentPropertiesBuilder(); + } + + @NotNull public final TournamentPropertiesBuilder propertiesBuilder; + @NotNull private final ArrayList seededPlayers = new ArrayList<>(); + @NotNull private final ArrayList unseededPlayers = new ArrayList<>(); + + // player registration & management + public int getPlayerCount() + { + return seededPlayers.size() + unseededPlayers.size(); + } + public boolean addPlayer(UUID playerID, Integer seed) + { + Predicate predicate = sp -> sp.id() == playerID; + if (containsPlayerWith(seededPlayers,predicate) || containsPlayerWith(unseededPlayers,predicate)) { + return false; + } + return seededPlayers.add(new SeededPlayer(playerID, seed)); + } + public boolean updateSeededPlayer(UUID playerID, Integer seed) + { + int notNullSeed = (seed != null && seed > 0) ? seed : -1; + Predicate removePredicate = sp -> sp.id() == playerID && sp.seed() != notNullSeed; + removePlayerIf(seededPlayers,removePredicate); + removePlayerIf(unseededPlayers,removePredicate); + return addPlayer(playerID, seed); + } + public boolean removePlayer(@NotNull UUID playerID) + { + boolean removed = removePlayerIf(seededPlayers, sp -> sp.id() == playerID); + return removePlayerIf(unseededPlayers, sp -> sp.id() == playerID) || removed; + } + private boolean containsPlayerWith(ArrayList collection,Predicate predicate) + { + return collection.stream().anyMatch(predicate); + } + private boolean removePlayerIf(ArrayList collection,Predicate predicate) + { + return collection.removeIf(predicate); + } + + public int getRoundCount() + { + return switch (propertiesBuilder.getTournamentType()) { + case SingleElimination -> getRoundCountSingleElimination(); + case DoubleElimination -> getRoundCountDoubleElimination(); + case RoundRobin -> getRoundCountRoundRobin(); + case VGC -> getRoundCountVGC(); + }; + } + private int getRoundCountSingleElimination() + { + int playerCount = getPlayerCount(); + int bracketSlots = SeedUtil.ceilToPowerOfTwo(playerCount); + int rounds = 0; + while (bracketSlots > 0) { + bracketSlots = bracketSlots >> 1; + rounds++; + } + return rounds; + } + private int getRoundCountDoubleElimination() + { + double playersRemaining = getPlayerCount(); + int rounds = 0; + while (playersRemaining > 1) { + playersRemaining = Math.ceil(playersRemaining * 0.5f); + } + + // TODO lower bracket + + return rounds; + } + private int getRoundCountRoundRobin() + { + var playersRemaining = getPlayerCount(); + int rounds = 0; + + // TODO + + return rounds; + } + private int getRoundCountVGC() + { + double playersRemaining = getPlayerCount(); + int rounds = 0; + + // TODO + + return rounds; + } + + /** Construct a finalized tournament */ + public Tournament toTournament() + { + var playerCount = getPlayerCount(); + if (playerCount < 2) { + // TODO log not enough players for tournament + return null; + } + + return switch (propertiesBuilder.getTournamentType()){ + case SingleElimination -> handleSingleElimination(UUID.randomUUID(),playerCount); + case DoubleElimination -> handleDoubleElimination(UUID.randomUUID(),playerCount); + case RoundRobin -> handleRoundRobin(UUID.randomUUID(),playerCount); + case VGC -> handleVGC(UUID.randomUUID(),playerCount); + }; + } + + private Tournament handleSingleElimination(UUID tournamentID,int playerCount) + { + var properties = propertiesBuilder.toTournamentProperties(); + var roundCount = getRoundCount(); + + // get total matches in first -> always power of 2 for single & double elimination brackets + var matchCount = 1 << (roundCount - 1); // remove championship b/c... fml + + ArrayList rounds = new ArrayList<>(roundCount); + rounds.add(getFirstEliminationRound(tournamentID)); + + int totalMatches = matchCount; + for(int i = 1; i < roundCount; i++) { + // size of indexed seeds should always be power of 2 -> this cuts it in half 'safely' + matchCount = matchCount >> 1; + rounds.add(getInitializedRound(tournamentID,TournamentRoundType.Primary,i,totalMatches - 1,matchCount)); + totalMatches += matchCount; + } + + return new Tournament(properties,tournamentID,rounds); + } + private Tournament handleDoubleElimination(UUID tournamentID,int playerCount) + { + // TODO log || implement + return null; + } + private Tournament handleRoundRobin(UUID tournamentID,int playerCount) + { + // TODO log || implement + return null; + } + private Tournament handleVGC(UUID tournamentID,int playerCount) + { + // TODO log || implement + return null; + } + + + private TournamentRound getFirstEliminationRound(UUID tournamentID) + { + var roundID = UUID.randomUUID(); + var orderedPlayers = sortAndSyncSeededPlayers(seededPlayers); + var indexedSeeds = SeedUtil.getIndexedSeedArray(getPlayerCount(), IndexedSeedSortType.INDEX_ASCENDING); + ArrayList matches = getSeedOrderedMatches(tournamentID,roundID,orderedPlayers,indexedSeeds); + fillWithUnseededPlayers(matches,indexedSeeds); + return new TournamentRound(tournamentID,roundID,0,TournamentRoundType.Primary,matches); + } + + private ArrayList sortAndSyncSeededPlayers(ArrayList seededPlayers) + { + var orderedPlayers = new ArrayList<>(seededPlayers.stream().toList()); + orderedPlayers.sort(Comparator.comparing(SeededPlayer::seed)); // ascending + + Random random = new Random(); + var sameSeededPlayers = new ArrayList(); + int size = orderedPlayers.size(); + for (int i = 0; i < size; i++) { + SeededPlayer nextPlayer; + if (!sameSeededPlayers.isEmpty()) { + var index = random.ints(0, sameSeededPlayers.size()) + .findFirst() + .orElse(0); + nextPlayer = sameSeededPlayers.remove(index); + } else if (i + 1 != size && Objects.equals(orderedPlayers.get(i).seed(), orderedPlayers.get(i + 1).seed())) { // 'i + 1 != size' to catch out of bounds error on last iteration + // multiple players with same seed -> create collection to pull players from at random + var lastIndex = i; + sameSeededPlayers.add(orderedPlayers.get(lastIndex)); + while (Objects.equals(orderedPlayers.get(lastIndex).seed(), orderedPlayers.get(lastIndex + 1).seed())) + { + sameSeededPlayers.add(orderedPlayers.get(++lastIndex)); + } + var index = random.ints(0, sameSeededPlayers.size()) + .findFirst() + .orElse(0); + nextPlayer = sameSeededPlayers.remove(index); + } else { + // just add the next player in order with new instance containing synced seed + nextPlayer = orderedPlayers.get(i); + } + + orderedPlayers.remove(i); + orderedPlayers.add(i,new SeededPlayer(nextPlayer.id(), i + 1)); + } + return orderedPlayers; + } + + private ArrayList getSeedOrderedMatches( + UUID tournamentID, + UUID roundID, + ArrayList players, + @NotNull IndexedSeedArray indexedSeeds + ) { + if (indexedSeeds.sortStatus() != IndexedSeedSortType.INDEX_ASCENDING) { + indexedSeeds.sortBySeedAscending(); + } + var size = indexedSeeds.size(); + int matchCount = size >> 1; // size of indexed seeds should always be power of 2 -> cuts in half 'safely'... + var matches = new ArrayList(matchCount); + int seedIndex = 0; + for (int i = 0; i < matchCount; i++) { + var seed1 = indexedSeeds.collection.get(seedIndex++).seed(); + var seed2 = indexedSeeds.collection.get(seedIndex++).seed(); + var player1 = players.stream() + .filter(p -> p.seed().equals(seed1)) + .findFirst() + .orElse(null); + var player2 = players.stream() + .filter(p -> p.seed().equals(seed2)) + .findFirst() + .orElse(null); + UUID player1ID = player1 != null ? player1.id() : null; + UUID player2ID = player2 != null ? player2.id() : null; + matches.add(new TournamentMatch(tournamentID,roundID,UUID.randomUUID(),i,i,player1ID,player2ID)); + } + return matches; + } + + private void fillWithUnseededPlayers( + @NotNull ArrayList matches, + @NotNull IndexedSeedArray indexedSeeds + ) { + indexedSeeds.sortBySeedAscending(); + var unseededCount = unseededPlayers.size(); + var unseededIndex = 0; + for (int i = 0; i < indexedSeeds.size();i++) { + var seedIndex = indexedSeeds.get(i).index(); + var matchIndex = seedIndex/2; + var remainder = seedIndex%2; // used to place player correctly into player1 or player2 -> always -> 1 | 0 + var match = matches.get(matchIndex); + + if (remainder == 0) { + // is player 1 slot + if (match.player1() == null){ + // available slot + var player = unseededPlayers.get(unseededIndex); + if (match.trySetPlayer1(player.id())){ + unseededIndex++; + } //else { // TODO log if fail } + } + } else { + // is player 2 slot + if (match.player1() == null){ + // available slot + var player = unseededPlayers.get(unseededIndex); + if (match.trySetPlayer2(player.id())) { + unseededIndex++; + } //else { // TODO log if fail } + } + } + + if (unseededIndex == unseededCount) { + break; + } + } + } + + private TournamentRound getInitializedRound( + UUID tournamentID, + TournamentRoundType type, // ignore warning -> will go away when other types implemented + int roundIndex, + int lastMatchIndex, + int matchCount + ) { + UUID roundID = UUID.randomUUID(); + var matches = new ArrayList(matchCount); + for (var i = 0; i < matchCount; i++) { + matches.add(new TournamentMatch(tournamentID,roundID,UUID.randomUUID(),lastMatchIndex++,i)); + } + return new TournamentRound(tournamentID,roundID,roundIndex,type,matches); + } + +} diff --git a/src/main/java/com/cobblemontournament/api/builder/TournamentPropertiesBuilder.java b/src/main/java/com/cobblemontournament/api/builder/TournamentPropertiesBuilder.java new file mode 100644 index 0000000..2edd095 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/builder/TournamentPropertiesBuilder.java @@ -0,0 +1,114 @@ +package com.cobblemontournament.api.builder; + +import com.cobblemontournament.api.tournament.TournamentProperties; +import com.cobblemontournament.api.tournament.TournamentType; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import org.jetbrains.annotations.NotNull; + +public final class TournamentPropertiesBuilder +{ + public TournamentPropertiesBuilder() { + tournamentType = TournamentProperties.DEFAULT_TOURNAMENT_TYPE; + groupSize = TournamentProperties.DEFAULT_GROUP_SIZE; + maxPlayerCount = TournamentProperties.DEFAULT_MAX_PLAYER_COUNT; + challengeFormat = TournamentProperties.DEFAULT_CHALLENGE_FORMAT; + minLevel = TournamentProperties.DEFAULT_CHALLENGE_MIN_LEVEL; + maxLevel = TournamentProperties.DEFAULT_CHALLENGE_MAX_LEVEL; + showPreview = TournamentProperties.DEFAULT_SHOW_PREVIEW; + } + + public TournamentPropertiesBuilder( + TournamentType tournamentType, + Integer groupSize, + Integer maxPlayerCount, + ChallengeFormat challengeFormat, + Integer minLevel, + Integer maxLevel, + Boolean showPreview + ) { + this.tournamentType = tournamentType != null ? tournamentType : TournamentProperties.DEFAULT_TOURNAMENT_TYPE; + this.groupSize = groupSize != null ? groupSize : TournamentProperties.DEFAULT_GROUP_SIZE; + this.maxPlayerCount = maxPlayerCount != null ? maxPlayerCount : TournamentProperties.DEFAULT_MAX_PLAYER_COUNT; + this.challengeFormat = challengeFormat != null ? challengeFormat : TournamentProperties.DEFAULT_CHALLENGE_FORMAT; + this.minLevel = minLevel != null ? minLevel : TournamentProperties.DEFAULT_CHALLENGE_MIN_LEVEL; + this.maxLevel = maxLevel != null ? maxLevel : TournamentProperties.DEFAULT_CHALLENGE_MAX_LEVEL; + this.showPreview = showPreview != null ? showPreview : TournamentProperties.DEFAULT_SHOW_PREVIEW; + } + + @NotNull private TournamentType tournamentType; + private int groupSize; + private int maxPlayerCount; + @NotNull ChallengeFormat challengeFormat; + private int minLevel; + private int maxLevel; + private boolean showPreview; + + @NotNull public TournamentType getTournamentType() { return tournamentType; } + public boolean setTournamentType(TournamentType type) { + if (type != null && tournamentType != type) { + tournamentType = type; + return true; + } + return false; + } + public int getGroupSize() { return groupSize; } + public boolean setGroupSize(int groupSize) { + if (this.groupSize != groupSize) { + this.groupSize = groupSize; + return true; + } + return false; + } + public int getMaxPlayerCount() { return maxPlayerCount; } + public boolean setMaxPlayerCount(int maxPlayerCount) { + if (this.maxPlayerCount != maxPlayerCount) { + this.maxPlayerCount = maxPlayerCount; + return true; + } + return false; + } + @NotNull public ChallengeFormat getChallengeFormat() { return challengeFormat; } + public boolean setChallengeFormat(ChallengeFormat format) { + if (format != null && challengeFormat != format) { + challengeFormat = format; + return true; + } + return false; + } + public int getMinLevel() { return minLevel; } + public boolean setMinLevel(int min) { + if (minLevel != min) { + minLevel = min; + return true; + } + return false; + } + public int getMaxLevel() { return maxLevel; } + public boolean setMaxLevel(int max) { + if (maxLevel != max) { + maxLevel = max; + return true; + } + return false; + } + public boolean getShowPreview() { return showPreview; } + public boolean setShowPreview(boolean showPreview) { + if (this.showPreview != showPreview) { + this.showPreview = showPreview; + return true; + } + return false; + } + + public TournamentProperties toTournamentProperties() { + return new TournamentProperties( + getTournamentType(), + getGroupSize(), + getMaxPlayerCount(), + getChallengeFormat(), + getMinLevel(), + getMaxLevel(), + getShowPreview() + ); + } +} diff --git a/src/main/java/com/cobblemontournament/api/match/MatchStatus.java b/src/main/java/com/cobblemontournament/api/match/MatchStatus.java new file mode 100644 index 0000000..5ae5708 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/match/MatchStatus.java @@ -0,0 +1,12 @@ +package com.cobblemontournament.api.match; + +public enum MatchStatus +{ + Error, + Empty, + NotReady, + Ready, + InProgress, + Complete, + Finalized, +} diff --git a/src/main/java/com/cobblemontournament/api/match/TournamentMatch.java b/src/main/java/com/cobblemontournament/api/match/TournamentMatch.java new file mode 100644 index 0000000..c80f5d5 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/match/TournamentMatch.java @@ -0,0 +1,171 @@ +package com.cobblemontournament.api.match; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public final class TournamentMatch +{ + public TournamentMatch( + @NotNull UUID tournamentID, + @NotNull UUID roundID, + UUID matchID, + int tournamentMatchIndex, + int roundMatchIndex + ) { + this.tournamentID = tournamentID; + this.roundID = roundID; + this.matchID = matchID != null ? matchID : UUID.randomUUID(); + this.tournamentMatchIndex = tournamentMatchIndex; + this.roundMatchIndex = roundMatchIndex; + } + + public TournamentMatch( + @NotNull UUID tournamentID, + @NotNull UUID roundID, + UUID matchUUID, + int tournamentMatchIndex, + int roundMatchIndex, + @Nullable UUID player1id, + @Nullable UUID player2ID + ) { + this.tournamentID = tournamentID; + this.roundID = roundID; + this.matchID = matchUUID != null ? matchUUID : UUID.randomUUID(); + this.tournamentMatchIndex = tournamentMatchIndex; + this.roundMatchIndex = roundMatchIndex; + this.player1ID = player1id; + this.player2ID = player2ID; + updateStatus(); + } + + public final UUID tournamentID; + public final UUID roundID; + public final UUID matchID; + public final int tournamentMatchIndex; + public final int roundMatchIndex; + private MatchStatus status = MatchStatus.Empty; + + @Nullable private UUID player1ID; + @Nullable private UUID player2ID; + @Nullable private UUID victorID; + + @Nullable public UUID player1() + { + return player1ID; + } + @Nullable public UUID player2() + { + return player2ID; + } + @Nullable public UUID victorID() + { + return victorID; + } + + // TODO: create data class to hold & serialize finalized match details + // > possibly just use matchUUID to search a database + + public boolean trySetPlayer1(@NotNull UUID id) + { + if (player1ID != null) { + return false; + } + player1ID = id; + return true; + } + public boolean trySetPlayer2(@NotNull UUID id) + { + if (player2ID != null) { + return false; + } + player2ID = id; + return true; + } + + public boolean updatePlayer(@NotNull UUID id) + { + if (player1ID != null && player2ID != null) { + return false; + } + if (player1ID == id || player2ID == id) { + return false; + } + if (player1ID == null) { + player1ID = id; + return true; + } + player2ID = id; + return true; + } + public boolean updateResult(@NotNull UUID victorID) + { + if (player1ID != null && player2ID != null) { + return false; + } + if (player1ID != victorID && player2ID != victorID) { + return false; + } + this.victorID = victorID; + return true; + } + + public MatchStatus getStatus() + { + return updateStatus(); + } + public MatchStatus updateStatus() + { + return switch (status) { + case Error,Empty -> updateEmptyStatus(); // recycle error status for now to reset & possibly resolve issue + case NotReady -> updateNotReadyStatus(); + case Ready -> updateReadyStatus(); + case InProgress -> updateInProgressStatus(); + case Complete -> updateCompleteStatus(); + case Finalized -> status; + }; + } + private MatchStatus updateEmptyStatus() + { + if (player1ID == null && player2ID == null) { + if (status != MatchStatus.Empty) { + status = MatchStatus.Empty; + } + return status; + } + return updateNotReadyStatus(); + } + private MatchStatus updateNotReadyStatus() + { + if (player1ID == null || player2ID == null) { + if (status != MatchStatus.NotReady) { + status = MatchStatus.NotReady; + } + return status; + } + return updateReadyStatus(); + } + private MatchStatus updateReadyStatus() + { + if (player1ID != null && player2ID != null) { + if (status != MatchStatus.Ready) { + status = MatchStatus.Ready; + } + return status; + } + // TODO: implement check for matches in progress on the server -> progress to inProgress + return MatchStatus.Ready; + } + private MatchStatus updateInProgressStatus() + { + // TODO: implement check for matches in progress on the server -> if not progress to Complete + return MatchStatus.InProgress; + } + private MatchStatus updateCompleteStatus() + { + // TODO: implement check for matches in progress on the server -> + return MatchStatus.Complete; + } + +} diff --git a/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java b/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java new file mode 100644 index 0000000..982dcac --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java @@ -0,0 +1,5 @@ +package com.cobblemontournament.api.player; + +import java.util.UUID; + +public record SeededPlayer (UUID id, Integer seed) { } \ No newline at end of file diff --git a/src/main/java/com/cobblemontournament/api/player/TournamentPlayer.java b/src/main/java/com/cobblemontournament/api/player/TournamentPlayer.java new file mode 100644 index 0000000..89fdc0f --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/player/TournamentPlayer.java @@ -0,0 +1,50 @@ +package com.cobblemontournament.api.player; + +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +@SuppressWarnings("unused") +public final class TournamentPlayer +{ + public TournamentPlayer( + UUID player, + int seed + ) { + this.player = player; + this.seed = seed; + } + public TournamentPlayer( + UUID player, + int seed, + @Nullable UUID pokemonSideUUID + ) { + this.player = player; + this.seed = seed; + this.pokemonSideUUID = pokemonSideUUID; + } + + @Nullable private UUID pokemonSideUUID; + private Integer finalPlacement; + + public final UUID player; + public final int seed; + + @Nullable public UUID getPokemonSideUUID() { return pokemonSideUUID; } + public boolean updatePokemonSideUUID(UUID pokemonSideUUID, boolean override) { + if (this.pokemonSideUUID == pokemonSideUUID || (!override && this.pokemonSideUUID != null)){ + return false; + } + this.pokemonSideUUID = pokemonSideUUID; + return true; + } + public int getFinalPlacement() { return finalPlacement != null ? finalPlacement : -1; } + public boolean updateFinalPlacement(int finalPlacement) { + if (this.finalPlacement == finalPlacement) { + return false; + } + this.finalPlacement = finalPlacement; + return true; + } + +} diff --git a/src/main/java/com/cobblemontournament/api/pokemon/PokemonEntry.java b/src/main/java/com/cobblemontournament/api/pokemon/PokemonEntry.java new file mode 100644 index 0000000..891f1f8 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/pokemon/PokemonEntry.java @@ -0,0 +1,13 @@ +package com.cobblemontournament.api.pokemon; + +import com.cobblemon.mod.common.pokemon.Pokemon; + +import java.util.UUID; +import java.util.Vector; + +public record PokemonEntry( + UUID id, + Vector pokemon +) { + +} diff --git a/src/main/java/com/cobblemontournament/api/pokemon/PokemonTeam.java b/src/main/java/com/cobblemontournament/api/pokemon/PokemonTeam.java new file mode 100644 index 0000000..3ebbd9c --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/pokemon/PokemonTeam.java @@ -0,0 +1,6 @@ +package com.cobblemontournament.api.pokemon; + +import java.util.UUID; +import java.util.Vector; + +public record PokemonTeam (UUID id,Vector pokemon) { } diff --git a/src/main/java/com/cobblemontournament/api/round/TournamentRound.java b/src/main/java/com/cobblemontournament/api/round/TournamentRound.java new file mode 100644 index 0000000..ef2bcc5 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/round/TournamentRound.java @@ -0,0 +1,35 @@ +package com.cobblemontournament.api.round; + +import com.cobblemontournament.api.match.TournamentMatch; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; +import java.util.ArrayList; + +public final class TournamentRound +{ + public TournamentRound( + @NotNull UUID tournamentID, + UUID roundUUID, + int roundIndex, + @NotNull TournamentRoundType roundType, + ArrayList matches + ) { + this.tournamentID = tournamentID; + this.roundUUID = roundUUID != null ? roundUUID : UUID.randomUUID(); + this.roundIndex = roundIndex; + this.roundType = roundType; + this.matches = matches; + } + + public final UUID tournamentID; + public final UUID roundUUID; + public final int roundIndex; + public final TournamentRoundType roundType; + public final ArrayList matches; + + public void updateMatch(TournamentMatch match) + { + matches.add(match); + } +} diff --git a/src/main/java/com/cobblemontournament/api/round/TournamentRoundType.java b/src/main/java/com/cobblemontournament/api/round/TournamentRoundType.java new file mode 100644 index 0000000..0cbeedc --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/round/TournamentRoundType.java @@ -0,0 +1,10 @@ +package com.cobblemontournament.api.round; + +public enum TournamentRoundType +{ + Preliminary, + Primary, + Secondary, + RoundRobin, + TieBreaker, +} diff --git a/src/main/java/com/cobblemontournament/api/tournament/Tournament.java b/src/main/java/com/cobblemontournament/api/tournament/Tournament.java new file mode 100644 index 0000000..e82f5fd --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/tournament/Tournament.java @@ -0,0 +1,50 @@ +package com.cobblemontournament.api.tournament; + +import com.cobblemontournament.api.round.TournamentRound; +import com.cobblemontournament.api.pokemon.PokemonEntry; +import com.cobblemontournament.api.pokemon.PokemonTeam; +import com.cobblemontournament.api.match.TournamentMatch; +import com.cobblemontournament.api.player.TournamentPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.UUID; + +public final class Tournament +{ + public Tournament( + @NotNull TournamentProperties properties, + @NotNull UUID tournamentID, + @NotNull ArrayList rounds + ) { + this.tournamentID = tournamentID; + this.tournamentProperties = properties; + // TODO register rounds to a HashMap + this.rounds = rounds; + // TODO register matches, players, pokemon teams, pokemon to hashmaps + // -> pokemon teams & pokemon temporary until a server wide database is implemented + // -> !! still confirm pokemon & teams are in database when implemented !! + } + + @NotNull public final UUID tournamentID; + @NotNull public final TournamentProperties tournamentProperties; + @NotNull public final ArrayList rounds; + private final HashMap matches = new HashMap<>(); + private final HashMap players = new HashMap<>(); + private final HashMap pokemonTeams = new HashMap<>(); // TODO: temporary until rental team functionality implemented + private final HashMap pokemonEntries = new HashMap<>(); // TODO: temporary until rental team functionality implemented + + @SuppressWarnings("unused") + public void safeCloneOf(boolean reset){ + // TODO: make a deep copy of tournament + // if reset -> clone with clean start + } + + @SuppressWarnings("unused") + public void safeBuilderCloneOf(boolean reset){ + // TODO: make a deep copy of tournament back into tournament builder + // if reset -> clone with clean start + } +} diff --git a/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java b/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java new file mode 100644 index 0000000..f207c79 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java @@ -0,0 +1,60 @@ +package com.cobblemontournament.api.tournament; + +import com.cobblemontournament.config.TournamentConfig; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import org.jetbrains.annotations.NotNull; + +@NotNull +public final class TournamentProperties +{ + public static final TournamentType DEFAULT_TOURNAMENT_TYPE = TournamentConfig.DEFAULT_TOURNAMENT_TYPE; + public static final int DEFAULT_GROUP_SIZE = TournamentConfig.DEFAULT_GROUP_SIZE; + public static final int DEFAULT_MAX_PLAYER_COUNT = TournamentConfig.DEFAULT_MAX_PLAYER_COUNT; + public static final ChallengeFormat DEFAULT_CHALLENGE_FORMAT = TournamentConfig.DEFAULT_CHALLENGE_FORMAT; + public static final int DEFAULT_CHALLENGE_MIN_LEVEL = TournamentConfig.DEFAULT_CHALLENGE_MIN_LEVEL; + public static final int DEFAULT_CHALLENGE_MAX_LEVEL = TournamentConfig.DEFAULT_CHALLENGE_MAX_LEVEL; + public static final boolean DEFAULT_SHOW_PREVIEW = TournamentConfig.DEFAULT_SHOW_PREVIEW; + + public TournamentProperties() { + tournamentType = DEFAULT_TOURNAMENT_TYPE; + groupSize = DEFAULT_GROUP_SIZE; + maxPlayerCount = DEFAULT_MAX_PLAYER_COUNT; + challengeFormat = DEFAULT_CHALLENGE_FORMAT; + minLevel = DEFAULT_CHALLENGE_MIN_LEVEL; + maxLevel = DEFAULT_CHALLENGE_MAX_LEVEL; + showPreview = DEFAULT_SHOW_PREVIEW; + } + public TournamentProperties ( + TournamentType type, + Integer groupSize, + Integer maxPlayerCount, + ChallengeFormat format, + Integer minLevel, + Integer maxLevel, + Boolean showPreview + ) { + this.tournamentType = (type != null) ? type : DEFAULT_TOURNAMENT_TYPE; + this.groupSize = groupSize != null ? groupSize : DEFAULT_GROUP_SIZE; + this.maxPlayerCount = maxPlayerCount != null ? maxPlayerCount : DEFAULT_MAX_PLAYER_COUNT; + this.challengeFormat = format != null ? format : DEFAULT_CHALLENGE_FORMAT; + this.minLevel = minLevel != null ? minLevel : DEFAULT_CHALLENGE_MIN_LEVEL; + this.maxLevel = maxLevel != null ? maxLevel : DEFAULT_CHALLENGE_MAX_LEVEL; + this.showPreview = showPreview != null ? showPreview : DEFAULT_SHOW_PREVIEW; + } + + private final TournamentType tournamentType; + private final Integer groupSize; + private final Integer maxPlayerCount; + private final ChallengeFormat challengeFormat; + private final int minLevel; + private final int maxLevel; + private final boolean showPreview; + + @NotNull public TournamentType getTournamentType(){ return tournamentType; } + public int getGroupSize(){ return groupSize; } + public int getMaxPlayerCount(){ return maxPlayerCount; } + @NotNull public ChallengeFormat getChallengeFormat(){ return challengeFormat; } + public int getMinLevel(){ return minLevel; } + public int getMaxLevel(){ return maxLevel; } + public boolean getShowPreview(){ return showPreview; } +} \ No newline at end of file diff --git a/src/main/java/com/cobblemontournament/api/tournament/TournamentType.java b/src/main/java/com/cobblemontournament/api/tournament/TournamentType.java new file mode 100644 index 0000000..ff9d862 --- /dev/null +++ b/src/main/java/com/cobblemontournament/api/tournament/TournamentType.java @@ -0,0 +1,10 @@ +package com.cobblemontournament.api.tournament; + +@SuppressWarnings("unused") +public enum TournamentType +{ + RoundRobin, + SingleElimination, + DoubleElimination, + VGC, +} diff --git a/src/main/java/com/cobblemontournament/command/TournamentCommand.java b/src/main/java/com/cobblemontournament/command/TournamentCommand.java new file mode 100644 index 0000000..76da890 --- /dev/null +++ b/src/main/java/com/cobblemontournament/command/TournamentCommand.java @@ -0,0 +1,6 @@ +package com.cobblemontournament.command; + +public class TournamentCommand +{ + +} diff --git a/src/main/java/com/cobblemontournament/config/TournamentConfig.java b/src/main/java/com/cobblemontournament/config/TournamentConfig.java new file mode 100644 index 0000000..b2a5cfb --- /dev/null +++ b/src/main/java/com/cobblemontournament/config/TournamentConfig.java @@ -0,0 +1,51 @@ +package com.cobblemontournament.config; + +import com.cobblemontournament.CobblemonTournament; +import com.cobblemontournament.api.tournament.TournamentType; +import com.mojang.datafixers.util.Pair; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import com.turtlehoarder.cobblemonchallenge.config.ConfigProvider; +import com.turtlehoarder.cobblemonchallenge.config.SimpleConfig; + +@SuppressWarnings("unused") +public final class TournamentConfig +{ + public static SimpleConfig CONFIG; + private static ConfigProvider configs; + + public static TournamentType DEFAULT_TOURNAMENT_TYPE; + public static int DEFAULT_GROUP_SIZE; + public static int DEFAULT_MAX_PLAYER_COUNT; + // challenge properties + public static ChallengeFormat DEFAULT_CHALLENGE_FORMAT; + public static int DEFAULT_CHALLENGE_MIN_LEVEL; + public static int DEFAULT_CHALLENGE_MAX_LEVEL; + public static boolean DEFAULT_SHOW_PREVIEW; + + public static void registerConfigs() { + CobblemonTournament.LOGGER.info("Loading Tournament Configs"); + configs = new ConfigProvider(); + createConfigs(); + CONFIG = SimpleConfig.of(CobblemonTournament.MODID + "-config").provider(configs).request(); + assignConfigs(); + } + private static void createConfigs() { + configs.addKeyValuePair(new Pair<>("defaultTournamentType", TournamentType.SingleElimination)); + configs.addKeyValuePair(new Pair<>("defaultGroupSize", 4)); + configs.addKeyValuePair(new Pair<>("defaultMaxPlayerCount", 32)); + configs.addKeyValuePair(new Pair<>("defaultChallengeFormat", ChallengeFormat.STANDARD_6V6)); + configs.addKeyValuePair(new Pair<>("defaultMinLevel", 50)); + configs.addKeyValuePair(new Pair<>("defaultMaxLevel", 50)); + configs.addKeyValuePair(new Pair<>("defaultShowPreview", true)); + } + + private static void assignConfigs() { + DEFAULT_TOURNAMENT_TYPE = CONFIG.getOrDefault("defaultTournamentType", TournamentType.SingleElimination); + DEFAULT_GROUP_SIZE = CONFIG.getOrDefault("defaultGroupSize", 4); + DEFAULT_MAX_PLAYER_COUNT = CONFIG.getOrDefault("defaultMaxPlayerCount", 32); + DEFAULT_CHALLENGE_FORMAT = CONFIG.getOrDefault("defaultChallengeFormat", ChallengeFormat.STANDARD_6V6); + DEFAULT_CHALLENGE_MIN_LEVEL = CONFIG.getOrDefault("defaultChallengeMinLevel", 50); + DEFAULT_CHALLENGE_MAX_LEVEL = CONFIG.getOrDefault("defaultChallengeMaxLevel", 50); + DEFAULT_SHOW_PREVIEW = CONFIG.getOrDefault("", true); + } +} diff --git a/src/main/java/com/cobblemontournament/testing/TournamentBuilderTest.java b/src/main/java/com/cobblemontournament/testing/TournamentBuilderTest.java new file mode 100644 index 0000000..f221887 --- /dev/null +++ b/src/main/java/com/cobblemontournament/testing/TournamentBuilderTest.java @@ -0,0 +1,93 @@ +package com.cobblemontournament.testing; + +import com.cobblemontournament.api.builder.TournamentBuilder; +import com.cobblemontournament.api.builder.TournamentPropertiesBuilder; +import com.cobblemontournament.api.match.TournamentMatch; +import com.cobblemontournament.api.round.TournamentRound; +import com.cobblemontournament.api.tournament.Tournament; +import com.cobblemontournament.api.tournament.TournamentType; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import org.slf4j.helpers.Util; // Util.report(""); + +import java.util.UUID; + +public class TournamentBuilderTest +{ + public static void buildTournamentDebug(int maxPlayers,boolean doPrint) + { + var propertiesBuilder = new TournamentPropertiesBuilder( + TournamentType.SingleElimination, + null, + maxPlayers, + ChallengeFormat.STANDARD_6V6, + null, + null, + null + ); + var tournamentBuilder = new TournamentBuilder(propertiesBuilder); + + for (int i = 0; i < maxPlayers; i++) { + tournamentBuilder.addPlayer(UUID.randomUUID(),i); + } + + var tournament = tournamentBuilder.toTournament(); + + if (doPrint && tournament != null){ + printTournamentDebug(tournament); + } + } + + public static void printTournamentDebug(Tournament tournament) + { + // print Properties + Util.report("Tournament Debug - ID: " + tournament.tournamentID); + Util.report(" Properties:"); + Util.report(" - Tournament Type: " + tournament.tournamentProperties.getTournamentType()); + Util.report(" - Group Size: " + tournament.tournamentProperties.getGroupSize()); + Util.report(" - Max Players: " + tournament.tournamentProperties.getMaxPlayerCount()); + Util.report(" - Challenge Format: " + tournament.tournamentProperties.getChallengeFormat()); + Util.report(" - Min Level: " + tournament.tournamentProperties.getMinLevel()); + Util.report(" - Max Level: " + tournament.tournamentProperties.getMaxLevel()); + Util.report(" - Show Preview: " + tournament.tournamentProperties.getShowPreview()); + + int roundCount = tournament.rounds.size(); + int matchCount = 0; + for (int i = 0; i < roundCount; i++) { + matchCount += tournament.rounds.get(i).matches.size(); + } + + Util.report(" Details:"); + Util.report(" - Rounds: " + roundCount); + Util.report(" - Matches: " + matchCount); + + // print round details + for (int i = 0; i < roundCount;i++) { + printRoundDetails(tournament.rounds.get(i),true); + } + } + + public static void printRoundDetails(TournamentRound round,boolean includeMatches) + { + Util.report("Round Details - ID:" + round.roundUUID); + Util.report("- Round Type: " + round.roundType); + Util.report("- Round Index: " + round.roundIndex); + Util.report("- Matches: " + round.matches.size()); + if (!includeMatches) { + return; + } + var size = round.matches.size(); + for (int i = 0; i < size;i++) { + printMatchDetails(round.matches.get(i)); + } + } + public static void printMatchDetails(TournamentMatch match) + { + Util.report("Match Details - ID:" + match.matchID); + Util.report("- Tournament Match Index: " + match.tournamentMatchIndex); + Util.report("- Round Match Index: " + match.roundMatchIndex); + Util.report("- Player 1: " + match.player1()); + Util.report("- Player 2: " + match.player2()); + Util.report("- Victor: " + match.victorID()); + Util.report("- Status: " + match.getStatus()); + } +} diff --git a/src/main/java/com/cobblemontournament/testing/command/TournamentCommandTest.java b/src/main/java/com/cobblemontournament/testing/command/TournamentCommandTest.java new file mode 100644 index 0000000..c12f95b --- /dev/null +++ b/src/main/java/com/cobblemontournament/testing/command/TournamentCommandTest.java @@ -0,0 +1,49 @@ +package com.cobblemontournament.testing.command; + +import com.cobblemontournament.testing.TournamentBuilderTest; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; + +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.commands.arguments.selector.EntitySelector; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import net.minecraft.server.level.ServerPlayer; + +import java.util.UUID; + +public class TournamentCommandTest +{ + public static void register(CommandDispatcher dispatcher) + { + LiteralArgumentBuilder buildTournamentDebugCommand = Commands.literal("tournament") + .then(Commands.literal("testBuild") + .then(Commands.argument("maxPlayers", IntegerArgumentType.integer(0,64)) + .then(Commands.literal("print") + .then(Commands.argument("doPrint", BoolArgumentType.bool()) + .executes(c -> buildTournamentDebug(IntegerArgumentType.getInteger(c,"maxPlayers"),BoolArgumentType.getBool(c,"doPrint")) + ) + ) + ) + ) + ); + + dispatcher.register(buildTournamentDebugCommand); + } + + public static int buildTournamentDebug(int players,boolean doPrint) + { + TournamentBuilderTest.buildTournamentDebug(players,doPrint); + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/com/cobblemontournament/util/IndexedSeed.java b/src/main/java/com/cobblemontournament/util/IndexedSeed.java new file mode 100644 index 0000000..d08fc88 --- /dev/null +++ b/src/main/java/com/cobblemontournament/util/IndexedSeed.java @@ -0,0 +1,3 @@ +package com.cobblemontournament.util; + +public record IndexedSeed(Integer index, Integer seed) { } \ No newline at end of file diff --git a/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java b/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java new file mode 100644 index 0000000..0f7a1ad --- /dev/null +++ b/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java @@ -0,0 +1,62 @@ +package com.cobblemontournament.util; + +import org.jetbrains.annotations.Nullable; +import java.util.Comparator; +import java.util.Vector; + +public class IndexedSeedArray +{ + public IndexedSeedArray( + Vector collection, + @Nullable IndexedSeedSortType sortingType + ) { + this.collection = collection; + if (sortingType == null) { + return; + } + switch (sortingType) { + case SEED_ASCENDING -> sortBySeedAscending(); + case SEED_DESCENDING -> sortBySeedDescending(); + case INDEX_ASCENDING -> sortByIndexAscending(); + case INDEX_DESCENDING -> sortByIndexDescending(); + } + } + + public Vector collection; + private IndexedSeedSortType indexedSeedStatus = IndexedSeedSortType.UNKNOWN; + public IndexedSeedSortType sortStatus() + { + return indexedSeedStatus; + } + + public int size() + { + return collection.size(); + } + public IndexedSeed get(int index) + { + return collection.get(index); + } + + public void sortBySeedAscending() + { + collection.sort(Comparator.comparing(IndexedSeed::seed)); + indexedSeedStatus = IndexedSeedSortType.SEED_ASCENDING; + } + public void sortBySeedDescending() + { + collection.sort(Comparator.comparing(IndexedSeed::seed).reversed()); + indexedSeedStatus = IndexedSeedSortType.SEED_DESCENDING; + } + public void sortByIndexAscending() + { + collection.sort(Comparator.comparing(IndexedSeed::index)); + indexedSeedStatus = IndexedSeedSortType.INDEX_ASCENDING; + } + public void sortByIndexDescending() + { + collection.sort(Comparator.comparing(IndexedSeed::index).reversed()); + indexedSeedStatus = IndexedSeedSortType.INDEX_DESCENDING; + } + +} \ No newline at end of file diff --git a/src/main/java/com/cobblemontournament/util/IndexedSeedSortType.java b/src/main/java/com/cobblemontournament/util/IndexedSeedSortType.java new file mode 100644 index 0000000..7f4e441 --- /dev/null +++ b/src/main/java/com/cobblemontournament/util/IndexedSeedSortType.java @@ -0,0 +1,10 @@ +package com.cobblemontournament.util; + +public enum IndexedSeedSortType +{ + UNKNOWN, + SEED_ASCENDING, + SEED_DESCENDING, + INDEX_ASCENDING, + INDEX_DESCENDING, +} diff --git a/src/main/java/com/cobblemontournament/util/SeedUtil.java b/src/main/java/com/cobblemontournament/util/SeedUtil.java new file mode 100644 index 0000000..6be1c77 --- /dev/null +++ b/src/main/java/com/cobblemontournament/util/SeedUtil.java @@ -0,0 +1,239 @@ +package com.cobblemontournament.util; + +import java.util.ArrayList; +import java.util.Vector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SeedUtil +{ + /** + * IndexedSeeds( int index, int seed ) + * index: order index of seed in tournament structure + * seed: value of seed + */ + public static IndexedSeedArray getIndexedSeedArray(int seedCount, IndexedSeedSortType sortStatus) + { + var seeds = getSeedArray(seedCount); + var size = seeds.size(); + var placeHolder = new IndexedSeed(-1,-1); + var indexedSeeds = new Vector<>(Stream.generate(() -> placeHolder) + .limit(size) + .collect(Collectors.toList()) + ); + for (int i = 0; i < size; i++) { + indexedSeeds.set(i, new IndexedSeed(i,seeds.get(i))); + } + return new IndexedSeedArray(indexedSeeds,sortStatus); + } + + /** @return Vector with sorted seeds for tournament bracket
+ * -> the array size will be the nearest ceil to power of 2 int for (param)seedCount
+ * ex 1: (param)seedCount = 5 -> nearest power of 2 -> 8
+ * ex 2: (param)seedCount = 33 -> nearest power of 2 -> 64 (ceil not round b/c return val needs to include ALL seeds) + */ + public static Vector getSeedArray(int seedCount) + { + var testVector = new Vector() { { add(0); add(0); } }; + var testValue = testVector.get(0); + + int finalSize = ceilToPowerOfTwo(seedCount); + //System.out.println("finalSize " + finalSize); + ArrayList seeds = new ArrayList<>(2) { { add(1); add(2); } }; + //System.out.println("seeds " + seeds); + + // minSeed = the lowest seed (highest int values) 'processed' into the ArrayList<> seeds + int previousSeed = 2; + // empty ~ Pair(x,-1) -> x is always > 0 & represents a 'processed' seed + int emptySeeds = 0; + while (previousSeed < finalSize) { + // step 1 -> add filler entry (-1) to collection if all current seed values in seeds are processed + if (emptySeeds < 1) { + emptySeeds = seeds.size(); + int newSize = seeds.size() * 2; + // .collect & new ArrayList<> necessary b/c ArrayList is mutable & List is not + var newSeeds = new ArrayList<>(Stream.generate(() -> -1) + .limit(newSize) + .collect(Collectors.toList()) + ); + for (int i = 0; i * 2 < newSize; i++) { + newSeeds.add(i * 2,seeds.get(i)); // insert last processed value + newSeeds.remove(newSize); // trim offset filler entry from tail of collection + } + seeds = newSeeds; + //System.out.println("seeds " + seeds); + } + + // step 2 -> find next + // Pair + // value 1 -> the 'processed' seed value + // value 2 -> value 1's pair index (value 1 index + 1) + var seedIndex1 = new IndexedSeed(-1,-1); // empty ~ Pair(x,-1) + var seedIndex2 = new IndexedSeed(-1,-1); // empty ~ Pair(x,-1) + // +2 b/c always checking index that has a guaranteed processed seed value + // > basically -> value != -1 + for (int i = 0; i < seeds.size(); i = i + 2) { + if (seeds.get(i + 1) != -1) { + continue; + } + int seed = seeds.get(i); + if (seedIndex1.seed() >= seed && seedIndex2.seed() >= seed){ + continue; + } + // replace lower value seed (aka higher seed) + if (seedIndex1.seed() < seedIndex2.seed()){ + seedIndex1 = new IndexedSeed(i + 1,seed); // + 1, b/c looking for empty (-1) seed pairing + continue; + } + seedIndex2 = new IndexedSeed(i + 1,seed); + } + + // step 3 -> apply next seeds to array + var seed1 = ++previousSeed; + var seed2 = ++previousSeed; + emptySeeds = emptySeeds - 2; + + // !! 'Higher' -> 1 is 'Higher' seed than 2, but lower value + var isSeed1Higher = seedIndex1.seed() < seedIndex2.seed(); + int index1 = isSeed1Higher ? seedIndex2.index() : seedIndex1.index(); + int index2 = isSeed1Higher ? seedIndex1.index() : seedIndex2.index(); + seeds.add(index1,seed1); + seeds.remove(index1 + 1); + seeds.add(index2,seed2); + seeds.remove(index2 + 1); + } + + return doFinalSeedSort(seeds); + } + + private static Vector doFinalSeedSort(ArrayList seeds) + { + // all seed collections will be power of 2 -> safe to bit shift for half + int halfSize = seeds.size() >> 1; + var filler = new ArrayList(0); + // size = half size of seeds, b/c we want all arrays to have 2 seeds each + var seedArrays = new ArrayList<>(Stream.generate(() -> filler) + .limit(halfSize) + .collect(Collectors.toList()) + ); + seedArrays.add(0,seeds); + seedArrays.remove(1); + + // int used to make bit shifting safe + // -> should be 0 on even iterations & 1 on odd iterations + int iterationOffset = 0; + // iterations = (seeds.size/4) + var iterations = seeds.size() >> 2; + for (int i = 0; i < iterations; i++) { + + int size = firstFillerIndex(seedArrays); + for (int ii = 0; ii < size; ii++) { + + var index = ii * 2; + var array = seedArrays.get(index); + var arrays = split(array); + var front = arrays.get(0); + var back = arrays.get(1); + + //var frontMaxSeed = front.stream().min(Integer::compareTo).orElseThrow(); + //var backMaxSeed = back.stream().min(Integer::compareTo).orElseThrow(); + var frontMaxSeed = front.stream() + .min(Integer::compareTo) + .orElse(seeds.size()); + var backMaxSeed = back.stream() + .min(Integer::compareTo) + .orElse(seeds.size()); + + if (frontMaxSeed < backMaxSeed) { + // front has high seed -> reverse back order + reverse(back); + } else { + // back has high seed -> reverse front order + reverse(front); + } + + seedArrays.remove(index); // original array + seedArrays.add(index,front); + seedArrays.remove(seedArrays.size() - 1); // trim off tail + seedArrays.add(index + 1,back); + } + } + + + var finalSeeds = new Vector(Stream.generate(() -> -1) + .limit(seeds.size()) + .collect(Collectors.toList()) + ); + var index = 0; + var lastIndex = finalSeeds.size() - 1; + for (int i = 0; i < seedArrays.size(); i++) { + var nestedSeeds = seedArrays.get(i); + for (int ii = 0; ii < seedArrays.size(); ii++) { + if (nestedSeeds.size() <= ii) { + break; + } + finalSeeds.remove(lastIndex); + finalSeeds.add(index++,nestedSeeds.get(ii)); + } + } + return finalSeeds; + } + + public static Integer ceilToPowerOfTwo(int value) + { + int maxBitInt = Integer.highestOneBit(value); + return (!(value == 0) && (value ^ maxBitInt) == 0) ? value : maxBitInt << 1; + } + + private static int firstFillerIndex(ArrayList> arrayLists) + { + var firstEmptyIndex = -1; + for (int i = 0; i < arrayLists.size(); i++) { + if (arrayLists.get(i).isEmpty()){ + firstEmptyIndex = i; + break; + } + } + return firstEmptyIndex; + } + + // !! size must be pow of 2 b/c bit shift to middle used !! + private static ArrayList> split(ArrayList array) + { + int middle = array.size() >> 1; + var front = getNewFilled(middle); + var back = getNewFilled(middle); + for (int i = 0; i < middle; i++) { + front.add(i,array.get(i)); + front.remove(middle); + } + int index = middle; + for (int i = 0; i < middle; i++) { + back.add(i,array.get(index++)); + back.remove(middle); + } + return new ArrayList<>() { { add(front); add(back); } }; + } + + private static ArrayList getNewFilled(int length) + { + return new ArrayList<>(Stream.generate(() -> -1) + .limit(length) + .collect(Collectors.toList()) + ); + } + + private static void reverse(ArrayList arrayList) + { + int size = arrayList.size(); + for (int i = 0; i < size; i++) { + arrayList.add(i,arrayList.remove(size - 1)); + } + } + + public static boolean isPowerOfTwo(int value) + { + return !(value == 0) && (value ^ Integer.highestOneBit(value)) == 0; + } + +} diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java index ae3fcb8..8667847 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/CobblemonChallenge.java @@ -2,7 +2,7 @@ import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; -import com.turtlehoarder.cobblemonchallenge.config.DifficultyConfig; +import com.turtlehoarder.cobblemonchallenge.config.UniversalDifficultyConfig; import com.turtlehoarder.cobblemonchallenge.event.ChallengeEventHandler; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -18,7 +18,7 @@ public class CobblemonChallenge implements ModInitializer { @Override public void onInitialize() { ChallengeConfig.registerConfigs(); - DifficultyConfig.registerConfigs(); + UniversalDifficultyConfig.registerConfigs(); ChallengeEventHandler.registerEvents(); CommandRegistrationCallback.EVENT.register((commandDispatcher, commandBuildContext, commandSelection) -> ChallengeCommand.register(commandDispatcher)); } diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java index 374ca71..291e072 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/api/ChallengeProperties.java @@ -1,22 +1,47 @@ package com.turtlehoarder.cobblemonchallenge.api; -public class ChallengeProperties { - public ChallengeProperties(int minLevel,int maxLevel,int handicapP1,int handicapP2,boolean showPreview) { - _minLevel = minLevel; - _maxLevel = maxLevel; - _handicapP1 = handicapP1; - _handicapP2 = handicapP2; - _showPreview = showPreview; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import com.turtlehoarder.cobblemonchallenge.config.ChallengeConfig; +import org.jetbrains.annotations.Nullable; + +public final class ChallengeProperties +{ + private static final ChallengeFormat DEFAULT_CHALLENGE_FORMAT = ChallengeConfig.DEFAULT_CHALLENGE_FORMAT; + private static final int DEFAULT_CHALLENGE_LEVEL = ChallengeConfig.DEFAULT_CHALLENGE_LEVEL; + private static final int DEFAULT_HANDICAP = ChallengeConfig.DEFAULT_HANDICAP; + private static final Boolean DEFAULT_SHOW_PREVIEW = ChallengeConfig.DEFAULT_SHOW_PREVIEW; + + public ChallengeProperties(@Nullable ChallengeFormat format, Integer minLvl, Integer maxLvl, Integer handicapPlayer1, Integer handicapPlayer2, Boolean preview) { + _challengeFormat = format != null ? format : DEFAULT_CHALLENGE_FORMAT; + _maxLevel = clampIntNotNull(maxLvl, DEFAULT_CHALLENGE_LEVEL,0,100); + _minLevel = clampIntNotNull(maxLvl,Math.min(DEFAULT_CHALLENGE_LEVEL,_maxLevel),0,Math.min(100,_maxLevel)); + _handicapP1 = handicapPlayer1 != null ? handicapPlayer1 : DEFAULT_HANDICAP; + _handicapP2 = handicapPlayer2 != null ? handicapPlayer2 : DEFAULT_HANDICAP; + _showPreview = preview != null ? preview : DEFAULT_SHOW_PREVIEW; } - private int _minLevel; - private int _maxLevel; - private int _handicapP1; - private int _handicapP2; - private boolean _showPreview; - public int getMinLevel () { return _minLevel; } - public int getMaxLevel () { return _maxLevel; } - public int getHandicapP1 () { return _handicapP1; } - public int getHandicapP2 () { return _handicapP2; } - public boolean getShowPreview () { return _showPreview; } + private final ChallengeFormat _challengeFormat; + private final int _minLevel; + private final int _maxLevel; + private final int _handicapP1; + private final int _handicapP2; + private final boolean _showPreview; + + public ChallengeFormat getChallengeFormat() { return _challengeFormat; } + public int getMinLevel() { return _minLevel; } + public int getMaxLevel() { return _maxLevel; } + public int getHandicapP1() { return _handicapP1; } + public int getHandicapP2() { return _handicapP2; } + public boolean getShowPreview() { return _showPreview; } + + private int clampIntNotNull(Integer value,int defaultValue,int min, int max) { + if (value == null) { + return defaultValue; + } else if (value > max){ + return max; + } else if(value < min){ + return min; + } + return value; + } } diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java index e01421d..ccd548f 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/battle/ChallengeBattleBuilder.java @@ -1,5 +1,9 @@ package com.turtlehoarder.cobblemonchallenge.battle; +import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; +import com.turtlehoarder.cobblemonchallenge.api.ChallengeProperties; +import com.turtlehoarder.cobblemonchallenge.battle.pokemon.ChallengeBattlePokemon; + import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.api.battles.model.PokemonBattle; import com.cobblemon.mod.common.api.storage.party.PartyStore; @@ -10,28 +14,27 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon; import com.cobblemon.mod.common.entity.pokemon.PokemonEntity; import com.cobblemon.mod.common.pokemon.Pokemon; -import com.turtlehoarder.cobblemonchallenge.CobblemonChallenge; -import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; -import kotlin.Unit; + import net.minecraft.server.level.ServerPlayer; import java.util.ArrayList; import java.util.List; import java.util.Vector; +import kotlin.Unit; public class ChallengeBattleBuilder { public static Vector clonedPokemonList = new Vector<>(); public static Vector challengeBattles = new Vector<>(); - private ChallengeFormat format = ChallengeFormat.STANDARD_6V6; - public void lvlxpvp(ServerPlayer player1, ServerPlayer player2, BattleFormat battleFormat, int minLevel, int maxLevel, int handicapP1, int handicapP2, List player1Selection, List player2Selection) throws ChallengeBuilderException { + private final ChallengeFormat format = ChallengeFormat.STANDARD_6V6; + public void lvlxpvp(ServerPlayer player1, ServerPlayer player2, BattleFormat battleFormat, ChallengeProperties properties, List player1Selection, List player2Selection) throws ChallengeBuilderException { PartyStore p1Party = Cobblemon.INSTANCE.getStorage().getParty(player1); PartyStore p2Party = Cobblemon.INSTANCE.getStorage().getParty(player2); // Clone parties so original is not effected - List player1Team = createBattleTeamFromParty(p1Party, player1Selection, minLevel, maxLevel, handicapP1); - List player2Team = createBattleTeamFromParty(p2Party, player2Selection, minLevel, maxLevel, handicapP2); + List player1Team = createBattleTeamFromParty(p1Party, player1Selection, properties, properties.getHandicapP1()); + List player2Team = createBattleTeamFromParty(p2Party, player2Selection, properties, properties.getHandicapP2()); PlayerBattleActor player1Actor = new PlayerBattleActor(player1.getUUID(), player1Team); PlayerBattleActor player2Actor = new PlayerBattleActor(player2.getUUID(), player2Team); @@ -44,9 +47,9 @@ public void lvlxpvp(ServerPlayer player1, ServerPlayer player2, BattleFormat bat } // Method to create our own clones according to the format - private List createBattleTeamFromParty(PartyStore party, List selectedSlots, int minLevel, int maxLevel, int handicap) throws ChallengeBuilderException { + private List createBattleTeamFromParty(PartyStore party, List selectedSlots, ChallengeProperties properties, int handicap) throws ChallengeBuilderException { - List battlePokemonList = new ArrayList(); + List battlePokemonList = new ArrayList<>(); if (format == ChallengeFormat.STANDARD_6V6) { int leadSlot = selectedSlots.get(0); Pokemon leadPokemon = party.get(leadSlot); @@ -55,15 +58,15 @@ private List createBattleTeamFromParty(PartyStore party, List("challengeDistanceRestriction", true)); configs.addKeyValuePair(new Pair<>("maxChallengeDistance", 50)); + configs.addKeyValuePair(new Pair<>("defaultChallengeFormat", ChallengeFormat.STANDARD_6V6)); configs.addKeyValuePair(new Pair<>("defaultChallengeLevel", 50)); configs.addKeyValuePair(new Pair<>("defaultHandicap", 0)); configs.addKeyValuePair(new Pair<>("defaultShowPreview", true)); @@ -34,6 +40,7 @@ private static void createConfigs() { private static void assignConfigs() { CHALLENGE_COOLDOWN_MILLIS = CONFIG.getOrDefault("challengeCooldownTime", 5000); CHALLENGE_DISTANCE_RESTRICTION = CONFIG.getOrDefault("challengeDistanceRestriction", true); + DEFAULT_CHALLENGE_FORMAT = CONFIG.getOrDefault("defaultChallengeFormat",ChallengeFormat.STANDARD_6V6); DEFAULT_CHALLENGE_LEVEL = CONFIG.getOrDefault("defaultChallengeLevel", 50); DEFAULT_HANDICAP = CONFIG.getOrDefault("defaultHandicap", 0); DEFAULT_SHOW_PREVIEW = CONFIG.getOrDefault("defaultShowPreview", true); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ConfigProvider.java similarity index 87% rename from src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java rename to src/main/java/com/turtlehoarder/cobblemonchallenge/config/ConfigProvider.java index 3ab78e3..b9536d1 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ChallengeConfigProvider.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/ConfigProvider.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import java.util.List; -public class ChallengeConfigProvider implements SimpleConfig.DefaultConfig { +public class ConfigProvider implements SimpleConfig.DefaultConfig { private String configContents = ""; private final List configsList = new ArrayList<>(); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/SimpleConfig.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/SimpleConfig.java index dc52b94..ddc5e17 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/SimpleConfig.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/SimpleConfig.java @@ -21,6 +21,9 @@ * THE SOFTWARE. */ +import com.cobblemontournament.api.tournament.TournamentType; +import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; + import net.fabricmc.loader.api.FabricLoader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -226,6 +229,36 @@ public double getOrDefault( String key, double def ) { } } + /** + * Returns ChallengeFormat enum value from config corresponding to the given + * key, or the default ChallengeFormat enum if the key is missing or invalid. + * + * @return value corresponding to the given key, or the default value + */ + public ChallengeFormat getOrDefault( String key, ChallengeFormat def ) { + for (ChallengeFormat f : ChallengeFormat.values()) { + if (f.name().equals(key)) { + return f; + } + } + return def; + } + + /** + * Temporary while tournaments is integrated + * @param key + * @param def + * @return + */ + public TournamentType getOrDefault(String key, TournamentType def ) { + for (TournamentType f : TournamentType.values()) { + if (f.name().equals(key)) { + return f; + } + } + return def; + } + /** * If any error occurred during loading or reading from the config * a 'broken' flag is set, indicating that the config's state diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java index 5ffbdef..2c1eaf4 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/util/ChallengeUtil.java @@ -11,7 +11,6 @@ import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeBattleBuilder; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; -import com.turtlehoarder.cobblemonchallenge.command.ChallengeCommand; import net.minecraft.ChatFormatting; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringTag; @@ -55,12 +54,13 @@ public static boolean isBattleChallenge(UUID battleId) { } public static boolean isPlayerOnline(ServerPlayer player) { + if (player.getServer() == null) { return false; } // prevent NullPointerException return player.getServer().getPlayerList().getPlayer(player.getUUID()) != null; } public static ChallengeRequest createChallengeRequest(ServerPlayer challengerPlayer, ServerPlayer challengedPlayer, int minLevel, int maxLevel, int handicapP1, int handicapP2, boolean showPreview) { String key = UUID.randomUUID().toString().replaceAll("-", ""); - return new ChallengeRequest(key, challengerPlayer, challengedPlayer, new ChallengeProperties(minLevel, maxLevel, handicapP1, handicapP2, showPreview), System.currentTimeMillis()); + return new ChallengeRequest(key, challengerPlayer, challengedPlayer, new ChallengeProperties(null,minLevel, maxLevel, handicapP1, handicapP2, showPreview), System.currentTimeMillis()); } public static ItemLike getDisplayBlockForPokemon(Pokemon pokemon) { @@ -96,12 +96,12 @@ public static ListTag generateLoreTagForPokemon(Pokemon pokemon) { String statSeparator = ChatFormatting.GRAY + " / "; Component statsPartOne = Component.literal(String.format(ChatFormatting.RED + "HP: %d" + statSeparator + ChatFormatting.GOLD + "Atk: %d" + statSeparator + ChatFormatting.YELLOW + "Def: %d", pokemon.getHp(), pokemon.getAttack(), pokemon.getDefence())); Component statsPartTwo = Component.literal(String.format(ChatFormatting.AQUA + "SpA: %d" + statSeparator + ChatFormatting.GREEN + "SpD: %d" + statSeparator + ChatFormatting.LIGHT_PURPLE + "Spe: %d", pokemon.getSpecialAttack(), pokemon.getSpecialDefence(), pokemon.getSpeed())); - Component moveSeperator = Component.literal( "Moves:"); + Component moveSeparator = Component.literal( "Moves:"); loreTag.add(StringTag.valueOf(Component.Serializer.toJson(abilityComponent))); loreTag.add(StringTag.valueOf(Component.Serializer.toJson(natureComponent))); loreTag.add(StringTag.valueOf(Component.Serializer.toJson(statsPartOne))); loreTag.add(StringTag.valueOf(Component.Serializer.toJson(statsPartTwo))); - loreTag.add(StringTag.valueOf(Component.Serializer.toJson(moveSeperator))); + loreTag.add(StringTag.valueOf(Component.Serializer.toJson(moveSeparator))); pokemon.getMoveSet().getMoves().forEach(move -> { Component moveComponent = Component.literal(ChatFormatting.WHITE + String.format("%s - %d/%d", move.getDisplayName().getString() + ChatFormatting.GRAY, move.getMaxPp(), move.getMaxPp())); loreTag.add(StringTag.valueOf(Component.Serializer.toJson(moveComponent))); @@ -112,14 +112,15 @@ public static ListTag generateLoreTagForPokemon(Pokemon pokemon) { // Roundabout, but reliable way of getting the associated owner UUID of the cloned pokemon sent out in a challenge public static UUID getOwnerUuidOfClonedPokemon(PokemonBattle battle, PokemonEntity pokemonEntity) { for (ActiveBattlePokemon abp : battle.getActivePokemon()) { - if (abp.getBattlePokemon() != null && pokemonEntity.getPokemon().getUuid().equals(abp.getBattlePokemon().getEffectedPokemon().getUuid())) { - return abp.getBattlePokemon().getOriginalPokemon().getOwnerUUID(); + var battlePokemon = abp.getBattlePokemon(); + if (battlePokemon != null && pokemonEntity.getPokemon().getUuid().equals(battlePokemon.getEffectedPokemon().getUuid())) { + return battlePokemon.getOriginalPokemon().getOwnerUUID(); } } - return null; } + @SuppressWarnings("unused") public static BattlePokemon applyFormatTransformations(ChallengeFormat format, BattlePokemon pokemon, int level) { if (format == ChallengeFormat.STANDARD_6V6) { pokemon.getEffectedPokemon().setLevel(level); @@ -128,10 +129,6 @@ public static BattlePokemon applyFormatTransformations(ChallengeFormat format, B return pokemon; } - // Method for clamping Battle Pokemon to level range, between 1-100, & applying handicap - // > the handicap applied AFTER level clamp to range - // > A players level may be outside this range after the handicap is applied - // > But, the finalized handicap will be a hard clamped to (1,100) public static int getBattlePokemonAdjustedLevel(int actualLevel, int minLevel, int maxLevel, int handicap) { int adjustedLevel = (actualLevel < minLevel) ? minLevel + handicap : Math.min(actualLevel, maxLevel) + handicap; return (adjustedLevel < 1) ? 1 : Math.min(adjustedLevel, 100); diff --git a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt index d785504..290e293 100644 --- a/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt +++ b/src/main/kotlin/com/turtlehoarder/cobblemonchallenge/battle/pokemon/ChallengeBattlePokemon.kt @@ -5,12 +5,14 @@ import com.cobblemon.mod.common.api.battles.model.actor.ActorType import com.cobblemon.mod.common.api.tags.CobblemonItemTags import com.cobblemon.mod.common.battles.pokemon.BattlePokemon import com.cobblemon.mod.common.entity.pokemon.PokemonEntity -import com.cobblemon.mod.common.pokemon.evolution.requirements.LevelRequirement import com.cobblemon.mod.common.pokemon.Pokemon +import com.cobblemon.mod.common.pokemon.evolution.requirements.LevelRequirement +import kotlin.math.min import kotlin.math.pow import kotlin.math.roundToInt + class ChallengeBattlePokemon( originalPokemon: Pokemon, effectedPokemon: Pokemon, @@ -18,24 +20,33 @@ class ChallengeBattlePokemon( ) : BattlePokemon(originalPokemon) { companion object { - fun safeCopyOf(pokemon: Pokemon): ChallengeBattlePokemon = ChallengeBattlePokemon( + fun safeCopyOfChallenge(pokemon: Pokemon): ChallengeBattlePokemon = ChallengeBattlePokemon( originalPokemon = pokemon, - // TODO Need to apply changes to the effected pokemon as it is the one shown in battle - // > See com.cobblemon.mod.common.battles.pokemon.BattlePokemon - // > all val are based off of effectedPokemon in base class effectedPokemon = pokemon.clone(), - postBattleEntityOperation = { - // TODO inject post battle logic here - entity -> entity.discard() - } + postBattleEntityOperation = { entity -> entity.discard() } ) } - // TODO: call from mixin -> if BattlePokemon is ChallengeBattlePokemon in PokemonBattle.end() -> - // redirect here else -> - // call ExperienceCalculator.Calculate as normally done - // original call -> - // com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator.Calculate + /** + * Method for clamping Battle Pokemon to level range & applying a handicap + * + * - [handicap] is applied AFTER level clamp to range + * - The [effectedPokemon] level may be outside the levelRange after the handicap is applied, but will be a hard clamped to 1-100 inclusive + */ + fun applyChallengePropertiesToEffectedPokemon(minLevel: Int, maxLevel: Int, handicap: Int,heal: Boolean) : BattlePokemon { + val adjustedLevel = if ((originalPokemon.level < minLevel)) minLevel + handicap else (min(originalPokemon.level, maxLevel) + handicap) + effectedPokemon.level = if ((adjustedLevel < 1)) 1 else min(adjustedLevel,100) + if (heal) effectedPokemon.heal() + return this + } + + /* + * TODO: call from mixin -> if BattlePokemon is ChallengeBattlePokemon in PokemonBattle.end() -> + * redirect here else -> + * call ExperienceCalculator.Calculate as normally done + * original call -> + * com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator.Calculate + */ fun calculateExperience(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon, participationMultiplier: Double): Int { // This is meant to be a division but this is due to the intended behavior of handling the 2.0 sent over from Exp. All in modern Pokémon diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 5ea5a4d..1b66f0f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -19,7 +19,8 @@ "environment": "*", "entrypoints": { "main": [ - "com.turtlehoarder.cobblemonchallenge.CobblemonChallenge" + "com.turtlehoarder.cobblemonchallenge.CobblemonChallenge", + "com.cobblemontournament.CobblemonTournament" ] }, "depends": { From 1c9ed3a09a492984730e2cff61ad27b821bb038d Mon Sep 17 00:00:00 2001 From: Eight Date: Sun, 16 Jun 2024 21:17:14 -0700 Subject: [PATCH 14/15] WIP: 1. Mixin implementation 2. General refactor/clean up 3. Initial 'Universal Difficulty' config --- ...ig.java => UniversalDifficultyConfig.java} | 6 +- .../gui/LeadPokemonMenuProvider.java | 19 ++--- .../gui/LeadPokemonSelectionSession.java | 6 +- .../mixin/BattleBuilderMixin.java | 77 +++++++++++++++++++ .../mixin/pvePreBattleBuilderMixin.java | 14 ---- .../mixin/pvp1v1PreBattleBuilderMixin.java | 75 ------------------ src/main/resources/mixins.common.json | 3 +- 7 files changed, 92 insertions(+), 108 deletions(-) rename src/main/java/com/turtlehoarder/cobblemonchallenge/config/{DifficultyConfig.java => UniversalDifficultyConfig.java} (94%) create mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/BattleBuilderMixin.java delete mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java delete mode 100644 src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/UniversalDifficultyConfig.java similarity index 94% rename from src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java rename to src/main/java/com/turtlehoarder/cobblemonchallenge/config/UniversalDifficultyConfig.java index 2796692..5d2f979 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/config/DifficultyConfig.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/config/UniversalDifficultyConfig.java @@ -4,10 +4,10 @@ import com.mojang.datafixers.util.Pair; // temp class for possible configs while testing new features -public class DifficultyConfig { +public class UniversalDifficultyConfig { public static SimpleConfig CONFIG; - private static ChallengeConfigProvider configs; + private static ConfigProvider configs; public static Boolean USE_UNIVERSAL_LEVEL; public static Boolean USE_UNIVERSAL_LEVEL_RANGE; public static Boolean USE_UNIVERSAL_HANDICAP; @@ -19,7 +19,7 @@ public class DifficultyConfig { public static void registerConfigs() { CobblemonChallenge.LOGGER.info("Loading Universal Difficulty Configs"); - configs = new ChallengeConfigProvider(); + configs = new ConfigProvider(); createConfigs(); CONFIG = SimpleConfig.of(CobblemonChallenge.MODID + "-universal-difficulty-config").provider(configs).request(); assignConfigs(); diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java index 1ed4177..6ea5398 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonMenuProvider.java @@ -2,12 +2,12 @@ import com.turtlehoarder.cobblemonchallenge.api.ChallengeRequest; import com.turtlehoarder.cobblemonchallenge.battle.ChallengeFormat; +import com.turtlehoarder.cobblemonchallenge.battle.pokemon.ChallengeBattlePokemon; import com.turtlehoarder.cobblemonchallenge.util.ChallengeUtil; import com.cobblemon.mod.common.Cobblemon; import com.cobblemon.mod.common.CobblemonItems; import com.cobblemon.mod.common.api.storage.party.PartyStore; -import com.cobblemon.mod.common.battles.pokemon.BattlePokemon; import com.cobblemon.mod.common.item.PokemonItem; import com.cobblemon.mod.common.pokemon.Pokemon; @@ -72,27 +72,28 @@ private void setupPokemonRepresentation(LeadPokemonMenu leadPokemonMenu) { PartyStore p2Party = Cobblemon.INSTANCE.getStorage().getParty(rival); setupGlassFiller(leadPokemonMenu); + // Cache handicap int handicapP1 = (this.selector == request.challengerPlayer()) ? request.properties().getHandicapP1() : request.properties().getHandicapP2(); - int handicapP2 = (this.selector == request.challengerPlayer()) ? request.properties().getHandicapP2() : request.properties().getHandicapP1(); - for (int x = 0; x < p1Party.size(); x ++) { - int itemSlot = x * 9; // Lefthand column of the menu + int itemSlot = x * 9; // Left hand column of the menu Pokemon pokemon = p1Party.get(x); if (pokemon == null) // Skip any empty slots in the pokemon team continue; - BattlePokemon copy = BattlePokemon.Companion.safeCopyOf(pokemon); - int adjustedLevelP1 = ChallengeUtil.getBattlePokemonAdjustedLevel(pokemon.getLevel(), request.properties().getMinLevel(), request.properties().getMaxLevel(), handicapP1); - pokemon = ChallengeUtil.applyFormatTransformations(ChallengeFormat.STANDARD_6V6, copy, adjustedLevelP1).getEffectedPokemon(); // Apply battle transformations to each pokemon + ChallengeBattlePokemon copy = ChallengeBattlePokemon.Companion.safeCopyOfChallenge(pokemon); + copy.applyChallengePropertiesToEffectedPokemon(request.properties().getMinLevel(), request.properties().getMaxLevel(), handicapP1,true); + pokemon = copy.getEffectedPokemon(); ItemStack pokemonItem = PokemonItem.from(pokemon, 1); - pokemonItem.setHoverName(Component.literal(ChatFormatting.AQUA + String.format("%s (lvl%d)", pokemon.getDisplayName().getString(), adjustedLevelP1))); + pokemonItem.setHoverName(Component.literal(ChatFormatting.AQUA + String.format("%s (lvl%d)", pokemon.getDisplayName().getString(), pokemon.getLevel()))); ListTag pokemonLoreTag = ChallengeUtil.generateLoreTagForPokemon(pokemon); pokemonItem.getOrCreateTagElement("display").put("Lore", pokemonLoreTag); leadPokemonMenu.setItem(itemSlot, leadPokemonMenu.getStateId(), pokemonItem); } + // Cache enemy handicap + int handicapP2 = (this.selector == request.challengerPlayer()) ? request.properties().getHandicapP2() : request.properties().getHandicapP1(); // Set enemy side: for (int x= 0; x < p2Party.size(); x++) { - int itemSlot = (x * 9) + 8; // Righthand column of the menu + int itemSlot = (x * 9) + 8; // Right hand column of the menu Pokemon pokemon = p2Party.get(x); if (pokemon == null) { continue; diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java index f1e05c8..cfb7b8c 100644 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/gui/LeadPokemonSelectionSession.java @@ -63,16 +63,12 @@ public void onPokemonSelected(LeadPokemonMenuProvider menuProvider) { } private void beginBattle() { - int minLevel = originRequest.properties().getMinLevel(); - int maxLevel = originRequest.properties().getMaxLevel(); - int handicapP1 = originRequest.properties().getHandicapP1(); - int handicapP2 = originRequest.properties().getHandicapP2(); SESSIONS_TO_CANCEL.add(this); challengerMenuProvider.forceCloseMenu(); challengedMenuProvider.forceCloseMenu(); ChallengeBattleBuilder challengeBuilder = new ChallengeBattleBuilder(); try { - challengeBuilder.lvlxpvp(originRequest.challengerPlayer(), originRequest.challengedPlayer(), BattleFormat.Companion.getGEN_9_SINGLES(), minLevel, maxLevel, handicapP1, handicapP2, challengerMenuProvider.selectedSlots, challengedMenuProvider.selectedSlots); + challengeBuilder.lvlxpvp(originRequest.challengerPlayer(), originRequest.challengedPlayer(), BattleFormat.Companion.getGEN_9_SINGLES(), originRequest.properties(), challengerMenuProvider.selectedSlots, challengedMenuProvider.selectedSlots); } catch (ChallengeBuilderException e) { e.printStackTrace(); } diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/BattleBuilderMixin.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/BattleBuilderMixin.java new file mode 100644 index 0000000..098d8f5 --- /dev/null +++ b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/BattleBuilderMixin.java @@ -0,0 +1,77 @@ +package com.turtlehoarder.cobblemonchallenge.mixin; + +import com.cobblemon.mod.common.api.storage.party.PartyStore; +import com.cobblemon.mod.common.battles.BattleBuilder; +import com.cobblemon.mod.common.battles.pokemon.BattlePokemon; +import com.turtlehoarder.cobblemonchallenge.config.UniversalDifficultyConfig; + +import net.fabricmc.loader.impl.util.log.Log; +import net.fabricmc.loader.impl.util.log.LogCategory; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.UUID; + +@Mixin(BattleBuilder.class) +public class BattleBuilderMixin { + + @Unique private final static boolean useUniversalLevel = UniversalDifficultyConfig.USE_UNIVERSAL_LEVEL; + @Unique private final static boolean useUniversalLevelRange = UniversalDifficultyConfig.USE_UNIVERSAL_LEVEL_RANGE; + @Unique private final static boolean useUniversalHandicap = UniversalDifficultyConfig.USE_UNIVERSAL_HANDICAP; + + @Redirect( + method = "pvp1v1*", + at = @At( + value = "INVOKE", + target = "Lcom/cobblemon/mod/common/api/storage/party/PartyStore;toBattleTeam(ZZLjava/util/UUID;)Ljava/util/List;", + remap = false + )) + private List mixinPvpToBattleTeam( + PartyStore partyStore, + boolean cloneParties, + boolean healFirst, + @Nullable UUID leadingPokemon, + CallbackInfoReturnable> battleSideCI + ) { + Log.debug(LogCategory.LOG,"partyStore = '" + partyStore.toString() + "'"); + Log.debug(LogCategory.LOG,"mixin Redirect at BattleBuilder.pvp1v1 worked!"); + if (useUniversalLevel || useUniversalLevelRange) { // TODO expand on with future configs + // TODO Implement custom battle team here w/ ChallengeBattlePokemon + //return ; + } + // passing onto the default implementation + return partyStore.toBattleTeam(cloneParties, healFirst, leadingPokemon); + } + + + @Redirect( + method = "pve*", + at = @At( + value = "INVOKE", + target = "Lcom/cobblemon/mod/common/api/storage/party/PartyStore;toBattleTeam(ZZLjava/util/UUID;)Ljava/util/List;", + remap = false + )) + public List mixinPveToBattleTeam( + PartyStore partyStore, + boolean cloneParties, + boolean healFirst, + @Nullable UUID leadingPokemon + ) { + // TODO expand on with future configs + if ((useUniversalLevel || useUniversalLevelRange) || useUniversalHandicap) { + // TODO Implement custom battle team here w/ ChallengeBattlePokemon + // !! possible alternative -> + // capture parameters, or @Inject + HEAD to capture/test parameters then -> + // @Redirect + INVOKE .toBattleTeam w/ callbackInfo + //return ; + } + // passing onto the default implementation + return partyStore.toBattleTeam(cloneParties, healFirst, leadingPokemon); + } +} diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java deleted file mode 100644 index 0fcd70a..0000000 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvePreBattleBuilderMixin.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.turtlehoarder.cobblemonchallenge.mixin; - -import com.cobblemon.mod.common.battles.BattleBuilder; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; - -@Mixin(BattleBuilder.class) -public class pvePreBattleBuilderMixin { - @Inject(method = "pve*", at = @At(value = "HEAD")) - public void method() { - - } -} \ No newline at end of file diff --git a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java b/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java deleted file mode 100644 index 6f51af6..0000000 --- a/src/main/java/com/turtlehoarder/cobblemonchallenge/mixin/pvp1v1PreBattleBuilderMixin.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.turtlehoarder.cobblemonchallenge.mixin; - -import com.turtlehoarder.cobblemonchallenge.config.DifficultyConfig; -import com.cobblemon.mod.common.battles.BattleBuilder; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; - -@Mixin(BattleBuilder.class) -public class pvp1v1PreBattleBuilderMixin { - - @Inject(method = "pvp1v1*", at = @At(value = "HEAD")) - public void handlePreBattleCheck() { - /* - * Parameters for main method: - * - * player1: ServerPlayerEntity, - * player2: ServerPlayerEntity, - * leadingPokemonPlayer1: UUID? = null, - * leadingPokemonPlayer2: UUID? = null, - * battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES, - * cloneParties: Boolean = false, - * healFirst: Boolean = false, - * partyAccessor: (ServerPlayerEntity) -> PartyStore = { it.party() } - * - * gate -> Are there configs that apply? -> check cached value for pvp configs - * > true -> progress to #2 - * > false -> do nothing - * 2. Do the configs apply? - * > true -> register battle & apply adjustments to parameters -> progress to #3 - * > false -> do nothing - * 3. for now, nothing, b/c second mixin - * > second mixin: - * > path: 'com.cobblemon.mod.common.battles.BattleRegistry.startBattle' - * > when: 'tail' - * > purpose: get PokemonBattle && pokemon battle start result - * > class path: 'com.cobblemon.mod.common.api.battles.model.PokemonBattle' - * > target UUID: 'PokemonBattle.battleId' -> val battleId = UUID.randomUUID() - * > !! A LOT of uses for PokemonBattle !! - * >> Be thorough, so nothing slips through - * >> Find the single exit if possible - * After: Use override val battle: PokemonBattle in events to process each battle. - * com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent - * com.cobblemon.mod.common.api.events.battles.BattleFaintedEvent - * com.cobblemon.mod.common.api.events.battles.BattleFledEvent - * - * ?! Are there any battles that can escape outside of these three events... ?! - * - * TODO: Handle application of status & post battle impact on actual pokemon - * > still planning... - * > ?? store reference to original pokemon & use mixin to catch battle at conclusion ?? - * > this is the only entry -> find the only exit... - * > caveat: need to catch ErroredBattleStart if start battle called & error is thrown - * > need to update battle registry - */ - - - if (DifficultyConfig.USE_UNIVERSAL_LEVEL || DifficultyConfig.USE_UNIVERSAL_LEVEL_RANGE || DifficultyConfig.USE_UNIVERSAL_HANDICAP) { - /* - * 1. Apply Pre Battle Configs - * - * 2. Make sure clones are used - * > ?? should be thrown out after battle ?? - * > if so -> worst case is statuses/exp is not applied to actual pokemon - * > make sure clones that are cloned are also destroyed - * > check battles periodically to confirm any registered active battles are actually active - * > clear clones if any battles are left hanging - * 3. Cache HashMap of UUID (original,clone) - * > need to figure out how to get clones-clone to apply changes to original pokemon - */ - } - } -} - diff --git a/src/main/resources/mixins.common.json b/src/main/resources/mixins.common.json index 1f5f5ac..a4947ed 100644 --- a/src/main/resources/mixins.common.json +++ b/src/main/resources/mixins.common.json @@ -4,8 +4,7 @@ "package": "com.turtlehoarder.cobblemonchallenge.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ - "pvp1v1PreBattleBuilderMixin", - "pvePreBattleBuilderMixin" + "BattleBuilderMixin" ], "client": [ ], From d455f91407677b47477edef16d15454bedcd753a Mon Sep 17 00:00:00 2001 From: Eight Date: Sun, 16 Jun 2024 21:37:42 -0700 Subject: [PATCH 15/15] Fix: Added newline at end of files --- src/main/java/com/cobblemontournament/CobblemonTournament.java | 2 +- .../java/com/cobblemontournament/api/player/SeededPlayer.java | 2 +- .../api/tournament/TournamentProperties.java | 2 +- src/main/java/com/cobblemontournament/util/IndexedSeed.java | 2 +- .../java/com/cobblemontournament/util/IndexedSeedArray.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/cobblemontournament/CobblemonTournament.java b/src/main/java/com/cobblemontournament/CobblemonTournament.java index 27b6cb5..3ad8f70 100644 --- a/src/main/java/com/cobblemontournament/CobblemonTournament.java +++ b/src/main/java/com/cobblemontournament/CobblemonTournament.java @@ -21,4 +21,4 @@ public void onInitialize() CommandRegistrationCallback.EVENT.register((commandDispatcher, commandBuildContext, commandSelection) -> TournamentCommandTest.register(commandDispatcher)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java b/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java index 982dcac..4243a3f 100644 --- a/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java +++ b/src/main/java/com/cobblemontournament/api/player/SeededPlayer.java @@ -2,4 +2,4 @@ import java.util.UUID; -public record SeededPlayer (UUID id, Integer seed) { } \ No newline at end of file +public record SeededPlayer (UUID id, Integer seed) { } diff --git a/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java b/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java index f207c79..8a8525e 100644 --- a/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java +++ b/src/main/java/com/cobblemontournament/api/tournament/TournamentProperties.java @@ -57,4 +57,4 @@ public TournamentProperties ( public int getMinLevel(){ return minLevel; } public int getMaxLevel(){ return maxLevel; } public boolean getShowPreview(){ return showPreview; } -} \ No newline at end of file +} diff --git a/src/main/java/com/cobblemontournament/util/IndexedSeed.java b/src/main/java/com/cobblemontournament/util/IndexedSeed.java index d08fc88..1771d9e 100644 --- a/src/main/java/com/cobblemontournament/util/IndexedSeed.java +++ b/src/main/java/com/cobblemontournament/util/IndexedSeed.java @@ -1,3 +1,3 @@ package com.cobblemontournament.util; -public record IndexedSeed(Integer index, Integer seed) { } \ No newline at end of file +public record IndexedSeed(Integer index, Integer seed) { } diff --git a/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java b/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java index 0f7a1ad..ff6fa3c 100644 --- a/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java +++ b/src/main/java/com/cobblemontournament/util/IndexedSeedArray.java @@ -59,4 +59,4 @@ public void sortByIndexDescending() indexedSeedStatus = IndexedSeedSortType.INDEX_DESCENDING; } -} \ No newline at end of file +}