diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index d3d734c62e5..dfb054f81ec 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -783,6 +783,7 @@ public abstract class GameState { Card exiledWith = idToCard.get(Integer.parseInt(id)); c.setExiledWith(exiledWith); + c.setExiledBy(exiledWith.getController()); } } @@ -1241,6 +1242,7 @@ public abstract class GameState { saAdventure.setActivatingPlayer(c.getOwner()); saAdventure.resolve(); c.setExiledWith(c); // This seems to be the way it's set up internally. Potentially not needed here? + c.setExiledBy(c.getController()); } else if (info.startsWith("IsCommander")) { // TODO: This doesn't seem to properly restore the ability to play the commander. Why? c.setCommander(true); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 1b3cfb1a8fd..c92246ab804 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -302,6 +302,7 @@ public class GameAction { zoneFrom.remove(c); if (!zoneTo.is(ZoneType.Exile) && !zoneTo.is(ZoneType.Stack)) { c.setExiledWith(null); + c.setExiledBy(null); } // cleanup Encoding diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java index c9ad6dd4e02..911eab6e8dc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java @@ -188,6 +188,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { host = sa.getHostCard(); } movedCard.setExiledWith(host); + movedCard.setExiledBy(host.getController()); } if (sa.hasParam("ExileFaceDown")) { movedCard.turnFaceDown(true); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index 9dc3b68d0e5..40c0b3d88d6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -667,6 +667,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { host = sa.getHostCard(); } gameCard.setExiledWith(host); + gameCard.setExiledBy(host.getController()); } movedCard = game.getAction().moveTo(destination, gameCard, cause); if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) { @@ -687,6 +688,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect { // might set after card is moved again if something has changed if (destination.equals(ZoneType.Exile) && !movedCard.isToken()) { movedCard.setExiledWith(host); + if (host != null) { + movedCard.setExiledBy(host.getController()); + } } if (sa.hasParam("ExileFaceDown")) { @@ -1214,6 +1218,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { host = sa.getHostCard(); } movedCard.setExiledWith(host); + movedCard.setExiledBy(host.getController()); } if (sa.hasParam("ExileFaceDown")) { movedCard.turnFaceDown(true); @@ -1319,6 +1324,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } movedCard = game.getAction().exile(tgtHost, srcSA, params); movedCard.setExiledWith(host); + movedCard.setExiledBy(host.getController()); } else if (srcSA.getParam("Destination").equals("TopOfLibrary")) { movedCard = game.getAction().moveToLibrary(tgtHost, srcSA, params); } else if (srcSA.getParam("Destination").equals("Hand")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 24a9c9fd7d8..bac32af3f60 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -318,7 +318,12 @@ public class DigEffect extends SpellAbilityEffect { combatChanged = true; } } else if (destZone1.equals(ZoneType.Exile)) { + if (sa.hasParam("ExileWithCounter")) { + c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), + 1, player, true, counterTable); + } c.setExiledWith(effectHost); + c.setExiledBy(effectHost.getController()); } } if (!origin.equals(c.getZone().getZoneType())) { @@ -387,6 +392,7 @@ public class DigEffect extends SpellAbilityEffect { 1, player, true, counterTable); } c.setExiledWith(effectHost); + c.setExiledBy(effectHost.getController()); } } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 8a6cfdc0d04..6f2dfea20dd 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -239,6 +239,7 @@ public class Card extends GameEntity implements Comparable { private String chosenMode = ""; private Card exiledWith = null; + private Player exiledBy = null; private Map goad = Maps.newTreeMap(); @@ -1463,6 +1464,11 @@ public class Card extends GameEntity implements Comparable { exiledWith = e; } + public final Player getExiledBy() { return exiledBy; } + public final void setExiledBy(final Player ep) { + exiledBy = ep; + } + // used for cards like Belbe's Portal, Conspiracy, Cover of Darkness, etc. public final String getChosenType() { return chosenType; diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 9fe582a0eff..6b952216b37 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -350,6 +350,13 @@ public class CardProperty { if (!card.equals(source)) { return false; } + } else if (property.startsWith("ExiledByYou")) { + if (card.getExiledBy() == null) { + return false; + } + if (!card.getExiledBy().equals(sourceController)) { + return false; + } } else if (property.startsWith("ExiledWithSource")) { if (card.getExiledWith() == null) { return false; diff --git a/forge-game/src/main/java/forge/game/card/CounterEnumType.java b/forge-game/src/main/java/forge/game/card/CounterEnumType.java index 397959482fd..722180488e4 100644 --- a/forge-game/src/main/java/forge/game/card/CounterEnumType.java +++ b/forge-game/src/main/java/forge/game/card/CounterEnumType.java @@ -105,6 +105,8 @@ public enum CounterEnumType { FEATHER("FTHR", 195, 202, 165), + FETCH("FETCH", 180, 235, 52), + FILIBUSTER("FLBTR", 255, 179, 119), FLAME("FLAME", 255, 143, 43), diff --git a/forge-gui/res/cardsfolder/h/haldan_avid_arcanist.txt b/forge-gui/res/cardsfolder/h/haldan_avid_arcanist.txt new file mode 100755 index 00000000000..a657e06cafa --- /dev/null +++ b/forge-gui/res/cardsfolder/h/haldan_avid_arcanist.txt @@ -0,0 +1,9 @@ +Name:Haldan, Avid Arcanist +ManaCost:2 U +Types:Legendary Creature Human Wizard +PT:1/4 +K:Partner:Pako, Arcane Retriever:Pako +SVar:PlayMain1:TRUE +S:Mode$ Continuous | Affected$ Card.nonCreature+ExiledByYou+counters_GE1_FETCH | AffectedZone$ Exile | MayPlay$ True | MayPlayIgnoreColor$ True | Description$ You may play noncreature cards from exile with fetch counters on them if you exiled them, and you may spend mana as though it were mana of any color to cast those spells. +DeckNeeds:Name$Pako, Arcane Retriever +Oracle:Partner with Pako, Arcane Retriever (When this creature enters the battlefield, target player may put Pako into their hand from their library, then shuffle.)\nYou may play noncreature cards from exile with fetch counters on them if you exiled them, and you may spend mana as though it were mana of any color to cast those spells. diff --git a/forge-gui/res/cardsfolder/p/pako_arcane_retriever.txt b/forge-gui/res/cardsfolder/p/pako_arcane_retriever.txt new file mode 100755 index 00000000000..9e77d5a4eab --- /dev/null +++ b/forge-gui/res/cardsfolder/p/pako_arcane_retriever.txt @@ -0,0 +1,15 @@ +Name:Pako, Arcane Retriever +ManaCost:3 R G +Types:Legendary Creature Elemental Hound +PT:3/3 +K:Partner:Haldan, Avid Arcanist:Haldan +K:Haste +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, exile the top card of each player's library and put a counter counter on each of them. Put a +1/+1 counter on CARDNAME for each noncreature card exiled this way. +SVar:TrigExile:DB$ Dig | DigNum$ 1 | ChangeNum$ All | Defined$ Player | DestinationZone$ Exile | ExileWithCounter$ FETCH | RememberChanged$ True | SubAbility$ DBPutCounter +SVar:DBPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ X | References$ X | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$ValidExile Card.IsRemembered+nonCreature +DeckHas:Ability$Counters +SVar:HasAttackEffect:TRUE +DeckHints:Name$Haldan, Avid Arcanist +Oracle:Partner with Haldan, Avid Arcanist\nHaste\nWhenever Pako, Arcane Retriever attacks, exile the top card of each player's library and put a fetch counter on each of them. Put a +1/+1 counter on Pako for each noncreature card exiled this way.