mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Add Spire Phantasm, Agent of Acquisitions, Cogwork Librarian, Leovold's Operative (#5437)
* Next few Draft cards * Code review fixes * Add Spire Phantasm * Add Agent of Acquisitions, Cogwork Librarian, Leovold's Operative * Some small fixes * Getting closer * Now the UI is updating * Switch DraftPack to a delegate class * Fix agent of acquisitions when AI drafts it
This commit is contained in:
@@ -174,6 +174,21 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
|||||||
return cp != null && !cp.isEmpty();
|
return cp != null && !cp.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PaperCard removeCardName(String name) {
|
||||||
|
PaperCard paperCard;
|
||||||
|
for (Entry<DeckSection, CardPool> kv : parts.entrySet()) {
|
||||||
|
CardPool pool = kv.getValue();
|
||||||
|
for (Entry<PaperCard, Integer> pc : pool) {
|
||||||
|
if (pc.getKey().getName().equalsIgnoreCase(name)) {
|
||||||
|
paperCard = pc.getKey();
|
||||||
|
pool.remove(paperCard);
|
||||||
|
return paperCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// will return new if it was absent
|
// will return new if it was absent
|
||||||
public CardPool getOrCreate(DeckSection deckSection) {
|
public CardPool getOrCreate(DeckSection deckSection) {
|
||||||
CardPool p = get(deckSection);
|
CardPool p = get(deckSection);
|
||||||
|
|||||||
@@ -2129,6 +2129,9 @@ public class CardProperty {
|
|||||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||||
|
|
||||||
return nameList.contains(card.getName());
|
return nameList.contains(card.getName());
|
||||||
|
} else if (property.equals("NotedGuessPhantasm")) {
|
||||||
|
String names = sourceController.getDraftNotes().get("Spire Phantasm");
|
||||||
|
return names != null && !names.isEmpty();
|
||||||
} else if (property.equals("NotedTypes")) {
|
} else if (property.equals("NotedTypes")) {
|
||||||
// Should Paliano Vanguard be hardcoded here or part of the property?
|
// Should Paliano Vanguard be hardcoded here or part of the property?
|
||||||
String types = sourceController.getDraftNotes().get("Paliano Vanguard");
|
String types = sourceController.getDraftNotes().get("Paliano Vanguard");
|
||||||
|
|||||||
@@ -116,12 +116,21 @@ public class CEditorDraftingProcess extends ACEditorBase<PaperCard, DeckGroup> i
|
|||||||
// can only draft one at a time, regardless of the requested quantity
|
// can only draft one at a time, regardless of the requested quantity
|
||||||
PaperCard card = items.iterator().next().getKey();
|
PaperCard card = items.iterator().next().getKey();
|
||||||
|
|
||||||
|
if (boosterDraft.getHumanPlayer().shouldSkipThisPick()) {
|
||||||
|
System.out.println(card + " not drafted because we're skipping this pick");
|
||||||
|
showPackToDraft();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify if card is in the activate pack?
|
// Verify if card is in the activate pack?
|
||||||
this.getDeckManager().addItem(card, 1);
|
this.getDeckManager().addItem(card, 1);
|
||||||
|
|
||||||
// get next booster pack
|
// get next booster pack if we aren't picking again from this pack
|
||||||
this.boosterDraft.setChoice(card);
|
this.boosterDraft.setChoice(card);
|
||||||
|
showPackToDraft();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showPackToDraft() {
|
||||||
boolean nextChoice = this.boosterDraft.hasNextChoice();
|
boolean nextChoice = this.boosterDraft.hasNextChoice();
|
||||||
ItemPool<PaperCard> pool = null;
|
ItemPool<PaperCard> pool = null;
|
||||||
if (nextChoice) {
|
if (nextChoice) {
|
||||||
|
|||||||
@@ -58,10 +58,15 @@ public class BoosterDraftTest implements IBoosterDraft {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setChoice(final PaperCard c) {
|
public boolean setChoice(final PaperCard c) {
|
||||||
System.out.println(c.getName());
|
System.out.println(c.getName());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
7
forge-gui/res/cardsfolder/a/agent_of_acquisitions.txt
Normal file
7
forge-gui/res/cardsfolder/a/agent_of_acquisitions.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Agent of Acquisitions
|
||||||
|
ManaCost:2
|
||||||
|
Types:Artifact Creature Construct
|
||||||
|
PT:2/1
|
||||||
|
Draft:Draft CARDNAME face up.
|
||||||
|
Draft:Instead of drafting a card from a booster pack, you may draft each card in that booster pack, one at a time. If you do, turn CARDNAME face down and you can’t draft cards for the rest of this draft round. (You may look at booster packs passed to you.)
|
||||||
|
Oracle:Draft Agent of Acquisitions face up.\nInstead of drafting a card from a booster pack, you may draft each card in that booster pack, one at a time. If you do, turn Agent of Acquisitions face down and you can’t draft cards for the rest of this draft round. (You may look at booster packs passed to you.)
|
||||||
7
forge-gui/res/cardsfolder/c/cogwork_librarian.txt
Normal file
7
forge-gui/res/cardsfolder/c/cogwork_librarian.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Cogwork Librarian
|
||||||
|
ManaCost:4
|
||||||
|
Types:Artifact Creature Construct
|
||||||
|
PT:3/3
|
||||||
|
Draft:Draft CARDNAME face up.
|
||||||
|
Draft:As you draft a card, you may draft an additional card from that booster pack. If you do, put CARDNAME into that booster pack.
|
||||||
|
Oracle:Draft Cogwork Librarian face up.\nAs you draft a card, you may draft an additional card from that booster pack. If you do, put Cogwork Librarian into that booster pack.
|
||||||
7
forge-gui/res/cardsfolder/l/leovolds_operative.txt
Normal file
7
forge-gui/res/cardsfolder/l/leovolds_operative.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Leovold's Operative
|
||||||
|
ManaCost:2 G
|
||||||
|
Types:Creature Elf Rogue
|
||||||
|
PT:3/2
|
||||||
|
Draft:Draft CARDNAME face up.
|
||||||
|
Draft:As you draft a card, you may draft an additional card from that booster pack. If you do, turn CARDNAME face down, then pass the next booster pack without drafting a card from it. (You may look at that booster pack.)
|
||||||
|
Oracle:Draft Leovold’s Operative face up.\n\nAs you draft a card, you may draft an additional card from that booster pack. If you do, turn Leovold’s Operative face down, then pass the next booster pack without drafting a card from it. (You may look at that booster pack.)
|
||||||
10
forge-gui/res/cardsfolder/s/spire_phantasm.txt
Normal file
10
forge-gui/res/cardsfolder/s/spire_phantasm.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Spire Phantasm
|
||||||
|
ManaCost:2 U U
|
||||||
|
Types:Creature Gargoyle Illusion
|
||||||
|
PT:3/2
|
||||||
|
Draft:Reveal CARDNAME as you draft it.
|
||||||
|
Draft:The next time a player drafts a card from this booster pack, guess that card’s name. Then that player reveals the drafted card.
|
||||||
|
K:Flying
|
||||||
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+NotedGuessPhantasm | Execute$ DrawACard | TriggerDescription$ When CARDNAME enters the battlefield, if you guessed correctly for a card named Spire Phantasm, draw a card.
|
||||||
|
SVar:DrawACard:DB$ Draw | Amount$ 1
|
||||||
|
Oracle:Reveal Spire Phantasm as you draft it. The next time a player drafts a card from this booster pack, guess that card’s name. Then that player reveals the drafted card.\nFlying\nWhen Spire Phantasm enters the battlefield, if you guessed correctly for a card named Spire Phantasm, draw a card.
|
||||||
@@ -248,6 +248,7 @@ Hold the Perimeter
|
|||||||
Hymn of the Wilds
|
Hymn of the Wilds
|
||||||
Illusionary Informant
|
Illusionary Informant
|
||||||
Incendiary Dissent
|
Incendiary Dissent
|
||||||
|
Leovold's Operative
|
||||||
Natural Unity
|
Natural Unity
|
||||||
Noble Banneret
|
Noble Banneret
|
||||||
Paliano Vanguard
|
Paliano Vanguard
|
||||||
|
|||||||
@@ -221,31 +221,31 @@ ScryfallCode=CNS
|
|||||||
210 R Reflecting Pool @Fred Fields
|
210 R Reflecting Pool @Fred Fields
|
||||||
|
|
||||||
[Draft Matters]
|
[Draft Matters]
|
||||||
#1 Advantageous Proclamation|CNS
|
1 Advantageous Proclamation|CNS
|
||||||
1 Aether Searcher|CNS
|
1 Aether Searcher|CNS
|
||||||
#1 Agent of Acquisitions|CNS
|
1 Agent of Acquisitions|CNS
|
||||||
#1 Backup Plan|CNS
|
#1 Backup Plan|CNS
|
||||||
1 Brago's Favor|CNS
|
1 Brago's Favor|CNS
|
||||||
#1 Canal Dredger|CNS
|
#1 Canal Dredger|CNS
|
||||||
1 Cogwork Grinder|CNS
|
1 Cogwork Grinder|CNS
|
||||||
#1 Cogwork Librarian|CNS
|
1 Cogwork Librarian|CNS
|
||||||
1 Cogwork Spy|CNS
|
1 Cogwork Spy|CNS
|
||||||
1 Cogwork Tracker|CNS
|
1 Cogwork Tracker|CNS
|
||||||
#1 Deal Broker|CNS
|
#1 Deal Broker|CNS
|
||||||
#1 Double Stroke|CNS
|
1 Double Stroke|CNS
|
||||||
1 Immediate Action|CNS
|
1 Immediate Action|CNS
|
||||||
#1 Iterative Analysis|CNS
|
1 Iterative Analysis|CNS
|
||||||
#1 Lore Seeker|CNS
|
#1 Lore Seeker|CNS
|
||||||
1 Lurking Automaton|CNS
|
1 Lurking Automaton|CNS
|
||||||
1 Muzzio's Preparations|CNS
|
1 Muzzio's Preparations|CNS
|
||||||
1 Paliano, the High City|CNS
|
1 Paliano, the High City|CNS
|
||||||
#1 Power Play|CNS
|
1 Power Play|CNS
|
||||||
1 Secret Summoning|CNS
|
1 Secret Summoning|CNS
|
||||||
1 Secrets of Paradise|CNS
|
1 Secrets of Paradise|CNS
|
||||||
#1 Sentinel Dispatch|CNS
|
1 Sentinel Dispatch|CNS
|
||||||
#1 Unexpected Potential|CNS
|
1 Unexpected Potential|CNS
|
||||||
1 Whispergear Sneak|CNS
|
1 Whispergear Sneak|CNS
|
||||||
#1 Worldknit|CNS
|
1 Worldknit|CNS
|
||||||
|
|
||||||
[tokens]
|
[tokens]
|
||||||
w_1_1_spirit_flying
|
w_1_1_spirit_flying
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class BoosterDraft implements IBoosterDraft {
|
public class BoosterDraft implements IBoosterDraft {
|
||||||
|
|
||||||
|
private static int nextId = 0;
|
||||||
private static final int N_PLAYERS = 8;
|
private static final int N_PLAYERS = 8;
|
||||||
public static final String FILE_EXT = ".draft";
|
public static final String FILE_EXT = ".draft";
|
||||||
private final List<LimitedPlayer> players = new ArrayList<>();
|
private final List<LimitedPlayer> players = new ArrayList<>();
|
||||||
@@ -371,9 +372,11 @@ public class BoosterDraft implements IBoosterDraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void initializeBoosters() {
|
public void initializeBoosters() {
|
||||||
|
|
||||||
for (Supplier<List<PaperCard>> boosterRound : this.product) {
|
for (Supplier<List<PaperCard>> boosterRound : this.product) {
|
||||||
for (int i = 0; i < N_PLAYERS; i++) {
|
for (int i = 0; i < N_PLAYERS; i++) {
|
||||||
this.players.get(i).receiveUnopenedPack(boosterRound.get());
|
DraftPack pack = new DraftPack(boosterRound.get(), nextId++);
|
||||||
|
this.players.get(i).receiveUnopenedPack(pack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
startRound();
|
startRound();
|
||||||
@@ -437,8 +440,10 @@ public class BoosterDraft implements IBoosterDraft {
|
|||||||
adjust = 0;
|
adjust = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do any players have a Canal Dredger?
|
||||||
|
|
||||||
for (int i = 0; i < N_PLAYERS; i++) {
|
for (int i = 0; i < N_PLAYERS; i++) {
|
||||||
List<PaperCard> passingPack = this.players.get(i).passPack();
|
DraftPack passingPack = this.players.get(i).passPack();
|
||||||
|
|
||||||
if (passingPack == null)
|
if (passingPack == null)
|
||||||
continue;
|
continue;
|
||||||
@@ -463,7 +468,17 @@ public class BoosterDraft implements IBoosterDraft {
|
|||||||
for (int i = 1; i < N_PLAYERS; i++) {
|
for (int i = 1; i < N_PLAYERS; i++) {
|
||||||
LimitedPlayer pl = this.players.get(i);
|
LimitedPlayer pl = this.players.get(i);
|
||||||
// TODO Agent of Acquisitions activation to loop the entire pack?
|
// TODO Agent of Acquisitions activation to loop the entire pack?
|
||||||
pl.draftCard(pl.chooseCard());
|
|
||||||
|
if (pl.shouldSkipThisPick()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computer player has an empty pack or is passing the pack
|
||||||
|
Boolean passPack;
|
||||||
|
do {
|
||||||
|
// THe player holding onto the pack to draft an extra card... Do it now.
|
||||||
|
passPack = pl.draftCard(pl.chooseCard());
|
||||||
|
} while (passPack != null && !passPack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,6 +488,7 @@ public class BoosterDraft implements IBoosterDraft {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRoundOver() {
|
public boolean isRoundOver() {
|
||||||
|
// Really should check if all packs are empty, but this is a good enough approximation
|
||||||
return packsInDraft == 0;
|
return packsInDraft == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,10 +499,12 @@ public class BoosterDraft implements IBoosterDraft {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setChoice(final PaperCard c) {
|
public boolean setChoice(final PaperCard c) {
|
||||||
final List<PaperCard> thisBooster = this.localPlayer.nextChoice();
|
final DraftPack thisBooster = this.localPlayer.nextChoice();
|
||||||
|
|
||||||
if (!thisBooster.contains(c)) {
|
if (!thisBooster.contains(c)) {
|
||||||
throw new RuntimeException("BoosterDraft : setChoice() error - card not found - " + c
|
throw new RuntimeException("BoosterDraft : setChoice() error - card not found - " + c
|
||||||
@@ -495,12 +513,17 @@ public class BoosterDraft implements IBoosterDraft {
|
|||||||
|
|
||||||
recordDraftPick(thisBooster, c);
|
recordDraftPick(thisBooster, c);
|
||||||
|
|
||||||
// TODO Agent of Acquisitions activation to loop the entire pack?
|
boolean passPack = this.localPlayer.draftCard(c);
|
||||||
|
if (passPack) {
|
||||||
this.localPlayer.draftCard(c);
|
// Leovolds Operative and Cogwork Librarian get to draft an extra card.. How do we do that?
|
||||||
this.currentBoosterPick++;
|
|
||||||
this.passPacks();
|
this.passPacks();
|
||||||
}
|
}
|
||||||
|
this.currentBoosterPick++;
|
||||||
|
|
||||||
|
// Return whether or not we passed, but that the UI always needs to refresh
|
||||||
|
// But returning might be useful for testing or other things?
|
||||||
|
return passPack;
|
||||||
|
}
|
||||||
|
|
||||||
private static String choosePackByPack(final List<String> setz, int packs) {
|
private static String choosePackByPack(final List<String> setz, int packs) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package forge.gamemodes.limited;
|
|||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.DeckHints;
|
import forge.card.DeckHints;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
@@ -114,6 +113,22 @@ public class CardRanker {
|
|||||||
return cardScores;
|
return cardScores;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<PaperCard> getOrderedRawScores(List<PaperCard> cards) {
|
||||||
|
List<Pair<Double, PaperCard>> cardScores = Lists.newArrayList();
|
||||||
|
for(PaperCard card : cards) {
|
||||||
|
cardScores.add(Pair.of(getRawScore(card), card));
|
||||||
|
}
|
||||||
|
|
||||||
|
cardScores.sort(Collections.reverseOrder(new CardRankingComparator()));
|
||||||
|
|
||||||
|
List<PaperCard> rankedCards = new ArrayList<>(cardScores.size());
|
||||||
|
for (Pair<Double, PaperCard> pair : cardScores) {
|
||||||
|
rankedCards.add(pair.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return rankedCards;
|
||||||
|
}
|
||||||
|
|
||||||
public static double getRawScore(PaperCard card) {
|
public static double getRawScore(PaperCard card) {
|
||||||
Double rawScore;
|
Double rawScore;
|
||||||
if (MagicColor.Constant.BASIC_LANDS.contains(card.getName())) {
|
if (MagicColor.Constant.BASIC_LANDS.contains(card.getName())) {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package forge.gamemodes.limited;
|
||||||
|
|
||||||
|
import com.google.common.collect.ForwardingList;
|
||||||
|
import forge.item.PaperCard;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DraftPack extends ForwardingList<PaperCard> {
|
||||||
|
private final List<PaperCard> cards;
|
||||||
|
private final int id;
|
||||||
|
private LimitedPlayer passedFrom;
|
||||||
|
private Map.Entry<LimitedPlayer, PaperCard> awaitingGuess;
|
||||||
|
|
||||||
|
public DraftPack(List<PaperCard> cards, int id) {
|
||||||
|
this.cards = cards;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LimitedPlayer getPassedFrom() {
|
||||||
|
return passedFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassedFrom(LimitedPlayer passedFrom) {
|
||||||
|
this.passedFrom = passedFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAwaitingGuess(LimitedPlayer player, PaperCard card) {
|
||||||
|
this.awaitingGuess = new AbstractMap.SimpleEntry<>(player, card);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map.Entry<LimitedPlayer, PaperCard> getAwaitingGuess() {
|
||||||
|
return awaitingGuess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetAwaitingGuess() {
|
||||||
|
this.awaitingGuess = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<PaperCard> delegate() {
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ public interface IBoosterDraft {
|
|||||||
|
|
||||||
int getRound();
|
int getRound();
|
||||||
CardPool nextChoice();
|
CardPool nextChoice();
|
||||||
void setChoice(PaperCard c);
|
boolean setChoice(PaperCard c);
|
||||||
boolean hasNextChoice();
|
boolean hasNextChoice();
|
||||||
boolean isRoundOver();
|
boolean isRoundOver();
|
||||||
Deck[] getDecks(); // size 7, all the computers decks
|
Deck[] getDecks(); // size 7, all the computers decks
|
||||||
@@ -49,4 +49,6 @@ public interface IBoosterDraft {
|
|||||||
IDraftLog getDraftLog();
|
IDraftLog getDraftLog();
|
||||||
LimitedPlayer getNeighbor(LimitedPlayer p, boolean left);
|
LimitedPlayer getNeighbor(LimitedPlayer p, boolean left);
|
||||||
LimitedPlayer getPlayer(int i);
|
LimitedPlayer getPlayer(int i);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ public class LimitedPlayer {
|
|||||||
protected Deck deck;
|
protected Deck deck;
|
||||||
protected PaperCard lastPick;
|
protected PaperCard lastPick;
|
||||||
|
|
||||||
protected Queue<List<PaperCard>> packQueue;
|
protected Queue<DraftPack> packQueue;
|
||||||
protected Queue<List<PaperCard>> unopenedPacks;
|
protected Queue<DraftPack> unopenedPacks;
|
||||||
protected List<PaperCard> removedFromCardPool = new ArrayList<>();
|
protected List<PaperCard> removedFromCardPool = new ArrayList<>();
|
||||||
|
|
||||||
private static final int CantDraftThisRound = 1;
|
private static final int AgentAcquisitionsCanDraftAll = 1;
|
||||||
private static final int SpyNextCardDrafted = 1 << 1;
|
private static final int AgentAcquisitionsIsDraftingAll = 1 << 1;
|
||||||
private static final int ReceiveLastCard = 1 << 2;
|
private static final int AgentAcquisitionsSkipDraftRound = 1 << 2;
|
||||||
private static final int CanRemoveAfterDraft = 1 << 3;
|
private static final int CogworkLibrarianExtraDraft = 1 << 3;
|
||||||
private static final int CanTradeAfterDraft = 1 << 4;
|
private static final int CogworkLibrarianReturnLibrarian = 1 << 4;
|
||||||
private static final int AnimusRemoveFromPool = 1 << 5;
|
private static final int AnimusRemoveFromPool = 1 << 5;
|
||||||
private static final int NobleBanneretActive = 1 << 6;
|
private static final int NobleBanneretActive = 1 << 6;
|
||||||
private static final int PalianoVanguardActive = 1 << 7;
|
private static final int PalianoVanguardActive = 1 << 7;
|
||||||
@@ -37,6 +37,10 @@ public class LimitedPlayer {
|
|||||||
private static final int SearcherNoteNext = 1 << 9;
|
private static final int SearcherNoteNext = 1 << 9;
|
||||||
private static final int WhispergearBoosterPeek = 1 << 10;
|
private static final int WhispergearBoosterPeek = 1 << 10;
|
||||||
private static final int IllusionaryInformantPeek = 1 << 11;
|
private static final int IllusionaryInformantPeek = 1 << 11;
|
||||||
|
private static final int LeovoldsOperativeCanExtraDraft = 1 << 12;
|
||||||
|
private static final int LeovoldsOperativeExtraDraft = 1 << 13;
|
||||||
|
private static final int LeovoldsOperativeSkipNext = 1 << 14;
|
||||||
|
private static final int SpyNextCardDrafted = 1 << 15;
|
||||||
|
|
||||||
private int playerFlags = 0;
|
private int playerFlags = 0;
|
||||||
|
|
||||||
@@ -88,24 +92,25 @@ public class LimitedPlayer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean draftCard(PaperCard bestPick) {
|
public Boolean draftCard(PaperCard bestPick) {
|
||||||
return draftCard(bestPick, DeckSection.Sideboard);
|
return draftCard(bestPick, DeckSection.Sideboard);
|
||||||
}
|
}
|
||||||
public boolean draftCard(PaperCard bestPick, DeckSection section) {
|
public Boolean draftCard(PaperCard bestPick, DeckSection section) {
|
||||||
if (bestPick == null) {
|
if (bestPick == null) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PaperCard> chooseFrom = packQueue.peek();
|
DraftPack chooseFrom = packQueue.peek();
|
||||||
if (chooseFrom == null) {
|
if (chooseFrom == null) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean removedFromPool = false;
|
boolean removedFromPool = false;
|
||||||
boolean alreadyRevealed = false;
|
boolean alreadyRevealed = false;
|
||||||
|
boolean passPack = true;
|
||||||
|
|
||||||
chooseFrom.remove(bestPick);
|
chooseFrom.remove(bestPick);
|
||||||
lastPick = lastPick;
|
lastPick = bestPick;
|
||||||
|
|
||||||
draftedThisRound++;
|
draftedThisRound++;
|
||||||
|
|
||||||
@@ -134,9 +139,10 @@ public class LimitedPlayer {
|
|||||||
recordRemoveFromDraft(bestPick, choice);
|
recordRemoveFromDraft(bestPick, choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
LimitedPlayer fromPlayer = receivedFrom();
|
|
||||||
|
LimitedPlayer fromPlayer = chooseFrom.getPassedFrom();
|
||||||
// If the previous player has an active Cogwork Spy, show them this card
|
// If the previous player has an active Cogwork Spy, show them this card
|
||||||
if ((fromPlayer.playerFlags & SpyNextCardDrafted) == SpyNextCardDrafted) {
|
if (fromPlayer != null && (fromPlayer.playerFlags & SpyNextCardDrafted) == SpyNextCardDrafted) {
|
||||||
if (fromPlayer instanceof LimitedPlayerAI) {
|
if (fromPlayer instanceof LimitedPlayerAI) {
|
||||||
// I'm honestly not sure what the AI would do by learning this information
|
// I'm honestly not sure what the AI would do by learning this information
|
||||||
// But just log that a reveal "happened"
|
// But just log that a reveal "happened"
|
||||||
@@ -169,9 +175,73 @@ public class LimitedPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((playerFlags & AgentAcquisitionsCanDraftAll) == AgentAcquisitionsCanDraftAll) {
|
||||||
|
if (handleAgentOfAcquisitions(chooseFrom, bestPick)) {
|
||||||
|
addLog(name() + " drafted the rest of the pack with Agent of Acquisitions");
|
||||||
|
playerFlags &= ~AgentAcquisitionsCanDraftAll;
|
||||||
|
playerFlags |= AgentAcquisitionsIsDraftingAll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((playerFlags & AgentAcquisitionsIsDraftingAll) == AgentAcquisitionsIsDraftingAll) {
|
||||||
|
if (chooseFrom.isEmpty()) {
|
||||||
|
playerFlags &= ~AgentAcquisitionsIsDraftingAll;
|
||||||
|
playerFlags |= AgentAcquisitionsSkipDraftRound;
|
||||||
|
} else {
|
||||||
|
passPack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((playerFlags & LeovoldsOperativeExtraDraft) == LeovoldsOperativeExtraDraft) {
|
||||||
|
if (handleLeovoldsOperative(chooseFrom, bestPick)) {
|
||||||
|
addLog(name() + " skipped their next pick with Leovold's Operative.");
|
||||||
|
playerFlags &= ~LeovoldsOperativeExtraDraft;
|
||||||
|
playerFlags |= LeovoldsOperativeSkipNext;
|
||||||
|
passPack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((playerFlags & LeovoldsOperativeCanExtraDraft) == LeovoldsOperativeCanExtraDraft) {
|
||||||
|
if (handleLeovoldsOperative(chooseFrom, bestPick)) {
|
||||||
|
addLog(name() + " picking again with Leovold's Operative.");
|
||||||
|
playerFlags &= ~LeovoldsOperativeCanExtraDraft;
|
||||||
|
playerFlags |= LeovoldsOperativeExtraDraft;
|
||||||
|
passPack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((playerFlags & CogworkLibrarianReturnLibrarian) == CogworkLibrarianReturnLibrarian) {
|
||||||
|
addLog(name() + " returned Cogwork Librarian to the pack.");
|
||||||
|
|
||||||
|
PaperCard librarian = deck.removeCardName("Cogwork Librarian");
|
||||||
|
// TODO The librarian needs to be removed from the UI
|
||||||
|
|
||||||
|
// We shouldn't get here unless we've drafted librarian so we should be able to find one in Deck
|
||||||
|
// If somehow we don't wellll.. we should remove the bitflag anyway
|
||||||
|
playerFlags &= ~CogworkLibrarianReturnLibrarian;
|
||||||
|
if (librarian != null) {
|
||||||
|
chooseFrom.add(librarian);
|
||||||
|
} else {
|
||||||
|
System.out.println("This shouldn't happen. We drafted a libarian but didn't remove it properly.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((playerFlags & CogworkLibrarianExtraDraft) == CogworkLibrarianExtraDraft) {
|
||||||
|
if (handleCogworkLibrarian(chooseFrom, bestPick)) {
|
||||||
|
addLog(name() + " drafted an extra card with Cogwork Librarian.");
|
||||||
|
playerFlags &= ~CogworkLibrarianExtraDraft;
|
||||||
|
playerFlags |= CogworkLibrarianReturnLibrarian;
|
||||||
|
passPack = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chooseFrom.getAwaitingGuess() != null) {
|
||||||
|
comparePhantasmGuess(chooseFrom, bestPick);
|
||||||
|
}
|
||||||
|
|
||||||
if (removedFromPool) {
|
if (removedFromPool) {
|
||||||
// Can we hide this from UI?
|
// Can we hide this from UI?
|
||||||
return true;
|
return passPack;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardPool pool = deck.getOrCreate(section);
|
CardPool pool = deck.getOrCreate(section);
|
||||||
@@ -182,7 +252,7 @@ public class LimitedPlayer {
|
|||||||
|
|
||||||
Iterable<String> draftActions = bestPick.getRules().getMainPart().getDraftActions();
|
Iterable<String> draftActions = bestPick.getRules().getMainPart().getDraftActions();
|
||||||
if (draftActions == null || !draftActions.iterator().hasNext()) {
|
if (draftActions == null || !draftActions.iterator().hasNext()) {
|
||||||
return true;
|
return passPack;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draft Actions
|
// Draft Actions
|
||||||
@@ -223,17 +293,17 @@ public class LimitedPlayer {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (Iterables.contains(draftActions, "You may look at the next card drafted from this booster pack.")) {
|
if (Iterables.contains(draftActions, "You may look at the next card drafted from this booster pack.")) {
|
||||||
// Cogwork Spy
|
|
||||||
playerFlags |= SpyNextCardDrafted;
|
playerFlags |= SpyNextCardDrafted;
|
||||||
}
|
} else if (fromPlayer != null && Iterables.contains(draftActions, "Note the player who passed CARDNAME to you.")) {
|
||||||
|
|
||||||
if (Iterables.contains(draftActions, "Note the player who passed CARDNAME to you.")) {
|
|
||||||
// Note who passed it to you.
|
// Note who passed it to you.
|
||||||
// If you receive last card from Canal Dredger, we need to figure out who last had the pack?
|
// If you receive last card from Canal Dredger, we need to figure out who last had the pack?
|
||||||
List<String> note = noted.computeIfAbsent(bestPick.getName(), k -> Lists.newArrayList());
|
List<String> note = noted.computeIfAbsent(bestPick.getName(), k -> Lists.newArrayList());
|
||||||
note.add(String.valueOf(fromPlayer.order));
|
note.add(String.valueOf(fromPlayer.order));
|
||||||
|
addLog(name() + " revealed " + bestPick.getName() + " and noted " + fromPlayer.name() + " passed it.");
|
||||||
} else if (Iterables.contains(draftActions, "Reveal the next card you draft and note its name.")) {
|
} else if (Iterables.contains(draftActions, "Reveal the next card you draft and note its name.")) {
|
||||||
playerFlags |= SearcherNoteNext;
|
playerFlags |= SearcherNoteNext;
|
||||||
|
} else if (Iterables.contains(draftActions, "The next time a player drafts a card from this booster pack, guess that card’s name. Then that player reveals the drafted card.")) {
|
||||||
|
chooseFrom.setAwaitingGuess(this, handleSpirePhantasm(chooseFrom));
|
||||||
}
|
}
|
||||||
|
|
||||||
addLog(name() + " revealed " + bestPick.getName() + " as " + name() + " drafted it.");
|
addLog(name() + " revealed " + bestPick.getName() + " as " + name() + " drafted it.");
|
||||||
@@ -261,6 +331,12 @@ public class LimitedPlayer {
|
|||||||
// Do we need to ask to use the Sneak immediately?
|
// Do we need to ask to use the Sneak immediately?
|
||||||
} else if (Iterables.contains(draftActions, "During the draft, you may turn CARDNAME face down. If you do, look at the next card drafted by a player of your choice.")) {
|
} else if (Iterables.contains(draftActions, "During the draft, you may turn CARDNAME face down. If you do, look at the next card drafted by a player of your choice.")) {
|
||||||
playerFlags |= IllusionaryInformantPeek;
|
playerFlags |= IllusionaryInformantPeek;
|
||||||
|
} else if (Iterables.contains(draftActions, "As you draft a card, you may draft an additional card from that booster pack. If you do, put CARDNAME into that booster pack.")) {
|
||||||
|
playerFlags |= CogworkLibrarianExtraDraft;
|
||||||
|
} else if (Iterables.contains(draftActions, "As you draft a card, you may draft an additional card from that booster pack. If you do, turn CARDNAME face down, then pass the next booster pack without drafting a card from it. (You may look at that booster pack.)")) {
|
||||||
|
playerFlags |= LeovoldsOperativeExtraDraft;
|
||||||
|
} else if (Iterables.contains(draftActions, "Instead of drafting a card from a booster pack, you may draft each card in that booster pack, one at a time. If you do, turn CARDNAME face down and you can’t draft cards for the rest of this draft round. (You may look at booster packs passed to you.)")) {
|
||||||
|
playerFlags |= AgentAcquisitionsCanDraftAll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,32 +353,44 @@ public class LimitedPlayer {
|
|||||||
// Mobile doesnt have a draft log yet
|
// Mobile doesnt have a draft log yet
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PaperCard> nextChoice() {
|
public DraftPack nextChoice() {
|
||||||
return packQueue.peek();
|
return packQueue.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LimitedPlayer receivedFrom() {
|
|
||||||
return draft.getNeighbor(this, draft.getRound() % 2 == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void newPack() {
|
public void newPack() {
|
||||||
currentPack = order;
|
currentPack = order;
|
||||||
draftedThisRound = 0;
|
draftedThisRound = 0;
|
||||||
packQueue.add(unopenedPacks.poll());
|
packQueue.add(unopenedPacks.poll());
|
||||||
|
playerFlags &= ~AgentAcquisitionsSkipDraftRound;
|
||||||
}
|
}
|
||||||
public void adjustPackNumber(int adjust, int numPacks) {
|
public void adjustPackNumber(int adjust, int numPacks) {
|
||||||
|
// I shouldn't need this since DraftPack has this info
|
||||||
currentPack = (currentPack + adjust + numPacks) % numPacks;
|
currentPack = (currentPack + adjust + numPacks) % numPacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PaperCard> passPack() {
|
public DraftPack passPack() {
|
||||||
return packQueue.poll();
|
DraftPack pack = packQueue.poll();
|
||||||
|
if (pack != null) {
|
||||||
|
pack.setPassedFrom(this);
|
||||||
|
}
|
||||||
|
return pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void receiveUnopenedPack(List<PaperCard> pack) {
|
public boolean shouldSkipThisPick() {
|
||||||
|
boolean skipping = (playerFlags & AgentAcquisitionsSkipDraftRound) == AgentAcquisitionsSkipDraftRound || (playerFlags & LeovoldsOperativeSkipNext) == LeovoldsOperativeSkipNext;
|
||||||
|
|
||||||
|
if (skipping && (playerFlags & LeovoldsOperativeSkipNext) == LeovoldsOperativeSkipNext) {
|
||||||
|
playerFlags &= ~LeovoldsOperativeSkipNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return skipping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receiveUnopenedPack(DraftPack pack) {
|
||||||
unopenedPacks.add(pack);
|
unopenedPacks.add(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void receiveOpenedPack(List<PaperCard> pack) {
|
public void receiveOpenedPack(DraftPack pack) {
|
||||||
packQueue.add(pack);
|
packQueue.add(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +592,7 @@ public class LimitedPlayer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<PaperCard> peekAtBoosterPack(int round, int playerNumber) {
|
protected DraftPack peekAtBoosterPack(int round, int playerNumber) {
|
||||||
if (draft.getRound() > round) {
|
if (draft.getRound() > round) {
|
||||||
// There aren't any unopened packs from earlier rounds
|
// There aren't any unopened packs from earlier rounds
|
||||||
return null;
|
return null;
|
||||||
@@ -541,11 +629,51 @@ public class LimitedPlayer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PaperCard handleSpirePhantasm(DraftPack chooseFrom) {
|
||||||
|
if (chooseFrom.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SGuiChoose.one("Guess the next card drafted from this pack", chooseFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleLeovoldsOperative(DraftPack pack, PaperCard drafted) {
|
||||||
|
if (Objects.equals(SGuiChoose.one("Draft an extra pick with Leovold's Operative?", Lists.newArrayList("Yes", "No")), "No")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerFlags |= LeovoldsOperativeExtraDraft;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleCogworkLibrarian(DraftPack pack, PaperCard drafted) {
|
||||||
|
return !Objects.equals(SGuiChoose.one("Draft an extra pick with Cogwork Librarian?", Lists.newArrayList("Yes", "No")), "No");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleAgentOfAcquisitions(DraftPack pack, PaperCard drafted) {
|
||||||
|
return !Objects.equals(SGuiChoose.one("Draft the rest of the pack with Agent of Acquisitions?", Lists.newArrayList("Yes", "No")), "No");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void comparePhantasmGuess(DraftPack pack, PaperCard drafted) {
|
||||||
|
LimitedPlayer guesser = pack.getAwaitingGuess().getKey();
|
||||||
|
PaperCard guess = pack.getAwaitingGuess().getValue();
|
||||||
|
|
||||||
|
addLog(name() + " reveals " + drafted.getName() + " from " + guesser.name() + "'s guess of " + guess.getName() + " with Spire Phantasm.");
|
||||||
|
if (guess.equals(drafted)) {
|
||||||
|
addLog(guesser.name() + " correctly guessed " + guess.getName() + " with Spire Phantasm.");
|
||||||
|
guesser.getDraftNotes().computeIfAbsent("Spire Phantasm", k -> Lists.newArrayList()).add(guess.getName());
|
||||||
|
} else {
|
||||||
|
addLog(guesser.name() + " incorrectly guessed " + guess.getName() + " with Spire Phantasm.");
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.resetAwaitingGuess();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
public void addSingleBoosterPack(boolean random) {
|
public void addSingleBoosterPack(boolean random) {
|
||||||
// TODO Lore Seeker
|
// TODO Lore Seeker
|
||||||
// Generate booster pack then, insert it "before" the pack we're currently drafting from
|
// Generate booster pack then, insert it "before" the pack we're currently drafting from
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static forge.gamemodes.limited.CardRanker.getOrderedRawScores;
|
||||||
|
import static forge.gamemodes.limited.CardRanker.rankCardsInPack;
|
||||||
|
|
||||||
public class LimitedPlayerAI extends LimitedPlayer {
|
public class LimitedPlayerAI extends LimitedPlayer {
|
||||||
protected DeckColors deckCols;
|
protected DeckColors deckCols;
|
||||||
|
|
||||||
@@ -26,7 +29,7 @@ public class LimitedPlayerAI extends LimitedPlayer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PaperCard> chooseFrom = packQueue.peek();
|
DraftPack chooseFrom = packQueue.peek();
|
||||||
if (chooseFrom.isEmpty()) {
|
if (chooseFrom.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -38,11 +41,10 @@ public class LimitedPlayerAI extends LimitedPlayer {
|
|||||||
|
|
||||||
// TODO Archdemon of Paliano random draft while active
|
// TODO Archdemon of Paliano random draft while active
|
||||||
|
|
||||||
|
|
||||||
final ColorSet chosenColors = deckCols.getChosenColors();
|
final ColorSet chosenColors = deckCols.getChosenColors();
|
||||||
final boolean canAddMoreColors = deckCols.canChoseMoreColors();
|
final boolean canAddMoreColors = deckCols.canChoseMoreColors();
|
||||||
|
|
||||||
List<PaperCard> rankedCards = CardRanker.rankCardsInPack(chooseFrom, pool.toFlatList(), chosenColors, canAddMoreColors);
|
List<PaperCard> rankedCards = rankCardsInPack(chooseFrom, pool.toFlatList(), chosenColors, canAddMoreColors);
|
||||||
PaperCard bestPick = rankedCards.get(0);
|
PaperCard bestPick = rankedCards.get(0);
|
||||||
|
|
||||||
if (canAddMoreColors) {
|
if (canAddMoreColors) {
|
||||||
@@ -177,4 +179,47 @@ public class LimitedPlayerAI extends LimitedPlayer {
|
|||||||
//peekAt.getLastPick();
|
//peekAt.getLastPick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PaperCard handleSpirePhantasm(DraftPack chooseFrom) {
|
||||||
|
if (chooseFrom.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose the card with the highest rank left
|
||||||
|
return getOrderedRawScores(chooseFrom).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleLeovoldsOperative(DraftPack pack, PaperCard drafted) {
|
||||||
|
// Whats the score of the thing I just drafted?
|
||||||
|
// Whats the next card I would draft?
|
||||||
|
if (currentPack == 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return draftedThisRound < 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleAgentOfAcquisitions(DraftPack pack, PaperCard drafted) {
|
||||||
|
// Whats the score of the thing I just drafted?
|
||||||
|
// Whats the total score of the rest of the pack?
|
||||||
|
// How many of these cards would actually make my deck?
|
||||||
|
if (currentPack == 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return draftedThisRound > 2 && draftedThisRound < 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleCogworkLibrarian(DraftPack pack, PaperCard drafted) {
|
||||||
|
if (currentPack == 3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return draftedThisRound < 3;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user