Add Noble Banneret

This commit is contained in:
Chris H
2024-05-27 21:32:32 -04:00
parent 66bd484f27
commit edb85a18c4
5 changed files with 160 additions and 60 deletions

View File

@@ -2107,6 +2107,12 @@ public class CardProperty {
(colors.contains("black") && card.getColor().hasBlack()) || (colors.contains("black") && card.getColor().hasBlack()) ||
(colors.contains("red") && card.getColor().hasRed()) || (colors.contains("red") && card.getColor().hasRed()) ||
(colors.contains("green") && card.getColor().hasGreen()); (colors.contains("green") && card.getColor().hasGreen());
} else if (property.equals("NotedName")) {
String names = sourceController.getDraftNotes().get(spellAbility.getHostCard().getName());
if (names == null || names.isEmpty()) {
return false;
}
return names.contains(card.getName());
} else if (property.startsWith("Triggered")) { } else if (property.startsWith("Triggered")) {
if (spellAbility instanceof SpellAbility) { if (spellAbility instanceof SpellAbility) {
final String key = property.substring(9); final String key = property.substring(9);

View File

@@ -0,0 +1,8 @@
Name:Noble Banneret
ManaCost:2 W W
Types:Creature Human Knight
PT:3/3
Draft:Draft CARDNAME face up.
Draft:As you draft a creature card, you may reveal it, note its name, then turn CARDNAME face down.
S:Mode$ Continuous | Affected$ Card.Self,Creature.NotedName+YouCtrl | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Lifelink | IsPresent$ Creature.NotedName+YouCtrl | PresentCompare$ GE1 | Description$ As long as you control one or more creatures with a name you noted for cards named Noble Banneret, CARDNAME and those creatures get +1/+1 and have lifelink.
Oracle:Draft Noble Banneret face up.\nAs you draft a creature card, you may reveal it, note its name, then turn Noble Banneret face down.\nAs long as you control one or more creatures with a name you noted for cards named Noble Banneret, Noble Banneret and those creatures get +1/+1 and have lifelink.

View File

@@ -248,6 +248,7 @@ Hold the Perimeter
Hymn of the Wilds Hymn of the Wilds
Incendiary Dissent Incendiary Dissent
Natural Unity Natural Unity
Noble Banneret
Pyretic Hunter Pyretic Hunter
Sovereign's Realm Sovereign's Realm
Summoner's Bond Summoner's Bond

View File

@@ -23,15 +23,16 @@ public class LimitedPlayer {
protected Queue<List<PaperCard>> unopenedPacks; protected Queue<List<PaperCard>> unopenedPacks;
protected List<PaperCard> removedFromCardPool = new ArrayList<>(); protected List<PaperCard> removedFromCardPool = new ArrayList<>();
private static final int CantDraftThisRound = 1; private static final int CantDraftThisRound = 1;
private static final int SpyNextCardDrafted = 1 << 1; private static final int SpyNextCardDrafted = 1 << 1;
private static final int ReceiveLastCard = 1 << 2; private static final int ReceiveLastCard = 1 << 2;
private static final int CanRemoveAfterDraft = 1 << 3; private static final int CanRemoveAfterDraft = 1 << 3;
private static final int CanTradeAfterDraft = 1 << 4; private static final int CanTradeAfterDraft = 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 MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted private static final int MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted
| CanTradeAfterDraft | AnimusRemoveFromPool; | CanTradeAfterDraft | AnimusRemoveFromPool | NobleBanneretActive;
private int playerFlags = 0; private int playerFlags = 0;
@@ -90,68 +91,20 @@ public class LimitedPlayer {
return false; return false;
} }
boolean alreadyRevealed = false;
chooseFrom.remove(bestPick); chooseFrom.remove(bestPick);
draftedThisRound++; draftedThisRound++;
if ((playerFlags & AnimusRemoveFromPool) == AnimusRemoveFromPool && if (!handleAnimusOfPredation(bestPick)) {
removeWithAnimus(bestPick)) {
removedFromCardPool.add(bestPick);
addLog(name() + " removed " + bestPick.getName() + " from the draft for Animus of Predation.");
List<String> keywords = new ArrayList<String>();
if (bestPick.getRules().getType().isCreature()) {
for (String keyword : bestPick.getRules().getMainPart().getKeywords()) {
switch (keyword) {
case "Flying":
keywords.add("Flying");
break;
case "First strike":
keywords.add("First Strike");
break;
case "Double strike":
keywords.add("Double Strike");
break;
case "Deathtouch":
keywords.add("Deathtouch");
break;
case "Haste":
keywords.add("Haste");
break;
case "Hexproof":
keywords.add("Hexproof");
break;
case "Indestructible":
keywords.add("Indestructible");
break;
case "Lifelink":
keywords.add("Lifelink");
break;
case "Menace":
keywords.add("Menace");
break;
case "Reach":
keywords.add("Reach");
break;
case "Vigilance":
keywords.add("Vigilance");
break;
}
}
if (!keywords.isEmpty()) {
List<String> note = noted.computeIfAbsent("Animus of Predation", k -> Lists.newArrayList());
note.add(String.join(",", keywords));
addLog(name() + " added " + String.join(",", keywords) + " for Animus of Predation.");
}
}
return true;
} else {
CardPool pool = deck.getOrCreate(section); CardPool pool = deck.getOrCreate(section);
pool.add(bestPick); pool.add(bestPick);
} }
alreadyRevealed |= handleNobleBanneret(bestPick);
if (bestPick.getRules().getMainPart().getDraftActions() == null) { if (bestPick.getRules().getMainPart().getDraftActions() == null) {
return true; return true;
} }
@@ -159,8 +112,10 @@ public class LimitedPlayer {
// Draft Actions // Draft Actions
Iterable<String> draftActions = bestPick.getRules().getMainPart().getDraftActions(); Iterable<String> draftActions = bestPick.getRules().getMainPart().getDraftActions();
if (Iterables.contains(draftActions, "Reveal CARDNAME as you draft it.")) { if (Iterables.contains(draftActions, "Reveal CARDNAME as you draft it.")) {
revealed.add(bestPick); if (!alreadyRevealed) {
showRevealedCard(bestPick); revealed.add(bestPick);
showRevealedCard(bestPick);
}
if (Iterables.contains(draftActions, "Note how many cards you've drafted this draft round, including CARDNAME.")) { 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()); List<String> note = noted.computeIfAbsent(bestPick.getName(), k -> Lists.newArrayList());
@@ -198,15 +153,18 @@ public class LimitedPlayer {
if (Iterables.contains(draftActions, "Draft CARDNAME face up.")) { if (Iterables.contains(draftActions, "Draft CARDNAME face up.")) {
faceUp.add(bestPick); faceUp.add(bestPick);
addLog(name() + " drafted " + bestPick.getName() + " face up."); addLog(name() + " drafted " + bestPick.getName() + " face up.");
showRevealedCard(bestPick); if (!alreadyRevealed) {
showRevealedCard(bestPick);
}
// TODO Noble Banneret
// TODO Paliano Vanguard // TODO Paliano Vanguard
// As you draft a VALID, you may Note its [name/type/], and turn this face down // As you draft a VALID, you may Note its [name/type/], and turn this face down
if (Iterables.contains(draftActions, "As you draft a card, you may remove it from the draft face up. (It isnt in your card pool.)")) { if (Iterables.contains(draftActions, "As you draft a card, you may remove it from the draft face up. (It isnt in your card pool.)")) {
// Animus of Predation // Animus of Predation
playerFlags |= AnimusRemoveFromPool; playerFlags |= AnimusRemoveFromPool;
} else if (Iterables.contains(draftActions, "As you draft a creature card, you may reveal it, note its name, then turn CARDNAME face down.")) {
playerFlags |= NobleBanneretActive;
} }
// As you draft a VALID, you may remove it face up. (It's no longer in your draft pool) // 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? // TODO We need a deck section that's not your sideboard but is your cardpool?
@@ -267,6 +225,10 @@ public class LimitedPlayer {
return SGuiChoose.one("Remove this " + bestPick + " from the draft for ANnimus of Predation?", Lists.newArrayList("Yes", "No")).equals("Yes"); return SGuiChoose.one("Remove this " + bestPick + " from the draft for ANnimus of Predation?", Lists.newArrayList("Yes", "No")).equals("Yes");
} }
protected boolean revealWithBanneret(PaperCard bestPick) {
return SGuiChoose.one("Reveal this " + bestPick + " for Noble Banneret?", Lists.newArrayList("Yes", "No")).equals("Yes");
}
public String name() { public String name() {
if (this instanceof LimitedPlayerAI) { if (this instanceof LimitedPlayerAI) {
return "Player[" + order + "]"; return "Player[" + order + "]";
@@ -280,6 +242,117 @@ public class LimitedPlayer {
} }
public boolean handleAnimusOfPredation(PaperCard bestPick) {
if ((playerFlags & AnimusRemoveFromPool) != AnimusRemoveFromPool) {
return false;
}
if (!removeWithAnimus(bestPick)) {
return false;
}
removedFromCardPool.add(bestPick);
addLog(name() + " removed " + bestPick.getName() + " from the draft for Animus of Predation.");
List<String> keywords = new ArrayList<String>();
if (bestPick.getRules().getType().isCreature()) {
for (String keyword : bestPick.getRules().getMainPart().getKeywords()) {
switch (keyword) {
case "Flying":
keywords.add("Flying");
break;
case "First strike":
keywords.add("First Strike");
break;
case "Double strike":
keywords.add("Double Strike");
break;
case "Deathtouch":
keywords.add("Deathtouch");
break;
case "Haste":
keywords.add("Haste");
break;
case "Hexproof":
keywords.add("Hexproof");
break;
case "Indestructible":
keywords.add("Indestructible");
break;
case "Lifelink":
keywords.add("Lifelink");
break;
case "Menace":
keywords.add("Menace");
break;
case "Reach":
keywords.add("Reach");
break;
case "Vigilance":
keywords.add("Vigilance");
break;
}
}
if (!keywords.isEmpty()) {
List<String> note = noted.computeIfAbsent("Animus of Predation", k -> Lists.newArrayList());
note.add(String.join(",", keywords));
addLog(name() + " added " + String.join(",", keywords) + " for Animus of Predation.");
}
}
return true;
}
public boolean handleNobleBanneret(PaperCard bestPick) {
boolean alreadyRevealed = false;
if ((playerFlags & NobleBanneretActive) != NobleBanneretActive) {
return false;
}
if (!bestPick.getRules().getType().isCreature()) {
return false;
}
boolean remaining = false;
PaperCard found = null;
for(PaperCard c : faceUp) {
if (c.getName().equals("Noble Banneret")) {
if (found == null) {
found = c;
} else {
remaining = true;
break;
}
}
}
if (found == null) {
playerFlags &= ~NobleBanneretActive;
return false;
}
if (!revealWithBanneret(bestPick)) {
return false;
}
// As you draft a creature card, you may reveal it, note its name, then turn CARDNAME face down.
List<String> note = noted.computeIfAbsent(found.getName(), k -> Lists.newArrayList());
revealed.add(bestPick);
note.add(bestPick.getName());
addLog(name() + " revealed " + bestPick.getName() + " and noted its name for Noble Banneret.");
addLog(name() + " has flipped Noble Banneret face down.");
alreadyRevealed = true;
faceUp.remove(found);
if (!remaining) {
playerFlags &= ~NobleBanneretActive;
}
return alreadyRevealed;
}
/* /*
public void addSingleBoosterPack(boolean random) { public void addSingleBoosterPack(boolean random) {
// TODO Lore Seeker // TODO Lore Seeker

View File

@@ -79,4 +79,16 @@ public class LimitedPlayerAI extends LimitedPlayer {
// We should verify we don't already have the keyword bonus that card would grant // We should verify we don't already have the keyword bonus that card would grant
return false; return false;
} }
@Override
protected boolean revealWithBanneret(PaperCard bestPick) {
// Just choose the first creature that we haven't noted yet.
// This is a very simple heuristic, but it's good enough for now.
if (!bestPick.getRules().getType().isCreature()) {
return false;
}
List<String> nobleBanneret = getDraftNotes().getOrDefault("Noble Banneret", null);
return nobleBanneret == null || !nobleBanneret.contains(bestPick.getName());
}
} }