More draft matters

This commit is contained in:
Chris H
2024-05-20 20:23:37 -04:00
parent 4310fecc8b
commit 799fd4cc51
13 changed files with 212 additions and 79 deletions

View File

@@ -336,7 +336,10 @@ public enum DeckFormat {
return TextUtil.concatWithSpace("contains a Custom Card:", cp.getKey(), "\nPlease Enable Custom Cards in Forge Preferences to use this deck.");
// Might cause issues since it ignores "Special" Cards
if (simpleCard == null) {
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
simpleCard = StaticData.instance().getVariantCards().getCard(cp.getKey());
if (simpleCard == null) {
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
}
}
if (canHaveAnyNumberOf(simpleCard)) {

View File

@@ -9,7 +9,10 @@ import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
import forge.game.*;
import forge.game.CardTraitBase;
import forge.game.EvenOdd;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.card.CardPredicates.Presets;
@@ -2094,6 +2097,16 @@ public class CardProperty {
}
}
return false;
} else if (property.equals("NotedColor")) {
String colors = sourceController.getDraftNotes().get(spellAbility.getHostCard().getName());
if (colors == null) {
return false;
}
return (colors.contains("white") && card.getColor().hasWhite()) ||
(colors.contains("blue") && card.getColor().hasBlue()) ||
(colors.contains("black") && card.getColor().hasBlack()) ||
(colors.contains("red") && card.getColor().hasRed()) ||
(colors.contains("green") && card.getColor().hasGreen());
} else if (property.startsWith("Triggered")) {
if (spellAbility instanceof SpellAbility) {
final String key = property.substring(9);

View File

@@ -17,13 +17,7 @@
*/
package forge.game.spellability;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaAtom;
@@ -48,9 +42,13 @@ import forge.game.replacement.ReplacementType;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
/**
* <p>
@@ -524,6 +522,9 @@ public class AbilityManaPart implements java.io.Serializable {
return getComboColors(sa);
}
String produced = this.getOrigProduced();
if (produced.contains("NotedColor")) {
produced = produced.replace("NotedColor", sa.getActivatingPlayer().getDraftNotes().get(""));
}
if (produced.contains("Chosen")) {
produced = produced.replace("Chosen", this.getChosenColor(sa));
}
@@ -663,6 +664,23 @@ public class AbilityManaPart implements java.io.Serializable {
if (origProduced.contains("Chosen")) {
origProduced = origProduced.replace("Chosen", getChosenColor(sa));
}
if (origProduced.contains("NotedColors")) {
String colors = sa.getActivatingPlayer().getDraftNotes().get(sa.getHostCard().getName());
if (colors == null) {
return "";
}
// Colors here is an comma separated color list, potentially with duplicates
// We need to remove duplicates and convert to single letters
StringBuilder sb = new StringBuilder();
for (String color : colors.split(",")) {
String shortColor = MagicColor.toShortString(color);
if (sb.indexOf(shortColor) == -1) {
sb.append(shortColor).append(" ");
}
}
origProduced = origProduced.replace("NotedColors", sb.toString().trim());
}
if (!origProduced.contains("ColorIdentity")) {
return TextUtil.fastReplace(origProduced, "Combo ", "");
}

View File

@@ -17,9 +17,6 @@
*/
package forge.screens.deckeditor.controllers;
import java.util.HashSet;
import java.util.Map.Entry;
import forge.Singletons;
import forge.deck.Deck;
import forge.deck.DeckGroup;
@@ -43,6 +40,9 @@ import forge.toolbox.FOptionPane;
import forge.util.ItemPool;
import forge.util.Localizer;
import java.util.HashSet;
import java.util.Map.Entry;
/**
* Updates the deck editor UI as necessary draft selection mode.
*
@@ -132,6 +132,9 @@ public class CEditorDraftingProcess extends ACEditorBase<PaperCard, DeckGroup> i
this.showChoices(pool);
}
else {
// TODO Deal Broker
// Offer trades before saving
this.saveDraft();
}
}

View File

@@ -1,19 +1,18 @@
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;
import forge.deck.Deck;
import forge.game.card.Card;
import forge.gamemodes.limited.IBoosterDraft;
import forge.gamemodes.limited.IDraftLog;
import forge.gamemodes.limited.LimitedPlayer;
import forge.item.PaperCard;
import forge.item.SealedProduct;
import forge.item.generation.BoosterGenerator;
import forge.model.FModel;
import org.testng.annotations.Test;
import java.util.List;
/**
* <p>
@@ -91,4 +90,9 @@ public class BoosterDraftTest implements IBoosterDraft {
public IDraftLog getDraftLog() {
return null;
}
@Override
public LimitedPlayer getNeighbor(LimitedPlayer p, boolean left) {
return null;
}
}

View File

@@ -0,0 +1,8 @@
Name:Paliano, the High City
Types:Legendary Land
Draft:Reveal CARDNAME as you draft it.
Draft:As you draft CARDNAME, the player to your right chooses a color, you choose another color, then the player to your left chooses a third color.
A:AB$ Mana | Cost$ T | Produced$ Combo NotedColors | Condition$ Add one mana of any color chosen as you drafted cards named CARDNAME.
Oracle:Reveal CARDNAME as you draft it. As you draft CARDNAME, the player to your right chooses a color, you choose another color, then the player to your left chooses a third color.\n{T}: Add one mana of any color chosen as you drafted cards named Paliano, the High City.

View File

@@ -0,0 +1,7 @@
Name:Regicide
ManaCost:B
Types:Instant
Draft:Reveal CARDNAME as you draft it.
Draft:As you draft CARDNAME, the player to your right chooses a color, you choose another color, then the player to your left chooses a third color.
A:SP$ Destroy | ValidTgts$ Creature.NotedColor | SpellDescription$ Destroy target creature thats one or more of the colors chosen as you drafted cards named CARDNAME.
Oracle:Reveal Regicide as you draft it. The player to your right chooses a color, you choose another color, then the player to your left chooses a third color.\nDestroy target creature thats one or more of the colors chosen as you drafted cards named Regicide.

View File

@@ -17,22 +17,9 @@
*/
package forge.gamemodes.limited;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.TreeMap;
import org.apache.commons.lang3.ArrayUtils;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import forge.StaticData;
import forge.card.CardEdition;
import forge.deck.CardPool;
@@ -53,6 +40,10 @@ import forge.util.ItemPool;
import forge.util.Localizer;
import forge.util.TextUtil;
import forge.util.storage.IStorage;
import org.apache.commons.lang3.ArrayUtils;
import java.io.File;
import java.util.*;
/**
* Booster Draft Format.
@@ -294,6 +285,11 @@ public class BoosterDraft implements IBoosterDraft {
return draftLog;
}
@Override
public LimitedPlayer getNeighbor(LimitedPlayer player, boolean left) {
return players.get((player.order + (left ? -1 : 1) + N_PLAYERS) % N_PLAYERS);
}
private void setupCustomDraft(final CustomLimited draft) {
final ItemPool<PaperCard> dPool = draft.getCardPool();
if (dPool == null) {
@@ -451,6 +447,7 @@ public class BoosterDraft implements IBoosterDraft {
// Loop through players 1-7 to draft their current pack
for (int i = 1; i < N_PLAYERS; i++) {
LimitedPlayer pl = this.players.get(i);
// TODO Agent of Acquisitions activation to loop the entire pack?
pl.draftCard(pl.chooseCard());
}
}
@@ -483,6 +480,8 @@ public class BoosterDraft implements IBoosterDraft {
recordDraftPick(thisBooster, c);
// TODO Agent of Acquisitions activation to loop the entire pack?
this.localPlayer.draftCard(c);
this.currentBoosterPick++;
this.passPacks();

View File

@@ -17,12 +17,12 @@
*/
package forge.gamemodes.limited;
import java.util.List;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.item.IPaperCard;
import java.util.List;
public class DeckColors {
protected ColorSet chosen;
@@ -30,6 +30,13 @@ public class DeckColors {
public int MAX_COLORS = 2;
DeckColors() {}
DeckColors(int max_col) {
// If we want to draft decks that are more than 2 colors, we can change the MAX_COLORS value here.
MAX_COLORS = max_col;
}
public ColorSet getChosenColors() {
if (null == chosen) {
chosen = ColorSet.fromMask(colorMask);

View File

@@ -46,4 +46,5 @@ public interface IBoosterDraft {
void setLogEntry(IDraftLog draftingProcess);
IDraftLog getDraftLog();
LimitedPlayer getNeighbor(LimitedPlayer p, boolean left);
}

View File

@@ -2,9 +2,11 @@ package forge.gamemodes.limited;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.card.MagicColor;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.gui.util.SGuiChoose;
import forge.item.PaperCard;
import forge.util.TextUtil;
@@ -20,22 +22,23 @@ public class LimitedPlayer {
protected Queue<List<PaperCard>> packQueue;
protected Queue<List<PaperCard>> unopenedPacks;
protected List<PaperCard> removedFromCardPool = new ArrayList<>();
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 final int AnimusRemoveFromPool = 1 << 5;
private static final int MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted
| CanTradeAfterDraft;
| CanTradeAfterDraft | AnimusRemoveFromPool;
private final int playerFlags = 0;
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 Map<DraftPower, Integer> powers = new HashMap<>();
IBoosterDraft draft = null;
@@ -61,7 +64,9 @@ public class LimitedPlayer {
}
public PaperCard chooseCard() {
// A basic LimitedPlayer chooses cards via the UI instead of this function
// A non-AI LimitedPlayer chooses cards via the UI instead of this function
// TODO Archdemon of Paliano random draft while active
return null;
}
@@ -80,8 +85,15 @@ public class LimitedPlayer {
chooseFrom.remove(bestPick);
CardPool pool = deck.getOrCreate(section);
pool.add(bestPick);
if ((playerFlags & AnimusRemoveFromPool) == AnimusRemoveFromPool &&
removeWithAnimus(bestPick)) {
removedFromCardPool.add(bestPick);
return true;
} else {
CardPool pool = deck.getOrCreate(section);
pool.add(bestPick);
}
draftedThisRound++;
if (bestPick.getRules().getMainPart().getDraftActions() == null) {
@@ -92,22 +104,73 @@ public class LimitedPlayer {
Iterable<String> draftActions = bestPick.getRules().getMainPart().getDraftActions();
if (Iterables.contains(draftActions, "Reveal CARDNAME as you draft it.")) {
revealed.add(bestPick);
showRevealedCard(bestPick);
if (Iterables.contains(draftActions, "Note how many cards you've drafted this draft round, including CARDNAME.")) {
List<String> 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 {
} else if (Iterables.contains(draftActions, "As you draft CARDNAME, the player to your right chooses a color, you choose another color, then the player to your left chooses a third color.")) {
List<String> chosenColors = new ArrayList<String>();
LimitedPlayer leftPlayer = draft.getNeighbor(this, true);
LimitedPlayer rightPlayer = draft.getNeighbor(this, false);
List<String> availableColors = new ArrayList<String>(MagicColor.Constant.ONLY_COLORS);
String c = rightPlayer.chooseColor(availableColors, this, bestPick.getName());
chosenColors.add(c);
availableColors.remove(c);
c = this.chooseColor(availableColors, this, bestPick.getName());
chosenColors.add(c);
availableColors.remove(c);
c = leftPlayer.chooseColor(availableColors, this, bestPick.getName());
chosenColors.add(c);
availableColors.remove(c);
List<String> note = noted.computeIfAbsent(bestPick.getName(), k -> Lists.newArrayList());
note.add(String.join(",", chosenColors));
addLog(name() + " revealed " + bestPick.getName() + " and noted " + chosenColors + " chosen.");
}
else {
addLog(name() + " revealed " + bestPick.getName() + " as they drafted it.");
}
}
if (Iterables.contains(draftActions, "Draft CARDNAME face up.")) {
faceUp.add(bestPick);
addLog(name() + " drafted " + bestPick.getName() + " face up.");
showRevealedCard(bestPick);
// Colors
// TODO Note Paliano, the High City
// TODO Note Regicide
// TODO Note Paliano Vanguard
// TODO Note Aether Searcher (for the next card)
// TODO Noble Banneret
// TODO Paliano Vanguard
// As you draft a VALID, you may Note its [name/type/], and turn this face down
// TODO Animus of Predation
if (Iterables.contains(draftActions, "As you draft a card, you may remove it from the draft face up. (It isnt in your card pool.)")) {
playerFlags |= AnimusRemoveFromPool;
}
// As you draft a VALID, you may remove it face up. (It's no longer in your draft pool)
// TODO We need a deck section that's not your sideboard but is your cardpool?
// Keyword absorption: If creature is absorbed, it gains all the abilities of the creature it absorbed. This includes
// flying, first strike, double strike, deathtouch, haste, hexproof, indestructible, lifelink, menace, reach, and vigilance.
}
// 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
return true;
}
@@ -141,6 +204,14 @@ public class LimitedPlayer {
packQueue.add(pack);
}
protected String chooseColor(List<String> colors, LimitedPlayer player, String title) {
return SGuiChoose.one(player.name() + " drafted " + title + ": Choose a color", colors);
}
protected boolean removeWithAnimus(PaperCard bestPick) {
return SGuiChoose.one("Remove this " + bestPick + " from the draft for ANnimus of Predation?", Lists.newArrayList("Yes", "No")).equals("Yes");
}
public String name() {
if (this instanceof LimitedPlayerAI) {
return "Player[" + order + "]";
@@ -149,41 +220,15 @@ public class LimitedPlayer {
return "You";
}
public void showRevealedCard(PaperCard pick) {
// TODO Show the revealed card in the CardDetailPanel
}
/*
public void addSingleBoosterPack(boolean random) {
// TODO Lore Seeker
// Generate booster pack then, "receive" that pack
}
public boolean activatePower(DraftPower power) {
if (!powers.containsKey(power)) {
return false;
}
int i = (int)powers.get(power);
if (i == 1) {
powers.remove(power);
} else {
powers.put(power, i-1);
}
power.activate(this);
return true;
}
public boolean noteObject(String cardName, Object notedObj) {
// Returns boolean based on creation of new mapped param
boolean alreadyContained = noted.containsKey(cardName);
if (alreadyContained) {
noted.get(cardName).add(notedObj);
} else {
noted.put(cardName, Lists.newArrayList(notedObj));
}
return !alreadyContained;
// Generate booster pack then, insert it "before" the pack we're currently drafting from
}
*/
}

View File

@@ -1,7 +1,5 @@
package forge.gamemodes.limited;
import java.util.List;
import forge.card.ColorSet;
import forge.deck.CardPool;
import forge.deck.Deck;
@@ -9,6 +7,9 @@ import forge.deck.DeckSection;
import forge.item.PaperCard;
import forge.localinstance.properties.ForgePreferences;
import java.util.Collections;
import java.util.List;
public class LimitedPlayerAI extends LimitedPlayer {
protected DeckColors deckCols;
@@ -33,6 +34,9 @@ public class LimitedPlayerAI extends LimitedPlayer {
System.out.println("Player[" + order + "] pack: " + chooseFrom.toString());
}
// TODO Archdemon of Paliano random draft while active
final ColorSet chosenColors = deckCols.getChosenColors();
final boolean canAddMoreColors = deckCols.canChoseMoreColors();
@@ -54,4 +58,25 @@ public class LimitedPlayerAI extends LimitedPlayer {
CardPool section = deck.getOrCreate(DeckSection.Sideboard);
return new BoosterDeckBuilder(section.toFlatList(), deckCols).buildDeck(landSetCode);
}
@Override
protected String chooseColor(List<String> colors, LimitedPlayer player, String title) {
if (player.equals(this)) {
// For Paliano, choose one of my colors
// For Regicie, random is fine?
} else {
// For Paliano, if player has revealed anything, try to avoid that color
// For Regicide, don't choose one of my colors
}
Collections.shuffle(colors);
return colors.get(0);
}
@Override
protected boolean removeWithAnimus(PaperCard bestPick) {
// TODO Animus of Predation logic
// Feel free to remove any cards that we won't play and can give us a bonus
// We should verify we don't already have the keyword bonus that card would grant
return false;
}
}