Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0358a55
Represent, load, and save queues of DraftBags
ninevra Jun 17, 2025
80a7816
Mark some relevant code for draft bag queueing
ninevra Jun 17, 2025
6e391e2
Make Player.currentDraftBag nullable
ninevra Jun 17, 2025
ae8de85
Implement draft bag queueing logic
ninevra Jun 18, 2025
4aa7984
Move DraftBag load logic into DrafBag
ninevra Jun 18, 2025
856be88
Improve bag passing message
ninevra Jun 18, 2025
8445abd
Warn on passing null bags
ninevra Jun 18, 2025
a090339
Update draft start message to explain queueing
ninevra Jun 18, 2025
09adc10
Display draft status immediately after start
ninevra Jun 18, 2025
c7a19e2
Clear player queues in draft setup
ninevra Jun 18, 2025
9d42fff
Report an error when unselecting items with no bag
ninevra Jun 18, 2025
6856845
Refactor for clarity
ninevra Jun 18, 2025
9c1ea7e
Store current bag as first bag in queue
ninevra Jun 19, 2025
4618bb1
Rename "draft item queue" to "draft item selection"
ninevra Jun 19, 2025
75f7144
Fix bug in detecting when a player starts a bag
ninevra Jun 19, 2025
663d518
Fix printing first draft bag twice
ninevra Jun 19, 2025
e7d216a
Improve draft messages
ninevra Jun 20, 2025
a90c16d
Rename item queue to selection in franken_edit
ninevra Jun 20, 2025
d8b8b17
Show queued bags in franken_edit view_all
ninevra Jun 20, 2025
cb0f919
Support franken_edit on queued bags
ninevra Jun 20, 2025
0d3f78e
Allow franken_edit to force pass a player's bag
ninevra Jun 20, 2025
80c202f
Refactor draft messages
ninevra Jun 20, 2025
06c98e5
Clear selections before distributing bags
ninevra Jun 21, 2025
bc9a464
Fix typo in a franken_edit message
ninevra Jun 21, 2025
fe51129
Remove some debug log messages
ninevra Jun 21, 2025
7a4dd6f
Merge branch 'master' into queue-draft-bags
ninevra Jun 21, 2025
3423461
Pass draft bags to the right
ninevra Jun 21, 2025
c1f86bf
Prevent infinite recursion from undraftable bags
ninevra Jun 21, 2025
1c2b47e
Fix bugs with serializing empty draft bags
ninevra Jun 21, 2025
1e095b5
Add an emoji for draft bags
ninevra Jun 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 42 additions & 29 deletions src/main/java/ti4/buttons/handlers/FrankenButtonHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
import ti4.listeners.annotations.ButtonHandler;
import ti4.map.Game;
import ti4.map.Player;
import ti4.message.BotLogger;
import ti4.message.BotLogger.LogMessageOrigin;
import ti4.message.GameMessageManager;
import ti4.message.GameMessageType;
import ti4.message.MessageHelper;
import ti4.model.DraftErrataModel;
import ti4.model.FactionModel;
Expand Down Expand Up @@ -139,39 +143,38 @@ public static void resolveFrankenDraftAction(Game game, Player player, ButtonInt
if (!action.contains(":")) {
switch (action) {
case "reset_queue" -> {
player.getCurrentDraftBag().Contents.addAll(player.getDraftQueue().Contents);
player.resetDraftQueue();
FrankenDraftBagService.showPlayerBag(game, player);
DraftBag bag = player.getCurrentDraftBag().orElse(null);
if (bag == null) {
BotLogger.warning(new LogMessageOrigin(event, player), "Tried to reset draft selection while no current bag.");
} else {
bag.Contents.addAll(player.getDraftItemSelection().Contents);
player.resetDraftSelection();
FrankenDraftBagService.showPlayerBag(game, player);
}
return;
}
case "confirm_draft" -> {
player.getDraftHand().Contents.addAll(player.getDraftQueue().Contents);
player.resetDraftQueue();
draft.setPlayerReadyToPass(player, true);

// Clear out all existing messages
draft.findExistingBagChannel(player).getHistory().retrievePast(100).queue(m -> {
if (!m.isEmpty()) {
draft.findExistingBagChannel(player).deleteMessages(m).queue();
}
});
MessageHelper.sendMessageToChannel(draft.findExistingBagChannel(player), "Your Draft Bag is ready to pass and you are waiting for the other players to finish drafting.");
MessageHelper.sendMessageToChannel(player.getCardsInfoThread(), "You are passing the following cards to your right:\n" + FrankenDraftBagService.getBagReceipt(player.getCurrentDraftBag()));
player.getDraftHand().Contents.addAll(player.getDraftItemSelection().Contents);
player.resetDraftSelection();

DraftBag currentBag = player.getCurrentDraftBag().orElse(null);
if (currentBag == null) {
// This should never occur. The player just picked from their current bag; it could be empty, but not non-existent.
BotLogger.warning(new BotLogger.LogMessageOrigin(event, player), "Tried to pass a null bag");
} else {
MessageHelper.sendMessageToChannel(player.getCardsInfoThread(), "You are passing the following cards to your right:\n" + FrankenDraftBagService.getBagReceipt(currentBag));
}

FrankenDraftBagService.displayPlayerHand(game, player);

draft.passBag(player);

if (draft.isDraftStageComplete()) {
MessageHelper.sendMessageToChannel(game.getActionsChannel(), game.getPing() + " the draft stage of the FrankenDraft is complete. Please select your abilities from your drafted hands.");
FrankenDraftBagService.applyDraftBags(event, game);
return;
}
int passCounter = 0;
while (draft.allPlayersReadyToPass()) {
FrankenDraftBagService.passBags(game);
passCounter++;
if (passCounter > game.getRealPlayers().size()) {
MessageHelper.sendMessageToChannel(game.getActionsChannel(), game.getPing() + " an error has occurred where nobody is able to draft any cards, but there are cards still in the bag. Please notify @developer");
break;
}
}

return;
}
case "show_bag" -> {
Expand All @@ -180,21 +183,31 @@ public static void resolveFrankenDraftAction(Game game, Player player, ButtonInt
}
}
}
DraftBag currentBag = player.getCurrentDraftBag();

DraftBag currentBag = player.getCurrentDraftBag().orElse(null);

if (currentBag == null) {
MessageHelper.sendMessageToChannel(event.getMessageChannel(), "Something went wrong. You have no draft bag and are not allowed to draft right now. Please wait for a bag.");
return;
}

DraftItem selectedItem = DraftItem.generateFromAlias(action);

if (!selectedItem.isDraftable(player)) {
MessageHelper.sendMessageToChannel(event.getMessageChannel(), "Something went wrong. You are not allowed to draft " + selectedItem.getShortDescription() + " right now. Please select another item.");
return;
}
// TODO check what happens if a player attempts to draft an item not in the bag. This kinda looks like the item would just appear.
currentBag.Contents.removeIf((DraftItem bagItem) -> bagItem.getAlias().equals(action));
player.queueDraftItem(DraftItem.generateFromAlias(action));
player.selectDraftItem(DraftItem.generateFromAlias(action));

if (!draft.playerHasDraftableItemInBag(player) && !draft.playerHasItemInQueue(player)) {
draft.setPlayerReadyToPass(player, true);
}
// // TODO this shouldn't be reachable; queueDraftItem can't leave an empty item queue
// if (!draft.playerHasDraftableItemInBag(player) && !draft.playerHasItemInQueue(player)) {
// draft.setPlayerReadyToPass(player, true);
// }

FrankenDraftBagService.showPlayerBag(game, player);
// TODO this throws an error "Unknown message"
event.getMessage().delete().queue();
}
}
60 changes: 41 additions & 19 deletions src/main/java/ti4/commands/franken/FrankenEdit.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.Objects;

import org.jetbrains.annotations.Nullable;

import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
Expand All @@ -17,23 +19,25 @@
import ti4.service.franken.FrankenDraftBagService;

class FrankenEdit extends GameStateSubcommand {
private final String QUEUE_INDEX = "queue_index";

public FrankenEdit() {
super(Constants.FRANKEN_EDIT, "Frankendraft Edit Commands", true, true);
addOptions(new OptionData(OptionType.STRING, Constants.FRANKEN_EDIT_ACTION, "add/remove/swap/view")
.addChoice("Force Bag Swap", "forceSwap")
.addChoice("Force Bag Pass", "forceSwap")
.addChoice("Add Card To Bag", "addBag").addChoice("Remove Card From Bag", "removeBag")
.addChoice("Swap Cards In Bag", "swapBag").addChoice("View Bag", "viewBag")
.addChoice("Add Card To Hand", "addHand").addChoice("Remove Card From Hand", "removeHand")
.addChoice("Swap Cards In Hand", "swapHand").addChoice("View Hand", "viewHand")
.addChoice("Add Card To Queue", "addQueue").addChoice("Remove Card From Queue", "removeQueue")
.addChoice("Swap Cards In Queue", "swapQueue").addChoice("View Queue", "viewQueue")
.addChoice("Add Card To Selection", "addQueue").addChoice("Remove Card From Selection", "removeQueue")
.addChoice("Swap Cards In Selection", "swapQueue").addChoice("View Selection", "viewQueue")
.addChoice("View All", "viewAll")
.setRequired(true));
addOptions(new OptionData(OptionType.STRING, Constants.FRANKEN_ITEM + "1", "The card to edit"));
addOptions(new OptionData(OptionType.STRING, Constants.FRANKEN_ITEM + "2", "Use with 'swap'. The card to swap Arg1 with."));
addOptions(new OptionData(OptionType.USER, Constants.PLAYER, "Player @playername"));

addOptions(new OptionData(OptionType.INTEGER, QUEUE_INDEX, "The index in the player's queue of the bag to edit. Default 0, the current bag.")
.setMinValue(0));
}

@Override
Expand All @@ -42,43 +46,57 @@ public void execute(SlashCommandInteractionEvent event) {
OptionMapping editOption = event.getOption(Constants.FRANKEN_EDIT_ACTION);
OptionMapping card1 = event.getOption(Constants.FRANKEN_ITEM + "1");
OptionMapping card2 = event.getOption(Constants.FRANKEN_ITEM + "2");
// For some reason, getAsLong doesn't type check.
int queueIndex = event.getOption(QUEUE_INDEX, 0, OptionMapping::getAsInt);
String command = editOption.getAsString();

if ("viewAll".equals(command)) {
MessageHelper.sendMessageToUser("====================\n" + game.getName() +
" Frankendraft Status\n====================", event.getUser());
for (var player : game.getRealPlayers()) {
dmPlayerBag(game, player, player.getCurrentDraftBag(), "Held Bag", event.getUser());
int index = 0;
for (DraftBag bag : player.getDraftBagQueue()) {
String bagName = index == 0 ? "Held Bag" : "Bag " + index + " in queue";
dmPlayerBag(game, player, bag, bagName, event.getUser());
index++;
}
dmPlayerBag(game, player, player.getDraftHand(), "Hand", event.getUser());
dmPlayerBag(game, player, player.getDraftQueue(), "Queue", event.getUser());
dmPlayerBag(game, player, player.getDraftItemSelection(), "Selection", event.getUser());
}
return;
}

if ("forceSwap".equals(command)) {
FrankenDraftBagService.passBags(game);
return;
}

OptionMapping playerOption = event.getOption(Constants.PLAYER);
if (playerOption == null) {
MessageHelper.replyToMessage(event, "That command requires a player argument.");
return;
}
Player editingPlayer = game.getPlayer(playerOption.getAsUser().getId());

if ("forceSwap".equals(command)) {
game.getActiveBagDraft().passBag(editingPlayer);
return;
}

DraftBag editingBag = null;
String bagName = "";
if (command.contains("Bag")) {
editingBag = editingPlayer.getCurrentDraftBag();
bagName = "Held Bag";
// Hack for ArrayDeque not being a List
editingBag = editingPlayer.getDraftBagQueue().stream()
.skip(queueIndex)
.findFirst()
.orElse(null);
// TODO BAG_QUEUE fuller editing support might be wanted
// including creating bags, deleting bags,
// moving bags to arbitrary positions.
bagName = queueIndex == 0 ? "Held Bag" : "Queued bag " + queueIndex;
}
if (command.contains("Hand")) {
editingBag = editingPlayer.getDraftHand();
bagName = "Hand";
}
if (command.contains("Queue")) {
editingBag = editingPlayer.getDraftQueue();
editingBag = editingPlayer.getDraftItemSelection();
bagName = "Queue";
}

Expand All @@ -102,12 +120,16 @@ public void execute(SlashCommandInteractionEvent event) {
}
}

private void dmPlayerBag(Game game, Player player, DraftBag bag, String bagName, User user) {
private void dmPlayerBag(Game game, Player player, @Nullable DraftBag bag, String bagName, User user) {
StringBuilder sb = new StringBuilder();
sb.append(game.getName()).append(" ").append(player.getUserName()).append(" Current ").append(bagName).append(":\n");
for (DraftItem item : bag.Contents) {
sb.append(item.getAlias());
sb.append("\n");
if (bag == null) {
sb.append(game.getName()).append(" ").append(player.getUserName()).append(" No Current ").append(bagName).append("\n");
} else {
sb.append(game.getName()).append(" ").append(player.getUserName()).append(" Current ").append(bagName).append(":\n");
for (DraftItem item : bag.Contents) {
sb.append(item.getAlias());
sb.append("\n");
}
}
MessageHelper.sendMessageToUser(sb.toString(), user);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/ti4/commands/franken/StartFrankenDraft.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void execute(SlashCommandInteractionEvent event) {
}

FrankenDraftBagService.setUpFrankenFactions(game, event, force);
FrankenDraftBagService.clearPlayerHands(game);
FrankenDraftBagService.clearPlayerHandsAndQueues(game);

if (draftMode == null) {
game.setBagDraft(new FrankenDraft(game));
Expand Down
Loading