From 60871676735d25fe247cddb442630548ccf83257 Mon Sep 17 00:00:00 2001 From: Chris H Date: Tue, 21 May 2024 05:36:59 -0400 Subject: [PATCH] Draft Matters - How many cards you've drafted this round (#5203) --- .../src/main/java/forge/card/CardFace.java | 3 + .../src/main/java/forge/card/CardRules.java | 2 + .../java/forge/card/ICardRawAbilites.java | 1 + forge-core/src/main/java/forge/deck/Deck.java | 25 ++++- .../src/main/java/forge/deck/DeckGroup.java | 3 +- .../java/forge/deck/io/DeckFileHeader.java | 67 ++++++------ .../java/forge/deck/io/DeckSerializer.java | 19 +++- .../src/main/java/forge/game/Match.java | 1 + .../java/forge/game/ability/AbilityUtils.java | 17 +++ .../main/java/forge/game/player/Player.java | 10 ++ .../main/java/forge/gui/framework/EDocID.java | 12 +-- .../screens/deckeditor/CDeckEditorUI.java | 17 ++- .../controllers/CEditorDraftingProcess.java | 51 +++++++-- .../deckeditor/controllers/CEditorLog.java | 66 ++++++++++++ .../screens/deckeditor/views/VEditorLog.java | 102 ++++++++++++++++++ .../src/test/java/forge/BoosterDraftTest.java | 22 ++++ forge-gui/res/blockdata/printsheets.txt | 60 ----------- .../res/cardsfolder/c/custodi_peacekeeper.txt | 9 ++ forge-gui/res/cardsfolder/g/garbage_fire.txt | 8 ++ .../res/cardsfolder/l/lurking_automaton.txt | 9 ++ .../res/cardsfolder/p/pyretic_hunter.txt | 10 ++ forge-gui/res/draft/rankings/chk.rnk | 6 +- .../editions/Conspiracy Take the Crown.txt | 24 ++++- forge-gui/res/editions/Conspiracy.txt | 27 +++++ forge-gui/res/languages/en-US.properties | 1 + .../forge/gamemodes/limited/BoosterDraft.java | 42 +++++++- .../gamemodes/limited/IBoosterDraft.java | 4 + .../forge/gamemodes/limited/IDraftLog.java | 5 + .../gamemodes/limited/LimitedPlayer.java | 88 +++++++++++---- .../gamemodes/limited/LimitedPlayerAI.java | 4 +- 30 files changed, 548 insertions(+), 167 deletions(-) create mode 100644 forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLog.java create mode 100644 forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VEditorLog.java create mode 100644 forge-gui/res/cardsfolder/c/custodi_peacekeeper.txt create mode 100644 forge-gui/res/cardsfolder/g/garbage_fire.txt create mode 100644 forge-gui/res/cardsfolder/l/lurking_automaton.txt create mode 100644 forge-gui/res/cardsfolder/p/pyretic_hunter.txt create mode 100644 forge-gui/src/main/java/forge/gamemodes/limited/IDraftLog.java diff --git a/forge-core/src/main/java/forge/card/CardFace.java b/forge-core/src/main/java/forge/card/CardFace.java index d35ceb0670e..92b14951019 100644 --- a/forge-core/src/main/java/forge/card/CardFace.java +++ b/forge-core/src/main/java/forge/card/CardFace.java @@ -51,6 +51,7 @@ final class CardFace implements ICardFace, Cloneable { private List abilities = null; private List staticAbilities = null; private List triggers = null; + private List draftActions = null; private List replacements = null; private Map variables = null; @@ -74,6 +75,7 @@ final class CardFace implements ICardFace, Cloneable { @Override public Iterable getAbilities() { return abilities; } @Override public Iterable getStaticAbilities() { return staticAbilities; } @Override public Iterable getTriggers() { return triggers; } + @Override public Iterable getDraftActions() { return draftActions; } @Override public Iterable getReplacements() { return replacements; } @Override public String getNonAbilityText() { return nonAbilityText; } @Override public Iterable> getVariables() { return variables.entrySet(); } @@ -125,6 +127,7 @@ final class CardFace implements ICardFace, Cloneable { void addKeyword(String value) { if (null == this.keywords) { this.keywords = new ArrayList<>(); } this.keywords.add(value); } void addAbility(String value) { if (null == this.abilities) { this.abilities = new ArrayList<>(); } this.abilities.add(value);} void addTrigger(String value) { if (null == this.triggers) { this.triggers = new ArrayList<>(); } this.triggers.add(value);} + void addDraftAction(String value) { if (null == this.draftActions) { this.draftActions = new ArrayList<>(); } this.draftActions.add(value);} void addStaticAbility(String value) { if (null == this.staticAbilities) { this.staticAbilities = new ArrayList<>(); } this.staticAbilities.add(value);} void addReplacementEffect(String value) { if (null == this.replacements) { this.replacements = new ArrayList<>(); } this.replacements.add(value);} void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); } diff --git a/forge-core/src/main/java/forge/card/CardRules.java b/forge-core/src/main/java/forge/card/CardRules.java index 72a88f95450..71a36a2fe45 100644 --- a/forge-core/src/main/java/forge/card/CardRules.java +++ b/forge-core/src/main/java/forge/card/CardRules.java @@ -547,6 +547,8 @@ public final class CardRules implements ICardCharacteristics { has = new DeckHints(value); } else if ("Defense".equals(key)) { this.faces[this.curFace].setDefense(value); + } else if ("Draft".equals(key)) { + this.faces[this.curFace].addDraftAction(value); } break; diff --git a/forge-core/src/main/java/forge/card/ICardRawAbilites.java b/forge-core/src/main/java/forge/card/ICardRawAbilites.java index cc3c54ea5ee..07f68b19868 100644 --- a/forge-core/src/main/java/forge/card/ICardRawAbilites.java +++ b/forge-core/src/main/java/forge/card/ICardRawAbilites.java @@ -7,6 +7,7 @@ public interface ICardRawAbilites Iterable getKeywords(); Iterable getReplacements(); Iterable getTriggers(); + Iterable getDraftActions(); Iterable getStaticAbilities(); Iterable getAbilities(); diff --git a/forge-core/src/main/java/forge/deck/Deck.java b/forge-core/src/main/java/forge/deck/Deck.java index c0b3ec5aecc..7c8fa7ed594 100644 --- a/forge-core/src/main/java/forge/deck/Deck.java +++ b/forge-core/src/main/java/forge/deck/Deck.java @@ -49,6 +49,7 @@ public class Deck extends DeckBase implements Iterable aiHints = new TreeSet<>(); + private final Map draftNotes = new HashMap<>(); private Map> deferredSections = null; private Map> loadedSections = null; private String lastCardArtPreferenceUsed = ""; @@ -91,10 +92,6 @@ public class Deck extends DeckBase implements Iterable sections : other.parts.entrySet()) { - parts.put(sections.getKey(), new CardPool(sections.getValue())); - } - tags.addAll(other.getTags()); } @Override @@ -209,6 +206,8 @@ public class Deck extends DeckBase implements Iterable draftNotes) { + if (draftNotes == null) { + return; + } + + for(String key : draftNotes.keySet()) { + String notes = draftNotes.get(key); + if (notes == null || notes.isEmpty()) { + continue; + } + this.draftNotes.put(key, notes.trim()); + } + } + + public Map getDraftNotes() { + return draftNotes; + } + public UnplayableAICards getUnplayableAICards() { if (unplayableAI == null) { unplayableAI = new UnplayableAICards(this); diff --git a/forge-core/src/main/java/forge/deck/DeckGroup.java b/forge-core/src/main/java/forge/deck/DeckGroup.java index 9c436c22fb7..733aa29e097 100644 --- a/forge-core/src/main/java/forge/deck/DeckGroup.java +++ b/forge-core/src/main/java/forge/deck/DeckGroup.java @@ -26,7 +26,8 @@ import java.util.List; import com.google.common.base.Function; /** - * TODO: Write javadoc for this type. + * Related decks usually pertaining to a limited experience like draft or sealed + * This file represents a human player deck and all opposing AI decks * */ public class DeckGroup extends DeckBase { diff --git a/forge-core/src/main/java/forge/deck/io/DeckFileHeader.java b/forge-core/src/main/java/forge/deck/io/DeckFileHeader.java index 2d630ccb191..6e36e4efbaf 100644 --- a/forge-core/src/main/java/forge/deck/io/DeckFileHeader.java +++ b/forge-core/src/main/java/forge/deck/io/DeckFileHeader.java @@ -17,6 +17,7 @@ */ package forge.deck.io; +import java.util.HashMap; import java.util.Set; import java.util.TreeSet; @@ -37,9 +38,10 @@ public class DeckFileHeader { /** The Constant DECK_TYPE. */ public static final String DECK_TYPE = "Deck Type"; public static final String TAGS = "Tags"; - + public static final String TAGS_SEPARATOR = ","; - + public static final String DRAFT_NOTES = "DraftNotes"; + /** The Constant COMMENT. */ public static final String COMMENT = "Comment"; private static final String PLAYER = "Player"; @@ -54,30 +56,19 @@ public class DeckFileHeader { private final String comment; private final Set tags; + private final HashMap draftNotes; private final boolean intendedForAi; private final String aiHints; - /** - * @return the intendedForAi - */ public boolean isIntendedForAi() { return intendedForAi; } - /** - * @return the AI hints - */ public String getAiHints() { return aiHints; } - /** - * TODO: Write javadoc for Constructor. - * - * @param kvPairs - * the kv pairs - */ public DeckFileHeader(final FileSection kvPairs) { this.name = kvPairs.get(DeckFileHeader.NAME); this.comment = kvPairs.get(DeckFileHeader.COMMENT); @@ -85,6 +76,7 @@ public class DeckFileHeader { this.customPool = kvPairs.getBoolean(DeckFileHeader.CSTM_POOL); this.intendedForAi = "computer".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER)) || "ai".equalsIgnoreCase(kvPairs.get(DeckFileHeader.PLAYER_TYPE)); this.aiHints = kvPairs.get(DeckFileHeader.AI_HINTS); + this.tags = new TreeSet<>(); String rawTags = kvPairs.get(DeckFileHeader.TAGS); @@ -93,42 +85,42 @@ public class DeckFileHeader { if ( StringUtils.isNotBlank(t)) tags.add(t.trim()); } - - + this.draftNotes = new HashMap<>(); + extractDraftNotes(kvPairs.get(DeckFileHeader.DRAFT_NOTES)); + } + + private void extractDraftNotes(String rawNotes) { + if(StringUtils.isBlank(rawNotes) ) { + return; + } + + for(String t : rawNotes.split("\\|")) { + if (StringUtils.isBlank(t)) { + continue; + } + + String[] notes = t.trim().split(":", 2); + + if (notes[0].trim().isEmpty() || notes[1].trim().isEmpty()) { + continue; + } + + draftNotes.put(notes[0].trim(), notes[1].trim()); + } } - /** - * Checks if is custom pool. - * - * @return true, if is custom pool - */ public final boolean isCustomPool() { return this.customPool; } - /** - * Gets the name. - * - * @return the name - */ public final String getName() { return this.name; } - /** - * Gets the comment. - * - * @return the comment - */ public final String getComment() { return this.comment; } - /** - * Gets the deck type. - * - * @return the deck type - */ public final DeckFormat getDeckType() { return this.deckType; } @@ -137,4 +129,7 @@ public class DeckFileHeader { return tags; } + public final HashMap getDraftNotes() { + return draftNotes; + } } diff --git a/forge-core/src/main/java/forge/deck/io/DeckSerializer.java b/forge-core/src/main/java/forge/deck/io/DeckSerializer.java index 587c4efe81b..a067e53fb45 100644 --- a/forge-core/src/main/java/forge/deck/io/DeckSerializer.java +++ b/forge-core/src/main/java/forge/deck/io/DeckSerializer.java @@ -56,14 +56,30 @@ public class DeckSerializer { if (!d.getAiHints().isEmpty()) { out.add(TextUtil.concatNoSpace(DeckFileHeader.AI_HINTS, "=", StringUtils.join(d.getAiHints(), " | "))); } + if (!d.getDraftNotes().isEmpty()) { + String sb = serializeDraftNotes(d.getDraftNotes()); + out.add(TextUtil.concatNoSpace(DeckFileHeader.DRAFT_NOTES, "=", sb)); + } for(Entry s : d) { out.add(TextUtil.enclosedBracket(s.getKey().toString())); - out.add(s.getValue().toCardList(System.getProperty("line.separator"))); + out.add(s.getValue().toCardList(System.lineSeparator())); } return out; } + public static String serializeDraftNotes(final Map draftNotes) { + StringBuilder sb = new StringBuilder(); + for(String key : draftNotes.keySet()) { + if (sb.length() > 0) { + sb.append(" | "); + } + + sb.append(key).append(":").append(draftNotes.get(key)); + } + return sb.toString(); + } + public static Deck fromFile(final File deckFile) { return fromSections(FileSection.parseSections(FileUtil.readFile(deckFile))); } @@ -82,6 +98,7 @@ public class DeckSerializer { d.setComment(dh.getComment()); d.setAiHints(dh.getAiHints()); d.getTags().addAll(dh.getTags()); + d.setDraftNotes(dh.getDraftNotes()); d.setDeferredSections(sections); return d; } diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index 9bf2761d4ed..ec8b40930ea 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -288,6 +288,7 @@ public class Match { } Deck myDeck = psc.getDeck(); + player.setDraftNotes(myDeck.getDraftNotes()); Set myRemovedAnteCards = null; if (!rules.useAnte()) { diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 7deaf39dc8f..1dc538ead80 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -2358,6 +2358,23 @@ public class AbilityUtils { return doXMath(player.getNotedNumberForName(c.getName()), expr, c, ctb); } + if (sq[0].equals("DraftNotesHighest")) { + // Just in case you are playing this card in a deck without draft notes + String note = player.getDraftNotes().getOrDefault(sq[1], "0"); + int highest = 0; + for (String n : note.split(",")) { + int num = Integer.parseInt(n); + if (num > highest) { + highest = num; + } + } + + return doXMath(highest, expr, c, ctb); + // Other draft notes include: Names, Colors, Players, Creature Type. + // But these aren't really things you count so they'll show up in properties most likely + } + + //Count$TypesSharedWith [defined] if (sq[0].startsWith("TypesSharedWith")) { Set thisTypes = Sets.newHashSet(c.getType().getCoreTypes()); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index afaf16d2151..226e70d45ac 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -124,6 +124,7 @@ public class Player extends GameEntity implements Comparable { private final Map> notes = Maps.newHashMap(); private final Map notedNum = Maps.newHashMap(); + private final Map draftNotes = Maps.newHashMap(); private boolean revolt = false; private int descended = 0; @@ -2376,6 +2377,15 @@ public class Player extends GameEntity implements Comparable { return getName().compareTo(o.getName()); } + public void setDraftNotes(Map notes) { + this.draftNotes.clear(); + this.draftNotes.putAll(notes); + } + + public Map getDraftNotes() { + return draftNotes; + } + public static class Accessors { public static Function FN_GET_NAME = new Function() { @Override diff --git a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java index 203f30d3204..607b77a0b49 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java +++ b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java @@ -5,16 +5,7 @@ package forge.gui.framework; import com.google.common.collect.ObjectArrays; -import forge.screens.deckeditor.views.VAllDecks; -import forge.screens.deckeditor.views.VBrawlDecks; -import forge.screens.deckeditor.views.VCardCatalog; -import forge.screens.deckeditor.views.VCommanderDecks; -import forge.screens.deckeditor.views.VCurrentDeck; -import forge.screens.deckeditor.views.VDeckgen; -import forge.screens.deckeditor.views.VOathbreakerDecks; -import forge.screens.deckeditor.views.VProbabilities; -import forge.screens.deckeditor.views.VStatistics; -import forge.screens.deckeditor.views.VTinyLeadersDecks; +import forge.screens.deckeditor.views.*; import forge.screens.home.gauntlet.*; import forge.screens.home.online.VSubmenuOnlineLobby; import forge.screens.home.puzzle.VSubmenuPuzzleCreate; @@ -61,6 +52,7 @@ public enum EDocID { EDITOR_BRAWL (VBrawlDecks.SINGLETON_INSTANCE), EDITOR_TINY_LEADERS (VTinyLeadersDecks.SINGLETON_INSTANCE), EDITOR_OATHBREAKER (VOathbreakerDecks.SINGLETON_INSTANCE), + EDITOR_LOG(VEditorLog.SINGLETON_INSTANCE), WORKSHOP_CATALOG (VWorkshopCatalog.SINGLETON_INSTANCE), WORKSHOP_CARDDESIGNER (VCardDesigner.SINGLETON_INSTANCE), diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java index ab8ef94132e..ebeef61e46b 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/CDeckEditorUI.java @@ -42,13 +42,7 @@ import forge.screens.deckeditor.controllers.CEditorQuestCardShop; import forge.screens.deckeditor.controllers.CProbabilities; import forge.screens.deckeditor.controllers.CStatistics; import forge.screens.deckeditor.controllers.DeckController; -import forge.screens.deckeditor.views.VAllDecks; -import forge.screens.deckeditor.views.VBrawlDecks; -import forge.screens.deckeditor.views.VCardCatalog; -import forge.screens.deckeditor.views.VCommanderDecks; -import forge.screens.deckeditor.views.VCurrentDeck; -import forge.screens.deckeditor.views.VOathbreakerDecks; -import forge.screens.deckeditor.views.VTinyLeadersDecks; +import forge.screens.deckeditor.views.*; import forge.screens.match.controllers.CDetailPicture; import forge.util.ItemPool; @@ -72,6 +66,7 @@ public enum CDeckEditorUI implements ICDoc { private final VOathbreakerDecks vOathbreakerDecks; private final VBrawlDecks vBrawlDecks; private final VTinyLeadersDecks vTinyLeadersDecks; + private final VEditorLog vEditorLog; CDeckEditorUI() { screenChildControllers = new HashMap<>(); @@ -86,6 +81,7 @@ public enum CDeckEditorUI implements ICDoc { this.vBrawlDecks.setCDetailPicture(cDetailPicture); this.vTinyLeadersDecks = VTinyLeadersDecks.SINGLETON_INSTANCE; this.vTinyLeadersDecks.setCDetailPicture(cDetailPicture); + this.vEditorLog = VEditorLog.SINGLETON_INSTANCE; } public CDetailPicture getCDetailPicture() { @@ -230,9 +226,9 @@ public enum CDeckEditorUI implements ICDoc { } else if (KeyEvent.VK_LEFT == e.getKeyCode() || KeyEvent.VK_RIGHT == e.getKeyCode()) { if (e.isControlDown() || e.isMetaDown()) { - deckView.focus(); - e.consume(); //prevent losing selection -} + deckView.focus(); + e.consume(); //prevent losing selection + } } } }); @@ -308,6 +304,7 @@ public enum CDeckEditorUI implements ICDoc { public void register() { EDocID.CARD_PICTURE.setDoc(cDetailPicture.getCPicture().getView()); EDocID.CARD_DETAIL.setDoc(cDetailPicture.getCDetail().getView()); + EDocID.EDITOR_LOG.setDoc(vEditorLog); } /* (non-Javadoc) diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java index 9b49f7c9c9a..79cf6bac656 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorDraftingProcess.java @@ -27,6 +27,8 @@ import forge.deck.DeckSection; import forge.game.GameType; import forge.gamemodes.limited.BoosterDraft; import forge.gamemodes.limited.IBoosterDraft; +import forge.gamemodes.limited.IDraftLog; +import forge.gamemodes.limited.LimitedPlayer; import forge.gui.framework.DragCell; import forge.gui.framework.FScreen; import forge.item.PaperCard; @@ -34,13 +36,7 @@ import forge.itemmanager.CardManager; import forge.itemmanager.ItemManagerConfig; import forge.model.FModel; import forge.screens.deckeditor.CDeckEditorUI; -import forge.screens.deckeditor.views.VAllDecks; -import forge.screens.deckeditor.views.VBrawlDecks; -import forge.screens.deckeditor.views.VCommanderDecks; -import forge.screens.deckeditor.views.VCurrentDeck; -import forge.screens.deckeditor.views.VDeckgen; -import forge.screens.deckeditor.views.VOathbreakerDecks; -import forge.screens.deckeditor.views.VTinyLeadersDecks; +import forge.screens.deckeditor.views.*; import forge.screens.home.sanctioned.CSubmenuDraft; import forge.screens.match.controllers.CDetailPicture; import forge.toolbox.FOptionPane; @@ -55,7 +51,7 @@ import forge.util.Localizer; * @author Forge * @version $Id: CEditorDraftingProcess.java 24872 2014-02-17 07:35:47Z drdev $ */ -public class CEditorDraftingProcess extends ACEditorBase { +public class CEditorDraftingProcess extends ACEditorBase implements IDraftLog { private IBoosterDraft boosterDraft; private String ccAddLabel = Localizer.getInstance().getMessage("lblAddcard"); @@ -65,6 +61,7 @@ public class CEditorDraftingProcess extends ACEditorBase { private DragCell brawlDecksParent = null; private DragCell tinyLeadersDecksParent = null; private DragCell deckGenParent = null; + private DragCell draftLogParent = null; private boolean saved = false; private final Localizer localizer = Localizer.getInstance(); @@ -99,6 +96,13 @@ public class CEditorDraftingProcess extends ACEditorBase { */ public final void showGui(final IBoosterDraft inBoosterDraft) { this.boosterDraft = inBoosterDraft; + this.boosterDraft.setLogEntry(this); + + this.addLogEntry("Drafting process started."); + } + + public void addLogEntry(String message) { + CEditorLog.SINGLETON_INSTANCE.addLogEntry(message); } /* (non-Javadoc) @@ -110,6 +114,8 @@ public class CEditorDraftingProcess extends ACEditorBase { // can only draft one at a time, regardless of the requested quantity PaperCard card = items.iterator().next().getKey(); + + // Verify if card is in the activate pack? this.getDeckManager().addItem(card, 1); // get next booster pack @@ -212,16 +218,30 @@ public class CEditorDraftingProcess extends ACEditorBase { } while(s == null || s.isEmpty()); - saved = true; - // Construct computer's decks and save draft final Deck[] computer = this.boosterDraft.getDecks(); + final LimitedPlayer[] players = this.boosterDraft.getOpposingPlayers(); + for(int i = 0; i < computer.length; i++) { + Deck deck = computer[i]; + LimitedPlayer player = players[i]; + + deck.setDraftNotes(player.getSerializedDraftNotes()); + } + + // Assigned noted stuff to deck from LimitedPlayer final DeckGroup finishedDraft = new DeckGroup(s); - finishedDraft.setHumanDeck((Deck) this.getPlayersDeck().copyTo(s)); + final LimitedPlayer player = this.boosterDraft.getHumanPlayer(); + + Deck humanDeck = (Deck) this.getPlayersDeck().copyTo(s); + humanDeck.setDraftNotes(player.getSerializedDraftNotes()); + finishedDraft.setHumanDeck(humanDeck); finishedDraft.addAiDecks(computer); FModel.getDecks().getDraft().add(finishedDraft); + + saved = true; + CSubmenuDraft.SINGLETON_INSTANCE.update(); FScreen.DRAFTING_PROCESS.close(); @@ -267,6 +287,11 @@ public class CEditorDraftingProcess extends ACEditorBase { this.getCatalogManager().setup(ItemManagerConfig.DRAFT_PACK); this.getDeckManager().setup(ItemManagerConfig.DRAFT_POOL); + if (VEditorLog.SINGLETON_INSTANCE.getParentCell() == null) { + VCardCatalog.SINGLETON_INSTANCE.getParentCell().addDoc(VEditorLog.SINGLETON_INSTANCE); + VEditorLog.SINGLETON_INSTANCE.showView(); + } + ccAddLabel = this.getBtnAdd().getText(); if (this.getDeckManager().getPool() == null) { //avoid showing next choice or resetting pool if just switching back to Draft screen @@ -327,6 +352,7 @@ public class CEditorDraftingProcess extends ACEditorBase { this.getBtnRemove4().setVisible(true); VCurrentDeck.SINGLETON_INSTANCE.getPnlHeader().setVisible(true); + VEditorLog.SINGLETON_INSTANCE.getParentCell().setVisible(true); //Re-add tabs if (deckGenParent != null) { @@ -347,6 +373,9 @@ public class CEditorDraftingProcess extends ACEditorBase { if (tinyLeadersDecksParent != null) { tinyLeadersDecksParent.addDoc(VTinyLeadersDecks.SINGLETON_INSTANCE); } + if (draftLogParent != null) { + draftLogParent.addDoc(VEditorLog.SINGLETON_INSTANCE); + } // set catalog table back to free-selection mode getCatalogManager().setAllowMultipleSelections(true); diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLog.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLog.java new file mode 100644 index 00000000000..36a9dae8785 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorLog.java @@ -0,0 +1,66 @@ +package forge.screens.deckeditor.controllers; + +import forge.gui.FThreads; +import forge.gui.framework.ICDoc; +import forge.screens.deckeditor.views.VEditorLog; + +/** + * Controls the "editor log" panel in the deck editor UI. + * + *

(C at beginning of class name denotes a control class.) + * + */ +public enum CEditorLog implements ICDoc { + SINGLETON_INSTANCE; + + /** */ + CEditorDraftingProcess draftingProcess; + + private final VEditorLog view; + + CEditorLog() { + this.view = VEditorLog.SINGLETON_INSTANCE; + } + + //========== Overridden methods + + public final VEditorLog getView() { + return view; + } + + public final void addLogEntry(final String entry) { + view.addLogEntry(entry); + } + + @Override + public void register() { + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#initialize() + */ + @Override + public void initialize() { + FThreads.invokeInEdtNowOrLater(reset); + } + + private final Runnable reset = new Runnable() { + @Override + public void run() { + view.resetNewDraft(); + } + }; + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#update() + */ + @Override + public void update() { + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override + public void run() { + view.updateConsole(); + } + }); + } +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VEditorLog.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VEditorLog.java new file mode 100644 index 00000000000..e101a4ede38 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VEditorLog.java @@ -0,0 +1,102 @@ +package forge.screens.deckeditor.views; + +import com.google.common.collect.Lists; +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.gui.framework.IVDoc; +import forge.screens.deckeditor.controllers.CEditorLog; +import forge.screens.match.GameLogPanel; +import forge.toolbox.FScrollPane; +import forge.util.Localizer; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; +import java.util.List; + +/** + * Assembles Swing components of card catalog in deck editor. + * + *

(V at beginning of class name denotes a view class.) + * + */ +public enum VEditorLog implements IVDoc { + SINGLETON_INSTANCE; + + // Fields used with interface IVDoc + private DragCell parentCell; + final Localizer localizer = Localizer.getInstance(); + private final DragTab tab = new DragTab(localizer.getMessage("lblEditorLog")); + + private final GameLogPanel gameLog; + + private final JPanel pnlContent = new JPanel(new MigLayout("insets 0, gap 0, wrap")); + private final FScrollPane scroller = new FScrollPane(pnlContent, false); + + + private final List editorLogEntries = Lists.newArrayList(); + + VEditorLog() { + pnlContent.setOpaque(false); + scroller.getViewport().setBorder(null); + + this.gameLog = new GameLogPanel(); + } + + //========== Overridden from IVDoc + + @Override + public EDocID getDocumentID() { + return EDocID.EDITOR_LOG; + } + + @Override + public DragTab getTabLabel() { + return tab; + } + + public void showView() { + tab.setVisible(true); + tab.setOpaque(true); + pnlContent.setOpaque(true); + pnlContent.setVisible(true); + } + + @Override + public CEditorLog getLayoutControl() { + return CEditorLog.SINGLETON_INSTANCE; + } + + @Override + public void setParentCell(final DragCell cell0) { + this.parentCell = cell0; + } + + @Override + public DragCell getParentCell() { + return this.parentCell; + } + + @Override + public void populate() { + final JPanel parentBody = parentCell.getBody(); + parentBody.setLayout(new MigLayout("insets 5, gap 0, wrap, hidemode 3")); + // Add the panel that contains the log entries + parentBody.add(gameLog, "w 10:100%, h 100%"); + } + + public void resetNewDraft() { + // Should we store the draft? + gameLog.reset(); + editorLogEntries.clear(); + } + + public void updateConsole() { + gameLog.updateUI(); + } + + public void addLogEntry(String entry) { + gameLog.addLogEntry(entry); + this.editorLogEntries.add(entry); + } +} diff --git a/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java b/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java index 2001a56b16b..a719931e293 100644 --- a/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java +++ b/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java @@ -2,6 +2,8 @@ package forge; import java.util.List; +import forge.gamemodes.limited.IDraftLog; +import forge.gamemodes.limited.LimitedPlayer; import org.testng.annotations.Test; import forge.deck.CardPool; @@ -32,6 +34,16 @@ public class BoosterDraftTest implements IBoosterDraft { return null; } + @Override + public LimitedPlayer[] getOpposingPlayers() { + return new LimitedPlayer[0]; + } + + @Override + public LimitedPlayer getHumanPlayer() { + return null; + } + @Override public CardPool nextChoice() { this.n--; @@ -69,4 +81,14 @@ public class BoosterDraftTest implements IBoosterDraft { public boolean isPileDraft() { return false; } + + @Override + public void setLogEntry(IDraftLog draftingProcess) { + + } + + @Override + public IDraftLog getDraftLog() { + return null; + } } diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index 89ab4e8df45..c2bf1cd8d7c 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -635,33 +635,6 @@ 1 Wayfaring Temple|RTR 1 Wild Beastmaster|RTR -[CNS Draft Matters] -#1 Advantageous Proclamation|CNS -#1 AEther Searcher|CNS -#1 Agent of Acquisitions|CNS -#1 Backup Plan|CNS -1 Brago's Favor|CNS -#1 Canal Dredger|CNS -#1 Cogwork Grinder|CNS -#1 Cogwork Librarian|CNS -#1 Cogwork Spy|CNS -#1 Cogwork Tracker|CNS -#1 Deal Broker|CNS -#1 Double Stroke|CNS -1 Immediate Action|CNS -#1 Iterative Analysis|CNS -#1 Lore Seeker|CNS -#1 Lurking Automaton|CNS -1 Muzzio's Preparations|CNS -#1 Paliano, the High City|CNS -#1 Power Play|CNS -1 Secret Summoning|CNS -1 Secrets of Paradise|CNS -#1 Sentinel Dispatch|CNS -#1 Unexpected Potential|CNS -#1 Whispergear Sneak|CNS -#1 Worldknit|CNS - [M15 Sample Cards] 1 Aegis Angel|M15 1 Cancel|M15 @@ -965,21 +938,6 @@ Strip Mine|EXP Tectonic Edge|EXP Wasteland|EXP - -[CN2 Draft Matters] -Adriana's Valor -Assemble the Rank and Vile -Echoing Boon -#Emissary's Ploy -Hired Heist -Hold the Perimeter -Hymn of the Wilds -Incendiary Dissent -Natural Unity -#Sovereign's Realm -Summoner's Bond -Weight Advantage - [MPS Amonkhet Invocations] Austere Command|MPS_AKH Aven Mindcensor|MPS_AKH @@ -1483,24 +1441,6 @@ Kenrith's Transformation|ELD|2 Improbable Alliance|ELD|2 Inspiring Veteran|ELD|2 -[CN2 Not In Normal Slots] -Adriana's Valor -Assemble the Rank and Vile -Echoing Boon -#Emissary's Ploy -Hired Heist -Hold the Perimeter -Hymn of the Wilds -Incendiary Dissent -Natural Unity -#Sovereign's Realm -Summoner's Bond -Weight Advantage -Kaya, Ghost Assassin|CN2|2 - -[CN2 Foil Kaya] -Kaya, Ghost Assassin|CN2|2 - [JMP Above the Clouds 1] 1 Warden of Evos Isle|JMP 1 Inniaz, the Gale Force|JMP diff --git a/forge-gui/res/cardsfolder/c/custodi_peacekeeper.txt b/forge-gui/res/cardsfolder/c/custodi_peacekeeper.txt new file mode 100644 index 00000000000..20ca4ee7bf6 --- /dev/null +++ b/forge-gui/res/cardsfolder/c/custodi_peacekeeper.txt @@ -0,0 +1,9 @@ +Name:Custodi Peacekeeper +ManaCost:2 W +Types:Creature Human Cleric +PT:2/3 +Draft:Reveal CARDNAME as you draft it. +Draft:Note how many cards you've drafted this draft round, including CARDNAME. +A:AB$ Tap | Cost$ W T | ValidTgts$ Creature.powerLEX | TgtPrompt$ Tap target creature with power less than or equal to the highest number you noted for cards named Custodi Peacekeeper. | SpellDescription$ Tap target creature with power less than or equal to the highest number you noted for cards named Custodi Peacekeeper. +SVar:X:Count$DraftNotesHighest.Custodi Peacekeeper +Oracle:Reveal Custodi Peacekeeper as you draft it and note how many cards you’ve drafted this draft round, including Custodi Peacekeeper.\n{W}, {T}: Tap target creature with power less than or equal to the highest number you noted for cards named Custodi Peacekeeper. diff --git a/forge-gui/res/cardsfolder/g/garbage_fire.txt b/forge-gui/res/cardsfolder/g/garbage_fire.txt new file mode 100644 index 00000000000..a5d80b74e13 --- /dev/null +++ b/forge-gui/res/cardsfolder/g/garbage_fire.txt @@ -0,0 +1,8 @@ +Name:Garbage Fire +ManaCost:2 R +Types:Instant +Draft:Reveal CARDNAME as you draft it. +Draft:Note how many cards you've drafted this draft round, including CARDNAME. +A:SP$ DealDamage | ValidTgts$ Creature | NumDmg$ X | Description$ CARDNAME deals damage to target creature equal to the highest number you noted for cards named Garbage Fire. +SVar:X:Count$DraftNotesHighest.Garbage Fire +Oracle:Reveal Garbage Fire as you draft it and note how many cards you’ve drafted this draft round, including Garbage Fire.\nGarbage Fire deals damage to target creature equal to the highest number you noted for cards named Garbage Fire. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/l/lurking_automaton.txt b/forge-gui/res/cardsfolder/l/lurking_automaton.txt new file mode 100644 index 00000000000..9856ed3d729 --- /dev/null +++ b/forge-gui/res/cardsfolder/l/lurking_automaton.txt @@ -0,0 +1,9 @@ +Name:Lurking Automaton +ManaCost:5 +Types:Artifact Creature Construct +PT:0/0 +Draft:Reveal CARDNAME as you draft it. +Draft:Note how many cards you've drafted this draft round, including CARDNAME. +K:etbCounter:P1P1:X:no Condition:CARDNAME enters the battlefield with X +1/+1 counters on it, where X is the highest number you noted for cards named Lurking Automaton. +SVar:X:Count$DraftNotesHighest.Lurking Automaton +Oracle:Reveal Lurking Automaton as you draft it and note how many cards you’ve drafted this draft round, including Lurking Automaton.\nLurking Automaton enters the battlefield with X +1/+1 counters on it, where X is the highest number you noted for cards named Lurking Automaton. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/pyretic_hunter.txt b/forge-gui/res/cardsfolder/p/pyretic_hunter.txt new file mode 100644 index 00000000000..841bb902165 --- /dev/null +++ b/forge-gui/res/cardsfolder/p/pyretic_hunter.txt @@ -0,0 +1,10 @@ +Name:Pyretic Hunter +ManaCost:4 R +Types:Creature Elemental Cat +PT:0/0 +Draft:Reveal CARDNAME as you draft it. +Draft:Note how many cards you've drafted this draft round, including CARDNAME. +K:etbCounter:P1P1:X:no Condition:CARDNAME enters the battlefield with X +1/+1 counters on it, where X is the highest number you noted for cards named Pyretic Hunter. +K:Menace +SVar:X:Count$DraftNotesHighest.Pyretic Hunter +Oracle:Reveal Pyretic Hunter as you draft it and note how many cards you’ve drafted this draft round, including Pyretic Hunter.\nMenace (This creature can’t be blocked except by two or more creatures.)\nPyretic Hunter enters the battlefield with X +1/+1 counters on it, where X is the highest number you noted for cards named Pyretic Hunter. \ No newline at end of file diff --git a/forge-gui/res/draft/rankings/chk.rnk b/forge-gui/res/draft/rankings/chk.rnk index 082c0e8fc7a..2bef639bb4a 100644 --- a/forge-gui/res/draft/rankings/chk.rnk +++ b/forge-gui/res/draft/rankings/chk.rnk @@ -56,7 +56,7 @@ #55|He Who Hungers|R|CHK #56|Ghostly Prison|U|CHK #57|Samurai of the Pale Curtain|U|CHK -#58|Soratami Mirror Guard|C|CHK +#58|Soratami Mirror-Guard|C|CHK #59|Reciprocate|U|CHK #60|Sensei's Divining Top|U|CHK #61|Frostwielder|C|CHK @@ -108,7 +108,7 @@ #107|Orochi Sustainer|C|CHK #108|Blood Speaker|U|CHK #109|Brutal Deceiver|C|CHK -#110|Ember Fist Zubera|C|CHK +#110|Ember-Fist Zubera|C|CHK #111|Kami of Old Stone|U|CHK #112|Budoka Gardener|R|CHK #113|Indomitable Will|C|CHK @@ -186,7 +186,7 @@ #185|Distress|C|CHK #186|Myojin of Night's Reach|R|CHK #187|Eerie Procession|U|CHK -#188|Battle Mad Ronin|C|CHK +#188|Battle-Mad Ronin|C|CHK #189|Cranial Extraction|R|CHK #190|Orochi Leafcaller|C|CHK #191|Ethereal Haze|C|CHK diff --git a/forge-gui/res/editions/Conspiracy Take the Crown.txt b/forge-gui/res/editions/Conspiracy Take the Crown.txt index d3ee6263e15..27ef5f1b568 100644 --- a/forge-gui/res/editions/Conspiracy Take the Crown.txt +++ b/forge-gui/res/editions/Conspiracy Take the Crown.txt @@ -5,7 +5,7 @@ Name=Conspiracy: Take the Crown Code2=CN2 Type=Draft BoosterCovers=3 -Booster=10 Common:!fromSheet("CN2 Not In Normal Slots"), 3 Uncommon:!fromSheet("CN2 Not In Normal Slots"), 1 RareMythic:!fromSheet("CN2 Not In Normal Slots"), 1 fromSheet("CN2 Draft Matters") +Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"), 1 fromSheet("CN2 Draft Matters") AdditionalSheetForFoils=fromSheet("CN2 Foil Kaya") ScryfallCode=CN2 @@ -231,8 +231,30 @@ ScryfallCode=CN2 219 R Exotic Orchard @Steven Belledin 220 U Rogue's Passage @Christine Choi 221 U Shimmering Grotto @Cliff Childs + +[promo] 222 M Kaya, Ghost Assassin @Chris Rallis +[Draft Matters] +Adriana's Valor +Assemble the Rank and Vile +Custodi Peacekeeper +Echoing Boon +#Emissary's Ploy +Garbage Fire +Hired Heist +Hold the Perimeter +Hymn of the Wilds +Incendiary Dissent +Natural Unity +Pyretic Hunter +#Sovereign's Realm +Summoner's Bond +Weight Advantage + +[Foil Kaya] +Kaya, Ghost Assassin|CN2|2 + [tokens] w_1_1_soldier w_1_2_soldier_defender diff --git a/forge-gui/res/editions/Conspiracy.txt b/forge-gui/res/editions/Conspiracy.txt index b43ef5b47fa..4afb9352f24 100644 --- a/forge-gui/res/editions/Conspiracy.txt +++ b/forge-gui/res/editions/Conspiracy.txt @@ -220,6 +220,33 @@ ScryfallCode=CNS 209 U Quicksand @Matt Stewart 210 R Reflecting Pool @Fred Fields +[Draft Matters] +#1 Advantageous Proclamation|CNS +#1 AEther Searcher|CNS +#1 Agent of Acquisitions|CNS +#1 Backup Plan|CNS +1 Brago's Favor|CNS +#1 Canal Dredger|CNS +#1 Cogwork Grinder|CNS +#1 Cogwork Librarian|CNS +#1 Cogwork Spy|CNS +#1 Cogwork Tracker|CNS +#1 Deal Broker|CNS +#1 Double Stroke|CNS +1 Immediate Action|CNS +#1 Iterative Analysis|CNS +#1 Lore Seeker|CNS +1 Lurking Automaton|CNS +1 Muzzio's Preparations|CNS +#1 Paliano, the High City|CNS +#1 Power Play|CNS +1 Secret Summoning|CNS +1 Secrets of Paradise|CNS +#1 Sentinel Dispatch|CNS +#1 Unexpected Potential|CNS +#1 Whispergear Sneak|CNS +#1 Worldknit|CNS + [tokens] w_1_1_spirit_flying b_x_x_demon_flying diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 1553a332cb1..b6239712def 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -870,6 +870,7 @@ ttRemove4ofcard=Remove up to 4 of selected card to current deck lblAddBasicLands=Add Basic Lands ttAddBasicLands=Add basic lands to the deck lblCardCatalog=Card Catalog +lblEditorLog=Editor Log lblJumptoprevioustable=Jump to previous table lblJumptopnexttable=Jump to next table lblJumptotextfilter=Jump to text filter diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java index 4a532879d11..d36b55b2713 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java @@ -62,7 +62,9 @@ public class BoosterDraft implements IBoosterDraft { private static final int N_PLAYERS = 8; public static final String FILE_EXT = ".draft"; private final List players = new ArrayList<>(); - private LimitedPlayer localPlayer; + private final LimitedPlayer localPlayer; + + private IDraftLog draftLog = null; private String doublePickDuringDraft = ""; // "FirstPick" or "Always" protected int nextBoosterGroup = 0; @@ -264,12 +266,16 @@ public class BoosterDraft implements IBoosterDraft { } protected BoosterDraft(final LimitedPoolType draftType) { + this(draftType, N_PLAYERS); + } + + protected BoosterDraft(final LimitedPoolType draftType, int numPlayers) { this.draftFormat = draftType; - localPlayer = new LimitedPlayer(0); + localPlayer = new LimitedPlayer(0, this); players.add(localPlayer); - for (int i = 1; i < N_PLAYERS; i++) { - players.add(new LimitedPlayerAI(i)); + for (int i = 1; i < numPlayers; i++) { + players.add(new LimitedPlayerAI(i, this)); } } @@ -278,6 +284,16 @@ public class BoosterDraft implements IBoosterDraft { return false; } + @Override + public void setLogEntry(IDraftLog draftingProcess) { + draftLog = draftingProcess; + } + + @Override + public IDraftLog getDraftLog() { + return draftLog; + } + private void setupCustomDraft(final CustomLimited draft) { final ItemPool dPool = draft.getCardPool(); if (dPool == null) { @@ -374,6 +390,9 @@ public class BoosterDraft implements IBoosterDraft { for (LimitedPlayer pl : this.players) { pl.newPack(); } + if (this.getDraftLog() != null) { + this.getDraftLog().addLogEntry("Round " + this.nextBoosterGroup + " is starting..."); + } this.currentBoosterSize = firstPlayer.packQueue.peek().size(); return true; } @@ -387,6 +406,16 @@ public class BoosterDraft implements IBoosterDraft { return decks; } + @Override + public LimitedPlayer[] getOpposingPlayers() { + return this.players.toArray(new LimitedPlayer[7]); + } + + @Override + public LimitedPlayer getHumanPlayer() { + return this.localPlayer; + } + public void passPacks() { // Alternate direction of pack passing int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1; @@ -404,7 +433,10 @@ public class BoosterDraft implements IBoosterDraft { continue; if (!passingPack.isEmpty()) { - // TODO Canal Dredger for passing a pack with a single card in it + if (passingPack.size() == 1) { + // TODO Canal Dredger for passing a pack with a single card in it + + } int passTo = (i + adjust + N_PLAYERS) % N_PLAYERS; this.players.get(passTo).receiveOpenedPack(passingPack); diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/IBoosterDraft.java b/forge-gui/src/main/java/forge/gamemodes/limited/IBoosterDraft.java index d8d1514a636..43b1a0ee70a 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/IBoosterDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/IBoosterDraft.java @@ -37,9 +37,13 @@ public interface IBoosterDraft { boolean hasNextChoice(); boolean isRoundOver(); Deck[] getDecks(); // size 7, all the computers decks + LimitedPlayer[] getOpposingPlayers(); // size 7, all the computers + LimitedPlayer getHumanPlayer(); CardEdition[] LAND_SET_CODE = { null }; String[] CUSTOM_RANKINGS_FILE = { null }; boolean isPileDraft(); + void setLogEntry(IDraftLog draftingProcess); + IDraftLog getDraftLog(); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/IDraftLog.java b/forge-gui/src/main/java/forge/gamemodes/limited/IDraftLog.java new file mode 100644 index 00000000000..db028dd4c87 --- /dev/null +++ b/forge-gui/src/main/java/forge/gamemodes/limited/IDraftLog.java @@ -0,0 +1,5 @@ +package forge.gamemodes.limited; + +public interface IDraftLog { + public void addLogEntry(String message); +} diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java index 30ca03724d7..0c0d7cbd627 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java @@ -1,14 +1,14 @@ package forge.gamemodes.limited; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -//import com.google.common.collect.Lists; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; import forge.item.PaperCard; +import forge.util.TextUtil; + +import java.util.*; //import forge.gamemodes.limited.powers.DraftPower; public class LimitedPlayer { @@ -21,31 +21,43 @@ public class LimitedPlayer { protected Queue> packQueue; protected Queue> unopenedPacks; - // WIP - Draft Matters cards - /* - private static int CantDraftThisRound = 1, - SpyNextCardDrafted = 1 << 1, - ReceiveLastCard = 1 << 2, - CanRemoveAfterDraft = 1 << 3, - CanTradeAfterDraft = 1 << 4; + private static final int CantDraftThisRound = 1; + private static final int SpyNextCardDrafted = 1 << 1; + private static final int ReceiveLastCard = 1 << 2; + private static final int CanRemoveAfterDraft = 1 << 3; + private static final int CanTradeAfterDraft = 1 << 4; - private static int MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted + private static final int MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted | CanTradeAfterDraft; + private final int playerFlags = 0; - private int playerFlags = 0; + private final List faceUp = Lists.newArrayList(); + private final List revealed = Lists.newArrayList(); + private final Map> noted = new HashMap<>(); + //private Map powers = new HashMap<>(); - private List revealed = Lists.newArrayList(); - private Map> noted = new HashMap<>(); - private Map powers = new HashMap<>(); - */ + IBoosterDraft draft = null; - public LimitedPlayer(int seatingOrder) { + public LimitedPlayer(int seatingOrder, IBoosterDraft draft) { order = seatingOrder; deck = new Deck(); packQueue = new LinkedList<>(); unopenedPacks = new LinkedList<>(); + this.draft = draft; + } + + public Map> getDraftNotes() { + return noted; + } + + public Map getSerializedDraftNotes() { + Map serialized = new HashMap<>(); + for (Map.Entry> entry : noted.entrySet()) { + serialized.put(entry.getKey(), TextUtil.join(entry.getValue(), ",")); + } + return serialized; } public PaperCard chooseCard() { @@ -72,16 +84,38 @@ public class LimitedPlayer { pool.add(bestPick); draftedThisRound++; - // TODO Note Lurking Automaton + if (bestPick.getRules().getMainPart().getDraftActions() == null) { + return true; + } + + // Draft Actions + Iterable draftActions = bestPick.getRules().getMainPart().getDraftActions(); + if (Iterables.contains(draftActions, "Reveal CARDNAME as you draft it.")) { + revealed.add(bestPick); + + if (Iterables.contains(draftActions, "Note how many cards you've drafted this draft round, including CARDNAME.")) { + List note = noted.computeIfAbsent(bestPick.getName(), k -> Lists.newArrayList()); + note.add(String.valueOf(draftedThisRound)); + + addLog(name() + " revealed " + bestPick.getName() + " and noted " + draftedThisRound + " cards drafted this round."); + } else { + addLog(name() + " revealed " + bestPick.getName() + " as they drafted it."); + } + } + + // Colors // TODO Note Paliano, the High City - // TODO Note Aether Searcher - // TODO Note Custodi Peacepeeper + // TODO Note Regicide // TODO Note Paliano Vanguard - // TODO Note Garbage Fire + // TODO Note Aether Searcher (for the next card) return true; } + public void addLog(String message) { + this.draft.getDraftLog().addLogEntry(message); + } + public List nextChoice() { return packQueue.peek(); } @@ -107,6 +141,14 @@ public class LimitedPlayer { packQueue.add(pack); } + public String name() { + if (this instanceof LimitedPlayerAI) { + return "Player[" + order + "]"; + } + + return "You"; + } + /* public void addSingleBoosterPack(boolean random) { // TODO Lore Seeker diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java index 51f8bc0355f..a0ad94c8fed 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java @@ -12,8 +12,8 @@ import forge.localinstance.properties.ForgePreferences; public class LimitedPlayerAI extends LimitedPlayer { protected DeckColors deckCols; - public LimitedPlayerAI(int seatingOrder) { - super(seatingOrder); + public LimitedPlayerAI(int seatingOrder, BoosterDraft draft) { + super(seatingOrder, draft); deckCols = new DeckColors(); }