mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Draft matters with a vengeance (#5400)
* Next few Draft cards * Whispergear Sneak * Whispergear Sneak * Code review fixes * Fix index mapping * Don't try to log if Draft doesnt have one
This commit is contained in:
@@ -50,7 +50,6 @@ import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Visitor;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -327,7 +326,9 @@ public class Game {
|
||||
int plId = 0;
|
||||
for (RegisteredPlayer psc : players0) {
|
||||
IGameEntitiesFactory factory = (IGameEntitiesFactory)psc.getPlayer();
|
||||
Player pl = factory.createIngamePlayer(this, plId++);
|
||||
// If the Registered Player already has a pre-assigned ID, use that. Otherwise, assign a new one.
|
||||
Integer id = psc.getId();
|
||||
Player pl = factory.createIngamePlayer(this, id == null ? plId++ : id);
|
||||
allPlayers.add(pl);
|
||||
ingamePlayers.add(pl);
|
||||
|
||||
|
||||
@@ -2120,6 +2120,14 @@ public class CardProperty {
|
||||
}
|
||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||
|
||||
return nameList.contains(card.getName());
|
||||
} else if (property.equals("NotedNameAetherSearcher")) {
|
||||
String names = sourceController.getDraftNotes().get("Aether Searcher");
|
||||
if (names == null || names.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||
|
||||
return nameList.contains(card.getName());
|
||||
} else if (property.equals("NotedTypes")) {
|
||||
// Should Paliano Vanguard be hardcoded here or part of the property?
|
||||
|
||||
@@ -2,7 +2,6 @@ package forge.game.player;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -15,6 +14,7 @@ import forge.util.Expressions;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -264,6 +264,10 @@ public class PlayerProperty {
|
||||
if (source.getChosenPlayer() == null || !source.getChosenPlayer().equals(player)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("NotedDefender")) {
|
||||
String tracker = player.getDraftNotes().getOrDefault("Cogwork Tracker", "");
|
||||
|
||||
return Iterables.contains(Arrays.asList(tracker.split(",")), String.valueOf(player));
|
||||
} else if (property.startsWith("life")) {
|
||||
int life = player.getLife();
|
||||
int amount = AbilityUtils.calculateAmount(source, property.substring(6), spellAbility);
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package forge.game.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
@@ -16,6 +10,11 @@ import forge.game.GameType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RegisteredPlayer {
|
||||
private final Deck originalDeck; // never return or modify this instance (it's a reference to game resources)
|
||||
private Deck currentDeck;
|
||||
@@ -37,6 +36,7 @@ public class RegisteredPlayer {
|
||||
private List<PaperCard> vanguardAvatars = null;
|
||||
private PaperCard planeswalker = null;
|
||||
private int teamNumber = -1; // members of teams with negative id will play FFA.
|
||||
private Integer id = null;
|
||||
private boolean randomFoil = false;
|
||||
private boolean enableETBCountersEffect = false;
|
||||
|
||||
@@ -45,6 +45,14 @@ public class RegisteredPlayer {
|
||||
restoreDeck();
|
||||
}
|
||||
|
||||
public final Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public final void setId(Integer id0) {
|
||||
id = id0;
|
||||
}
|
||||
|
||||
public final Deck getDeck() {
|
||||
return currentDeck;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
package forge.screens.home.sanctioned;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.Singletons;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
@@ -38,6 +28,14 @@ import forge.screens.deckeditor.views.VStatistics;
|
||||
import forge.toolbox.FOptionPane;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controls the draft submenu in the home UI.
|
||||
*
|
||||
@@ -142,6 +140,12 @@ public enum CSubmenuDraft implements ICDoc {
|
||||
|
||||
FModel.getGauntletMini().resetGauntletDraft();
|
||||
String duelType = (String)VSubmenuDraft.SINGLETON_INSTANCE.getCbOpponent().getSelectedItem();
|
||||
|
||||
if (duelType == null) {
|
||||
FOptionPane.showErrorDialog("Please select duel types for the draft match.", "Missing opponent items");
|
||||
return;
|
||||
}
|
||||
|
||||
final DeckGroup opponentDecks = FModel.getDecks().getDraft().get(humanDeck.getName());
|
||||
if (gauntlet) {
|
||||
if ("Gauntlet".equals(duelType)) {
|
||||
@@ -161,7 +165,7 @@ public enum CSubmenuDraft implements ICDoc {
|
||||
}
|
||||
});
|
||||
|
||||
List<Deck> aiDecks = Lists.newArrayList();
|
||||
Map<Integer, Deck> aiMap = Maps.newHashMap();
|
||||
if (VSubmenuDraft.SINGLETON_INSTANCE.isSingleSelected()) {
|
||||
// Restore Zero Indexing
|
||||
final int aiIndex = Integer.parseInt(duelType)-1;
|
||||
@@ -169,28 +173,43 @@ public enum CSubmenuDraft implements ICDoc {
|
||||
if (aiDeck == null) {
|
||||
throw new IllegalStateException("Draft: Computer deck is null!");
|
||||
}
|
||||
aiDecks.add(aiDeck);
|
||||
|
||||
aiMap.put(aiIndex, aiDeck);
|
||||
} else {
|
||||
final int numOpponents = Integer.parseInt(duelType);
|
||||
|
||||
List<Deck> randomOpponents = Lists.newArrayList(opponentDecks.getAiDecks());
|
||||
Collections.shuffle(randomOpponents);
|
||||
aiDecks = randomOpponents.subList(0, numOpponents);
|
||||
for(Deck d : aiDecks) {
|
||||
if (d == null) {
|
||||
int maxDecks = opponentDecks.getAiDecks().size();
|
||||
if (numOpponents > maxDecks) {
|
||||
throw new IllegalStateException("Draft: Not enough decks for the number of opponents!");
|
||||
}
|
||||
|
||||
List<Integer> aiIndices = Lists.newArrayList();
|
||||
for(int i = 0; i < maxDecks; i++) {
|
||||
aiIndices.add(i);
|
||||
}
|
||||
Collections.shuffle(aiIndices);
|
||||
aiIndices = aiIndices.subList(0, numOpponents);
|
||||
|
||||
for(int i : aiIndices) {
|
||||
final Deck aiDeck = opponentDecks.getAiDecks().get(i);
|
||||
if (aiDeck == null) {
|
||||
throw new IllegalStateException("Draft: Computer deck is null!");
|
||||
}
|
||||
|
||||
aiMap.put(i + 1, aiDeck);
|
||||
}
|
||||
}
|
||||
|
||||
final List<RegisteredPlayer> starter = new ArrayList<>();
|
||||
// Human is 0
|
||||
final RegisteredPlayer human = new RegisteredPlayer(humanDeck.getDeck()).setPlayer(GamePlayerUtil.getGuiPlayer());
|
||||
starter.add(human);
|
||||
for(Deck aiDeck : aiDecks) {
|
||||
starter.add(new RegisteredPlayer(aiDeck).setPlayer(GamePlayerUtil.createAiPlayer()));
|
||||
}
|
||||
for (final RegisteredPlayer pl : starter) {
|
||||
pl.assignConspiracies();
|
||||
human.setId(0);
|
||||
for(Map.Entry<Integer, Deck> aiDeck : aiMap.entrySet()) {
|
||||
RegisteredPlayer aiPlayer = new RegisteredPlayer(aiDeck.getValue()).setPlayer(GamePlayerUtil.createAiPlayer());
|
||||
aiPlayer.setId(aiDeck.getKey());
|
||||
starter.add(aiPlayer);
|
||||
aiPlayer.assignConspiracies();
|
||||
}
|
||||
|
||||
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.List;
|
||||
public class BoosterDraftTest implements IBoosterDraft {
|
||||
|
||||
private int n = 3;
|
||||
private int round = 1;
|
||||
|
||||
@Override
|
||||
@Test(timeOut = 1000)
|
||||
@@ -43,6 +44,11 @@ public class BoosterDraftTest implements IBoosterDraft {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRound() {
|
||||
return round;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardPool nextChoice() {
|
||||
this.n--;
|
||||
@@ -95,4 +101,9 @@ public class BoosterDraftTest implements IBoosterDraft {
|
||||
public LimitedPlayer getNeighbor(LimitedPlayer p, boolean left) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitedPlayer getPlayer(int i) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
17
forge-gui/res/cardsfolder/a/aether_searcher.txt
Normal file
17
forge-gui/res/cardsfolder/a/aether_searcher.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Name:Aether Searcher
|
||||
ManaCost:7
|
||||
PT:6/4
|
||||
Types:Artifact Creature Construct
|
||||
Draft:Reveal CARDNAME as you draft it.
|
||||
Draft:Reveal the next card you draft and note its name.
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSearchHand | TriggerDescription$ When CARDNAME enters the battlefield, you may search your hand and/or library for a card with a name noted as you drafted cards named Aether Searcher. You may cast it without paying its mana cost. If you searched your library this way, shuffle.
|
||||
SVar:TrigSearchHand:DB$ ChangeZone | Origin$ Hand | Destination$ Hand | ChangeType$ Card.NotedNameAetherSearcher | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ TrigBranch
|
||||
# Branch to casting the found spell
|
||||
SVar:TrigBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ EQ1 | TrueSubAbility$ CastFromHand | FalseSubAbility$ SearchLibrary
|
||||
SVar:CastFromHand:DB$ Play | ValidZone$ Hand | Valid$ Card.IsRemembered | Controller$ You | WithoutManaCost$ True | Optional$ True | SubAbility$ DBCleanup
|
||||
# Or search the library
|
||||
SVar:SearchLibrary:DB$ ChangeZone | Origin$ Library | Destination$ Library | ChangeType$ Card.NotedNameAetherSearcher | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ CastFromLibrary
|
||||
SVar:CastFromLibrary:DB$ Play | ValidZone$ Library | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | Valid$ Card.IsRemembered | Controller$ You | WithoutManaCost$ True | Optional$ True | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:X:Remembered$Amount
|
||||
Oracle:Reveal Aether Searcher as you draft it. Reveal the next card you draft and note its name.\nWhen Aether Searcher enters the battlefield, you may search your hand and/or library for a card with a name noted as you drafted cards named Aether Searcher. You may cast it without paying its mana cost. If you searched your library this way, shuffle.
|
||||
8
forge-gui/res/cardsfolder/c/cogwork_spy.txt
Normal file
8
forge-gui/res/cardsfolder/c/cogwork_spy.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Cogwork Spy
|
||||
ManaCost:3
|
||||
Types:Artifact Creature Bird Construct
|
||||
PT:2/1
|
||||
K:Flying
|
||||
Draft:Reveal CARDNAME as you draft it.
|
||||
Draft:You may look at the next card drafted from this booster pack.
|
||||
Oracle:Reveal Cogwork Spy as you draft it. You may look at the next card drafted from this booster pack.\nFlying
|
||||
9
forge-gui/res/cardsfolder/c/cogwork_tracker.txt
Normal file
9
forge-gui/res/cardsfolder/c/cogwork_tracker.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Cogwork Tracker
|
||||
ManaCost:4
|
||||
Types:Artifact Creature Hound Construct
|
||||
PT:4/4
|
||||
Draft:Reveal CARDNAME as you draft it.
|
||||
Draft:Note the player who passed CARDNAME to you.
|
||||
S:Mode$ MustAttack | ValidCreature$ Card.Self | Description$ CARDNAME attacks each combat if able.
|
||||
S:Mode$ MustAttack | ValidPlayer$ Opponent.NotedDefender | ValidCreature$ Card.Self | Description$ CARDNAME attacks a player you noted for cards named Cogwork Tracker each combat if able.
|
||||
Oracle:Reveal Cogwork Tracker as you draft it and note the player who passed it to you.\nCogwork Tracker attacks each combat if able.\nCogwork Tracker attacks a player you noted for cards named Cogwork Tracker each combat if able.
|
||||
8
forge-gui/res/cardsfolder/i/illusionary_informant.txt
Normal file
8
forge-gui/res/cardsfolder/i/illusionary_informant.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Illusionary Informant
|
||||
ManaCost:1 U
|
||||
Types:Creature Bird Illusion
|
||||
PT:1/3
|
||||
Draft:Draft CARDNAME face up.
|
||||
Draft:During the draft, you may turn CARDNAME face down. If you do, look at the next card drafted by a player of your choice.
|
||||
K:Flying
|
||||
Oracle:Draft Illusionary Informant face up.\nDuring the draft, you may turn Illusionary Informant face down. If you do, look at the next card drafted by a player of your choice.\nFlying
|
||||
7
forge-gui/res/cardsfolder/w/whispergear_sneak.txt
Normal file
7
forge-gui/res/cardsfolder/w/whispergear_sneak.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Name:Whispergear Sneak
|
||||
ManaCost:1
|
||||
Types:Artifact Creature Construct
|
||||
PT:1/1
|
||||
Draft:Draft CARDNAME face up.
|
||||
Draft:During the draft, you may turn CARDNAME face down. If you do, look at any unopened booster pack in the draft or any booster pack not being looked at by another player.
|
||||
Oracle:Draft Whispergear Sneak face up.\nDuring the draft, you may turn Whispergear Sneak face down. If you do, look at any unopened booster pack in the draft or any booster pack not being looked at by another player.
|
||||
@@ -246,6 +246,7 @@ Garbage Fire
|
||||
Hired Heist
|
||||
Hold the Perimeter
|
||||
Hymn of the Wilds
|
||||
Illusionary Informant
|
||||
Incendiary Dissent
|
||||
Natural Unity
|
||||
Noble Banneret
|
||||
|
||||
@@ -222,15 +222,15 @@ ScryfallCode=CNS
|
||||
|
||||
[Draft Matters]
|
||||
#1 Advantageous Proclamation|CNS
|
||||
#1 AEther Searcher|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 Cogwork Spy|CNS
|
||||
1 Cogwork Tracker|CNS
|
||||
#1 Deal Broker|CNS
|
||||
#1 Double Stroke|CNS
|
||||
1 Immediate Action|CNS
|
||||
@@ -244,7 +244,7 @@ ScryfallCode=CNS
|
||||
1 Secrets of Paradise|CNS
|
||||
#1 Sentinel Dispatch|CNS
|
||||
#1 Unexpected Potential|CNS
|
||||
#1 Whispergear Sneak|CNS
|
||||
1 Whispergear Sneak|CNS
|
||||
#1 Worldknit|CNS
|
||||
|
||||
[tokens]
|
||||
|
||||
@@ -285,9 +285,14 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
return draftLog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRound() {
|
||||
return nextBoosterGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitedPlayer getNeighbor(LimitedPlayer player, boolean left) {
|
||||
return players.get((player.order + (left ? -1 : 1) + N_PLAYERS) % N_PLAYERS);
|
||||
return players.get((player.order + (left ? 1 : -1) + N_PLAYERS) % N_PLAYERS);
|
||||
}
|
||||
|
||||
private void setupCustomDraft(final CustomLimited draft) {
|
||||
@@ -412,6 +417,15 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
return this.localPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitedPlayer getPlayer(int i) {
|
||||
if (i == 0) {
|
||||
return this.localPlayer;
|
||||
}
|
||||
|
||||
return this.players.get(i - 1);
|
||||
}
|
||||
|
||||
public void passPacks() {
|
||||
// Alternate direction of pack passing
|
||||
int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1;
|
||||
@@ -419,6 +433,7 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
adjust = 0;
|
||||
} else if (currentBoosterPick % 2 == 1 && "Always".equals(this.doublePickDuringDraft)) {
|
||||
// This may not work with Conspiracy cards that mess with the draft
|
||||
// But it probably doesn't matter since Conspiracy doesn't have double pick?
|
||||
adjust = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import forge.item.PaperCard;
|
||||
*/
|
||||
public interface IBoosterDraft {
|
||||
|
||||
int getRound();
|
||||
CardPool nextChoice();
|
||||
void setChoice(PaperCard c);
|
||||
boolean hasNextChoice();
|
||||
@@ -47,4 +48,5 @@ public interface IBoosterDraft {
|
||||
void setLogEntry(IDraftLog draftingProcess);
|
||||
IDraftLog getDraftLog();
|
||||
LimitedPlayer getNeighbor(LimitedPlayer p, boolean left);
|
||||
LimitedPlayer getPlayer(int i);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public class LimitedPlayer {
|
||||
protected int currentPack;
|
||||
protected int draftedThisRound;
|
||||
protected Deck deck;
|
||||
protected PaperCard lastPick;
|
||||
|
||||
protected Queue<List<PaperCard>> packQueue;
|
||||
protected Queue<List<PaperCard>> unopenedPacks;
|
||||
@@ -33,17 +34,16 @@ public class LimitedPlayer {
|
||||
private static final int NobleBanneretActive = 1 << 6;
|
||||
private static final int PalianoVanguardActive = 1 << 7;
|
||||
private static final int GrinderRemoveFromPool = 1 << 8;
|
||||
|
||||
private static final int MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted
|
||||
| CanTradeAfterDraft | AnimusRemoveFromPool | NobleBanneretActive | PalianoVanguardActive
|
||||
| GrinderRemoveFromPool;
|
||||
private static final int SearcherNoteNext = 1 << 9;
|
||||
private static final int WhispergearBoosterPeek = 1 << 10;
|
||||
private static final int IllusionaryInformantPeek = 1 << 11;
|
||||
|
||||
private int playerFlags = 0;
|
||||
|
||||
private final List<PaperCard> faceUp = Lists.newArrayList();
|
||||
private final List<PaperCard> revealed = Lists.newArrayList();
|
||||
private final Map<String, List<String>> noted = new HashMap<>();
|
||||
private final HashSet<String> semicolonDelimiter = Sets.newHashSet("Noble Banneret", "Cogwork Grinder");
|
||||
private final HashSet<String> semicolonDelimiter = Sets.newHashSet("Noble Banneret", "Cogwork Grinder", "Aether Searcher");
|
||||
|
||||
IBoosterDraft draft;
|
||||
|
||||
@@ -69,6 +69,10 @@ public class LimitedPlayer {
|
||||
return serialized;
|
||||
}
|
||||
|
||||
public PaperCard getLastPick() {
|
||||
return lastPick;
|
||||
}
|
||||
|
||||
public Deck getDeck() {
|
||||
return deck;
|
||||
}
|
||||
@@ -101,6 +105,7 @@ public class LimitedPlayer {
|
||||
boolean alreadyRevealed = false;
|
||||
|
||||
chooseFrom.remove(bestPick);
|
||||
lastPick = lastPick;
|
||||
|
||||
draftedThisRound++;
|
||||
|
||||
@@ -129,6 +134,41 @@ public class LimitedPlayer {
|
||||
recordRemoveFromDraft(bestPick, choice);
|
||||
}
|
||||
|
||||
LimitedPlayer fromPlayer = receivedFrom();
|
||||
// If the previous player has an active Cogwork Spy, show them this card
|
||||
if ((fromPlayer.playerFlags & SpyNextCardDrafted) == SpyNextCardDrafted) {
|
||||
if (fromPlayer instanceof LimitedPlayerAI) {
|
||||
// I'm honestly not sure what the AI would do by learning this information
|
||||
// But just log that a reveal "happened"
|
||||
addLog(this.name() + " revealed a card to " + fromPlayer.name() + " via Cogwork Spy.");
|
||||
} else {
|
||||
addLog(this.name() + " revealed " + bestPick.getName() + " to you with Cogwork Spy.");
|
||||
}
|
||||
|
||||
fromPlayer.playerFlags &= ~SpyNextCardDrafted;
|
||||
}
|
||||
|
||||
if ((playerFlags & SearcherNoteNext) == SearcherNoteNext) {
|
||||
addLog(name() + " revealed " + bestPick.getName() + " for Aether Searcher.");
|
||||
playerFlags &= ~SearcherNoteNext;
|
||||
List<String> note = noted.computeIfAbsent("Aether Searcher", k -> Lists.newArrayList());
|
||||
note.add(String.valueOf(bestPick.getName()));
|
||||
}
|
||||
|
||||
if ((playerFlags & WhispergearBoosterPeek) == WhispergearBoosterPeek) {
|
||||
if (handleWhispergearSneak()) {
|
||||
addLog(name() + " peeked at a booster pack with Whispergear Sneak and turned it face down.");
|
||||
playerFlags &= ~WhispergearBoosterPeek;
|
||||
}
|
||||
}
|
||||
|
||||
if ((playerFlags & IllusionaryInformantPeek) == IllusionaryInformantPeek) {
|
||||
if (handleIllusionaryInformant()) {
|
||||
addLog(name() + " peeked at " + fromPlayer.name() + "'s next pick with Illusionary Informant and turned it face down.");
|
||||
playerFlags &= ~IllusionaryInformantPeek;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedFromPool) {
|
||||
// Can we hide this from UI?
|
||||
return true;
|
||||
@@ -182,7 +222,21 @@ public class LimitedPlayer {
|
||||
addLog(name() + " revealed " + bestPick.getName() + " and noted " + String.join(",", chosenColors) + " chosen colors.");
|
||||
}
|
||||
else {
|
||||
addLog(name() + " revealed " + bestPick.getName() + " as they drafted it.");
|
||||
if (Iterables.contains(draftActions, "You may look at the next card drafted from this booster pack.")) {
|
||||
// Cogwork Spy
|
||||
playerFlags |= SpyNextCardDrafted;
|
||||
}
|
||||
|
||||
if (Iterables.contains(draftActions, "Note the player who passed CARDNAME 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?
|
||||
List<String> note = noted.computeIfAbsent(bestPick.getName(), k -> Lists.newArrayList());
|
||||
note.add(String.valueOf(fromPlayer.order));
|
||||
} else if (Iterables.contains(draftActions, "Reveal the next card you draft and note its name.")) {
|
||||
playerFlags |= SearcherNoteNext;
|
||||
}
|
||||
|
||||
addLog(name() + " revealed " + bestPick.getName() + " as " + name() + " drafted it.");
|
||||
}
|
||||
}
|
||||
if (Iterables.contains(draftActions, "Draft CARDNAME face up.")) {
|
||||
@@ -202,18 +256,14 @@ public class LimitedPlayer {
|
||||
playerFlags |= NobleBanneretActive;
|
||||
} else if (Iterables.contains(draftActions, "As you draft a creature card, you may reveal it, note its creature types, then turn CARDNAME face down.")) {
|
||||
playerFlags |= PalianoVanguardActive;
|
||||
} else if (Iterables.contains(draftActions, "During the draft, you may turn CARDNAME face down. If you do, look at any unopened booster pack in the draft or any booster pack not being looked at by another player.")) {
|
||||
playerFlags |= WhispergearBoosterPeek;
|
||||
// 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.")) {
|
||||
playerFlags |= IllusionaryInformantPeek;
|
||||
}
|
||||
}
|
||||
|
||||
// Note who passed it to you. (Either player before you in draft passing order except if you receive the last card
|
||||
// TODO Cogwork Tracker
|
||||
|
||||
// Note next card on this card
|
||||
// TODO Aether Searcher (for the next card)
|
||||
|
||||
// Peek at next card from this pack
|
||||
// TODO Cogwork Spy
|
||||
|
||||
// TODO Lore Seeker
|
||||
// This adds a pack and MIGHT screw up all of our assumptions about pack passing. Do this last probably
|
||||
|
||||
@@ -221,13 +271,20 @@ public class LimitedPlayer {
|
||||
}
|
||||
|
||||
public void addLog(String message) {
|
||||
this.draft.getDraftLog().addLogEntry(message);
|
||||
if (this.draft.getDraftLog() != null) {
|
||||
this.draft.getDraftLog().addLogEntry(message);
|
||||
}
|
||||
// Mobile doesnt have a draft log yet
|
||||
}
|
||||
|
||||
public List<PaperCard> nextChoice() {
|
||||
return packQueue.peek();
|
||||
}
|
||||
|
||||
public LimitedPlayer receivedFrom() {
|
||||
return draft.getNeighbor(this, draft.getRound() % 2 == 0);
|
||||
}
|
||||
|
||||
public void newPack() {
|
||||
currentPack = order;
|
||||
draftedThisRound = 0;
|
||||
@@ -430,10 +487,65 @@ public class LimitedPlayer {
|
||||
return alreadyRevealed;
|
||||
}
|
||||
|
||||
public boolean handleWhispergearSneak() {
|
||||
if (Objects.equals(SGuiChoose.oneOrNone("Peek at a booster pack with Whispergear Sneak?", Lists.newArrayList("Yes", "No")), "No")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int round = 3;
|
||||
if (draft.getRound() != 3) {
|
||||
round = SGuiChoose.getInteger("Which round would you like to peek at?", draft.getRound(), 3);
|
||||
}
|
||||
|
||||
int playerId = SGuiChoose.getInteger("Which player would you like to peek at?", 0, draft.getOpposingPlayers().length);
|
||||
SGuiChoose.reveal("Peeked booster", peekAtBoosterPack(round, playerId));
|
||||
// This reveal popup doesn't update the card detail panel in draft
|
||||
// How do we get to do that?
|
||||
return true;
|
||||
}
|
||||
|
||||
protected List<PaperCard> peekAtBoosterPack(int round, int playerNumber) {
|
||||
if (draft.getRound() > round) {
|
||||
// There aren't any unopened packs from earlier rounds
|
||||
return null;
|
||||
}
|
||||
|
||||
int relativeRound = round - draft.getRound();
|
||||
LimitedPlayer player;
|
||||
if (playerNumber == 0) {
|
||||
player = this.draft.getHumanPlayer();
|
||||
} else {
|
||||
player = this.draft.getOpposingPlayers()[playerNumber - 1];
|
||||
}
|
||||
if (relativeRound == 0) {
|
||||
// I want to see a pack from the current round
|
||||
return player.packQueue.peek();
|
||||
} else {
|
||||
return player.unopenedPacks.peek();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean handleIllusionaryInformant() {
|
||||
Integer player = SGuiChoose.getInteger("Peek at another player's last pick?", 0, draft.getOpposingPlayers().length);
|
||||
if (Objects.equals(player, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LimitedPlayer peekAt = draft.getPlayer(player);
|
||||
if (peekAt == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SGuiChoose.reveal("Player " + player + " lastPicked: ", Lists.newArrayList(peekAt.getLastPick()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
public void addSingleBoosterPack(boolean random) {
|
||||
// TODO Lore Seeker
|
||||
// Generate booster pack then, insert it "before" the pack we're currently drafting from
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.item.PaperCard;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -141,4 +142,39 @@ public class LimitedPlayerAI extends LimitedPlayer {
|
||||
|
||||
return types.containsAll(notedTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleWhispergearSneak() {
|
||||
// Always choose the next pack I will open
|
||||
// What do I do with this information? Great question. I have no idea.
|
||||
List<PaperCard> cards;
|
||||
if (draft.getRound() == 3) {
|
||||
// Take a peek at the pack you are about to get if its the last round
|
||||
cards = peekAtBoosterPack(this.order, 1);
|
||||
} else {
|
||||
cards = peekAtBoosterPack(this.order, draft.getRound() + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleIllusionaryInformant() {
|
||||
// Always choose the next pack I will open
|
||||
// What do I do with this information? Great question. I have no idea.
|
||||
int player;
|
||||
do {
|
||||
player = MyRandom.getRandom().nextInt(draft.getOpposingPlayers().length + 1);
|
||||
} while(player == this.order);
|
||||
|
||||
|
||||
LimitedPlayer peekAt = draft.getPlayer(player);
|
||||
if (peekAt == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not really sure what the AI does with this information. But its' known now.
|
||||
//peekAt.getLastPick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user