mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Fix drawn card being revealed during ability resolve
This commit is contained in:
@@ -1212,6 +1212,8 @@ public abstract class GameState {
|
|||||||
p.setLandsPlayedThisTurn(landsPlayed);
|
p.setLandsPlayedThisTurn(landsPlayed);
|
||||||
p.setLandsPlayedLastTurn(landsPlayedLastTurn);
|
p.setLandsPlayedLastTurn(landsPlayedLastTurn);
|
||||||
|
|
||||||
|
p.clearPaidForSA();
|
||||||
|
|
||||||
for (Entry<ZoneType, CardCollectionView> kv : playerCards.entrySet()) {
|
for (Entry<ZoneType, CardCollectionView> kv : playerCards.entrySet()) {
|
||||||
PlayerZone zone = p.getZone(kv.getKey());
|
PlayerZone zone = p.getZone(kv.getKey());
|
||||||
if (kv.getKey() == ZoneType.Battlefield) {
|
if (kv.getKey() == ZoneType.Battlefield) {
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ public class Game {
|
|||||||
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
|
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
|
||||||
|
|
||||||
private Map<Player, Card> topLibsCast = Maps.newHashMap();
|
private Map<Player, Card> topLibsCast = Maps.newHashMap();
|
||||||
|
private Map<Card, Integer> facedownWhileCasting = Maps.newHashMap();
|
||||||
|
|
||||||
private Player monarch = null;
|
private Player monarch = null;
|
||||||
private Player monarchBeginTurn = null;
|
private Player monarchBeginTurn = null;
|
||||||
@@ -1113,7 +1114,29 @@ public class Game {
|
|||||||
topLibsCast.put(p, p.getTopXCardsFromLibrary(1).isEmpty() ? null : p.getTopXCardsFromLibrary(1).get(0));
|
topLibsCast.put(p, p.getTopXCardsFromLibrary(1).isEmpty() ? null : p.getTopXCardsFromLibrary(1).get(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void clearTopLibsCast() {
|
public void clearTopLibsCast(SpellAbility sa) {
|
||||||
|
// if nothing left to pay
|
||||||
|
if (sa.getActivatingPlayer().getPaidForSA() == null) {
|
||||||
topLibsCast.clear();
|
topLibsCast.clear();
|
||||||
|
for (Card c : facedownWhileCasting.keySet()) {
|
||||||
|
// maybe it was discarded as payment?
|
||||||
|
if (c.isInZone(ZoneType.Hand)) {
|
||||||
|
c.forceTurnFaceUp();
|
||||||
|
|
||||||
|
// run triggers for cards that need reveal
|
||||||
|
final Map<AbilityKey, Object> runParams = Maps.newHashMap();
|
||||||
|
runParams.put(AbilityKey.Card, c);
|
||||||
|
runParams.put(AbilityKey.Number, facedownWhileCasting.get(c));
|
||||||
|
runParams.put(AbilityKey.Player, this);
|
||||||
|
runParams.put(AbilityKey.CanReveal, true);
|
||||||
|
// need to hold trigger to clear list first
|
||||||
|
getTriggerHandler().runTrigger(TriggerType.Drawn, runParams, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
facedownWhileCasting.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void addFacedownWhileCasting(Card c, int numDrawn) {
|
||||||
|
facedownWhileCasting.put(c, Integer.valueOf(numDrawn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public enum AbilityKey {
|
|||||||
AttackedTarget("AttackedTarget"),
|
AttackedTarget("AttackedTarget"),
|
||||||
Blocker("Blocker"),
|
Blocker("Blocker"),
|
||||||
Blockers("Blockers"),
|
Blockers("Blockers"),
|
||||||
|
CanReveal("CanReveal"),
|
||||||
CastSA("CastSA"),
|
CastSA("CastSA"),
|
||||||
CastSACMC("CastSACMC"),
|
CastSACMC("CastSACMC"),
|
||||||
Card("Card"),
|
Card("Card"),
|
||||||
|
|||||||
@@ -1219,20 +1219,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean canDraw() {
|
|
||||||
if (hasKeyword("You can't draw cards.")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (hasKeyword("You can't draw more than one card each turn.")) {
|
|
||||||
return numDrawnThisTurn < 1;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final CardCollectionView drawCard() {
|
|
||||||
return drawCards(1, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void surveil(int num, SpellAbility cause, CardZoneTable table) {
|
public void surveil(int num, SpellAbility cause, CardZoneTable table) {
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
|
||||||
repParams.put(AbilityKey.Source, cause);
|
repParams.put(AbilityKey.Source, cause);
|
||||||
@@ -1300,12 +1286,25 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return !getZone(ZoneType.Hand).isEmpty();
|
return !getZone(ZoneType.Hand).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean canDraw() {
|
||||||
|
if (hasKeyword("You can't draw cards.")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (hasKeyword("You can't draw more than one card each turn.")) {
|
||||||
|
return numDrawnThisTurn < 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final CardCollectionView drawCard() {
|
||||||
|
return drawCards(1, null);
|
||||||
|
}
|
||||||
|
|
||||||
public final CardCollectionView drawCards(final int n) {
|
public final CardCollectionView drawCards(final int n) {
|
||||||
return drawCards(n, null);
|
return drawCards(n, null);
|
||||||
}
|
}
|
||||||
public final CardCollectionView drawCards(final int n, SpellAbility cause) {
|
public final CardCollectionView drawCards(final int n, SpellAbility cause) {
|
||||||
final CardCollection drawn = new CardCollection();
|
final CardCollection drawn = new CardCollection();
|
||||||
final Map<Player, CardCollection> toReveal = Maps.newHashMap();
|
|
||||||
|
|
||||||
// Replacement effects
|
// Replacement effects
|
||||||
final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(this);
|
final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(this);
|
||||||
@@ -1317,6 +1316,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
// always allow drawing cards before the game actually starts (e.g. Maralen of the Mornsong Avatar)
|
// always allow drawing cards before the game actually starts (e.g. Maralen of the Mornsong Avatar)
|
||||||
final boolean gameStarted = game.getAge().ordinal() > GameStage.Mulligan.ordinal();
|
final boolean gameStarted = game.getAge().ordinal() > GameStage.Mulligan.ordinal();
|
||||||
|
final Map<Player, CardCollection> toReveal = Maps.newHashMap();
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
if (gameStarted && !canDraw()) {
|
if (gameStarted && !canDraw()) {
|
||||||
@@ -1358,7 +1358,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Player> pList = Lists.newArrayList();
|
List<Player> pList = Lists.newArrayList();
|
||||||
|
|
||||||
for (Player p : getAllOtherPlayers()) {
|
for (Player p : getAllOtherPlayers()) {
|
||||||
if (c.mayPlayerLook(p)) {
|
if (c.mayPlayerLook(p)) {
|
||||||
pList.add(p);
|
pList.add(p);
|
||||||
@@ -1385,8 +1384,16 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
view.updateNumDrawnThisTurn(this);
|
view.updateNumDrawnThisTurn(this);
|
||||||
|
|
||||||
// Run triggers
|
|
||||||
final Map<AbilityKey, Object> runParams = Maps.newHashMap();
|
final Map<AbilityKey, Object> runParams = Maps.newHashMap();
|
||||||
|
|
||||||
|
// CR 121.8 card was drawn as part of another sa (e.g. paying with Chromantic Sphere), hide it temporarily
|
||||||
|
if (game.getTopLibForPlayer(this) != null && getPaidForSA() != null && cause != null && getPaidForSA() != cause.getRootAbility()) {
|
||||||
|
c.turnFaceDown();
|
||||||
|
game.addFacedownWhileCasting(c, numDrawnThisTurn);
|
||||||
|
runParams.put(AbilityKey.CanReveal, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run triggers
|
||||||
runParams.put(AbilityKey.Card, c);
|
runParams.put(AbilityKey.Card, c);
|
||||||
runParams.put(AbilityKey.Number, numDrawnThisTurn);
|
runParams.put(AbilityKey.Number, numDrawnThisTurn);
|
||||||
runParams.put(AbilityKey.Player, this);
|
runParams.put(AbilityKey.Player, this);
|
||||||
@@ -3167,6 +3174,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
// it could be empty if spell couldn't be cast
|
// it could be empty if spell couldn't be cast
|
||||||
paidForStack.poll();
|
paidForStack.poll();
|
||||||
}
|
}
|
||||||
|
public void clearPaidForSA() {
|
||||||
|
paidForStack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMonarch() {
|
public boolean isMonarch() {
|
||||||
return equals(game.getMonarch());
|
return equals(game.getMonarch());
|
||||||
|
|||||||
@@ -86,6 +86,18 @@ public class TriggerDrawn extends Trigger {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runParams.containsKey(AbilityKey.CanReveal)) {
|
||||||
|
// while drawing this is only set if false
|
||||||
|
boolean canReveal = (boolean) runParams.get(AbilityKey.CanReveal);
|
||||||
|
if (hasParam("ForReveal")) {
|
||||||
|
if (!canReveal) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (canReveal) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:2 U U
|
|||||||
Types:Legendary Creature Zombie God
|
Types:Legendary Creature Zombie God
|
||||||
PT:4/5
|
PT:4/5
|
||||||
K:Flying
|
K:Flying
|
||||||
T:Mode$ Drawn | ValidCard$ Card.YouOwn | Number$ 1 | OptionalDecider$ You | Static$ True | Execute$ DBReveal | TriggerZones$ Battlefield | TriggerDescription$ You may reveal the first card you draw each turn as you draw it. Whenever you reveal an instant or sorcery card this way, copy that card and you may cast the copy. That copy costs {2} less to cast.
|
T:Mode$ Drawn | ValidCard$ Card.YouOwn | Number$ 1 | OptionalDecider$ You | Static$ True | ForReveal$ True | Execute$ DBReveal | TriggerZones$ Battlefield | TriggerDescription$ You may reveal the first card you draw each turn as you draw it. Whenever you reveal an instant or sorcery card this way, copy that card and you may cast the copy. That copy costs {2} less to cast.
|
||||||
SVar:DBReveal:DB$ Reveal | Defined$ You | RevealDefined$ TriggeredCard | RememberRevealed$ True | SubAbility$ DBTrigger | AILogic$ Kefnet
|
SVar:DBReveal:DB$ Reveal | Defined$ You | RevealDefined$ TriggeredCard | RememberRevealed$ True | SubAbility$ DBTrigger | AILogic$ Kefnet
|
||||||
SVar:DBTrigger:DB$ ImmediateTrigger | RememberObjects$ RememberedCard | ConditionDefined$ Remembered | ConditionPresent$ Instant,Sorcery | SubAbility$ DBCleanup | Execute$ DBPlay | TriggerDescription$ Whenever you reveal an instant or sorcery card this way, copy that card and you may cast the copy. That copy costs {2} less to cast.
|
SVar:DBTrigger:DB$ ImmediateTrigger | RememberObjects$ RememberedCard | ConditionDefined$ Remembered | ConditionPresent$ Instant,Sorcery | SubAbility$ DBCleanup | Execute$ DBPlay | TriggerDescription$ Whenever you reveal an instant or sorcery card this way, copy that card and you may cast the copy. That copy costs {2} less to cast.
|
||||||
SVar:DBPlay:DB$ Play | Defined$ DelayTriggerRemembered | ValidSA$ Spell | PlayReduceCost$ 2 | Optional$ True | CopyCard$ True
|
SVar:DBPlay:DB$ Play | Defined$ DelayTriggerRemembered | ValidSA$ Spell | PlayReduceCost$ 2 | Optional$ True | CopyCard$ True
|
||||||
@@ -11,4 +11,3 @@ SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
|||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard,Exile | ValidCard$ Card.Self | Execute$ TriReturn | OptionalDecider$ You | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies or is put into exile from the battlefield, you may put it into its owner's library third from the top.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard,Exile | ValidCard$ Card.Self | Execute$ TriReturn | OptionalDecider$ You | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies or is put into exile from the battlefield, you may put it into its owner's library third from the top.
|
||||||
SVar:TriReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Destination$ Library | LibraryPosition$ 2
|
SVar:TriReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Destination$ Library | LibraryPosition$ 2
|
||||||
Oracle:Flying\nYou may reveal the first card you draw each turn as you draw it. Whenever you reveal an instant or sorcery card this way, copy that card and you may cast the copy. That copy costs {2} less to cast.\nWhen God-Eternal Kefnet dies or is put into exile from the battlefield, you may put it into its owner's library third from the top.
|
Oracle:Flying\nYou may reveal the first card you draw each turn as you draw it. Whenever you reveal an instant or sorcery card this way, copy that card and you may cast the copy. That copy costs {2} less to cast.\nWhen God-Eternal Kefnet dies or is put into exile from the battlefield, you may put it into its owner's library third from the top.
|
||||||
|
|
||||||
|
|||||||
@@ -356,12 +356,10 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
|||||||
player.getManaPool().payManaFromAbility(saPaidFor, InputPayMana.this.manaCost, chosen);
|
player.getManaPool().payManaFromAbility(saPaidFor, InputPayMana.this.manaCost, chosen);
|
||||||
}
|
}
|
||||||
onManaAbilityPaid();
|
onManaAbilityPaid();
|
||||||
onStateChanged();
|
}
|
||||||
} else {
|
|
||||||
// Need to call this to unlock
|
// Need to call this to unlock
|
||||||
onStateChanged();
|
onStateChanged();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ public abstract class InputSyncronizedBase extends InputBase implements InputSyn
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected final void stop() {
|
protected final void stop() {
|
||||||
|
onStop();
|
||||||
|
|
||||||
// ensure input won't accept any user actions.
|
// ensure input won't accept any user actions.
|
||||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -44,8 +46,6 @@ public abstract class InputSyncronizedBase extends InputBase implements InputSyn
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onStop();
|
|
||||||
|
|
||||||
// thread irrelevant
|
// thread irrelevant
|
||||||
if (getController().getInputQueue().getInput() != null) {
|
if (getController().getInputQueue().getInput() != null) {
|
||||||
getController().getInputQueue().removeInput(InputSyncronizedBase.this);
|
getController().getInputQueue().removeInput(InputSyncronizedBase.this);
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ public class HumanPlaySpellAbility {
|
|||||||
manapool.restoreColorReplacements();
|
manapool.restoreColorReplacements();
|
||||||
human.decNumManaConversion();
|
human.decNumManaConversion();
|
||||||
}
|
}
|
||||||
game.clearTopLibsCast();
|
game.clearTopLibsCast(ability);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ public class HumanPlaySpellAbility {
|
|||||||
manapool.restoreColorReplacements();
|
manapool.restoreColorReplacements();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.clearTopLibsCast();
|
game.clearTopLibsCast(ability);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user