diff --git a/res/cardsfolder/c/chancellor_of_the_dross.txt b/res/cardsfolder/c/chancellor_of_the_dross.txt index 1f7f0a6c9fa..b16541c648b 100644 --- a/res/cardsfolder/c/chancellor_of_the_dross.txt +++ b/res/cardsfolder/c/chancellor_of_the_dross.txt @@ -3,10 +3,11 @@ ManaCost:4 B B B Types:Creature Vampire Text:You may reveal this card from your opening hand. If you do, at the beginning of the first upkeep, each opponent loses 3 life, then you gain life equal to the life lost this way. PT:6/6 -K:MayEffectFromOpeningHand:DrainOnUpkeep +K:MayEffectFromOpeningHand:RevealCard K:Flying K:Lifelink -SVar:DrainOnUpkeep:AB$ Effect | Cost$ 0 | Triggers$ TrigDrain | SVars$ DrainLife,GainLife,RemoveMe,AFLifeLost | Name$ Chancellor of the Dross effect +SVar:RevealCard:AB$ Reveal | Cost$ 0 | Defined$ Self | SubAbility$ DrainOnUpkeep | SpellDescription$ You may reveal this card from your opening hand. If you do, at the beginning of the first upkeep, each opponent loses 3 life, then you gain life equal to the life lost this way. +SVar:DrainOnUpkeep:DB$ Effect | Cost$ 0 | Triggers$ TrigDrain | SVars$ DrainLife,GainLife,RemoveMe,AFLifeLost | Name$ Chancellor of the Dross effect SVar:TrigDrain:Mode$ Phase | Phase$ Upkeep | Execute$ DrainLife | TriggerDescription$ At the beginning of the first upkeep, each opponent loses 3 life, then you gain life equal to the life lost this way. SVar:DrainLife:AB$ LoseLife | Cost$ 0 | Defined$ Player.Opponent | LifeAmount$ 3 | SubAbility$ GainLife SVar:GainLife:DB$ GainLife | Defined$ You | LifeAmount$ AFLifeLost | SubAbility$ RemoveMe diff --git a/res/cardsfolder/c/chancellor_of_the_forge.txt b/res/cardsfolder/c/chancellor_of_the_forge.txt index 53b74c298e7..70db6fa602e 100644 --- a/res/cardsfolder/c/chancellor_of_the_forge.txt +++ b/res/cardsfolder/c/chancellor_of_the_forge.txt @@ -6,8 +6,9 @@ PT:5/5 T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ EffMassToken | TriggerDescription$ When CARDNAME enters the battlefield, put X 1/1 red Goblin creature tokens with haste onto the battlefield, where X is the number of creatures you control. SVar:EffMassToken:AB$ Token | Cost$ 0 | TokenAmount$ X | TokenOwner$ You | TokenName$ Goblin | TokenTypes$ Creature,Goblin | TokenColors$ Red | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Haste | References$ X SVar:X:Count$Valid Creature.YouCtrl -K:MayEffectFromOpeningHand:TokenOnUpkeep -SVar:TokenOnUpkeep:AB$ Effect | Cost$ 0 | Triggers$ TrigToken | SVars$ EffToken,RemoveMe | Name$ Chancellor of the Forge effect +K:MayEffectFromOpeningHand:RevealCard +SVar:RevealCard:AB$ Reveal | Cost$ 0 | Defined$ Self | SubAbility$ TokenOnUpkeep | SpellDescription$ You may reveal this card from your opening hand. If you do, at the beginning of the first upkeep, put a 1/1 red Goblin creature token with haste onto the battlefield. +SVar:TokenOnUpkeep:DB$ Effect | Triggers$ TrigToken | SVars$ EffToken,RemoveMe | Name$ Chancellor of the Forge effect | SpellDescription$ You may reveal this card from your opening hand. If you do, at the beginning of the first upkeep, put a 1/1 red Goblin creature token with haste onto the battlefield. SVar:TrigToken:Mode$ Phase | Phase$ Upkeep | Execute$ EffToken | TriggerDescription$ At the beginning of the first upkeep, put a 1/1 red Goblin creature token with haste onto the battlefield. SVar:EffToken:AB$ Token | Cost$ 0 | TokenAmount$ 1 | TokenOwner$ You | TokenName$ Goblin | TokenTypes$ Creature,Goblin | TokenColors$ Red | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Haste | SubAbility$ RemoveMe SVar:RemoveMe:DB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Command | Destination$ Exile diff --git a/res/cardsfolder/c/chancellor_of_the_spires.txt b/res/cardsfolder/c/chancellor_of_the_spires.txt index bfd1cabe4ad..6611381d78a 100644 --- a/res/cardsfolder/c/chancellor_of_the_spires.txt +++ b/res/cardsfolder/c/chancellor_of_the_spires.txt @@ -3,9 +3,10 @@ ManaCost:4 U U U Types:Creature Sphinx Text:You may reveal this card from your opening hand. If you do, at the beginning of the first upkeep, each opponent puts the top seven cards of his or her library into his or her graveyard. PT:5/7 -K:MayEffectFromOpeningHand:DrainOnUpkeep +K:MayEffectFromOpeningHand:RevealCard K:Flying -SVar:DrainOnUpkeep:AB$ Effect | Cost$ 0 | Triggers$ TrigMill | SVars$ Mill,RemoveMe | Name$ Chancellor of the Spires effect +SVar:RevealCard:AB$ Reveal | Cost$ 0 | Defined$ Self | SubAbility$ DrainOnUpkeep | SpellDescription$ You may reveal this card from your opening hand. If you do, at the beginning of the first upkeep, each opponent puts the top seven cards of his or her library into his or her graveyard. +SVar:DrainOnUpkeep:DB$ Effect | Triggers$ TrigMill | SVars$ Mill,RemoveMe | Name$ Chancellor of the Spires effect SVar:TrigMill:Mode$ Phase | Phase$ Upkeep | Execute$ Mill | TriggerDescription$ At the beginning of the first upkeep, each opponent puts the top seven cards of his or her library into his or her graveyard. SVar:Mill:AB$ Mill | Cost$ 0 | NumCards$ 7 | Defined$ Player.Opponent | SubAbility$ RemoveMe SVar:RemoveMe:DB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Command | Destination$ Exile diff --git a/res/cardsfolder/c/chancellor_of_the_tangle.txt b/res/cardsfolder/c/chancellor_of_the_tangle.txt index eec81f1908d..a8f13544ffd 100644 --- a/res/cardsfolder/c/chancellor_of_the_tangle.txt +++ b/res/cardsfolder/c/chancellor_of_the_tangle.txt @@ -6,7 +6,8 @@ PT:6/7 K:MayEffectFromOpeningHand:ManaOnMain K:Vigilance K:Reach -SVar:ManaOnMain:AB$ Effect | Cost$ 0 | Triggers$ TrigMana | SVars$ EffMana,RemoveMe | Duration$ Permanent | Name$ Chancellor of the Tangle effect +SVar:RevealCard:AB$ Reveal | Cost$ 0 | Defined$ Self | SubAbility$ ManaOnMain | SpellDescription$ You may reveal this card from your opening hand. If you do, at the beginning of your first main phase, add G to your mana pool. +SVar:ManaOnMain:DB$ Effect | Triggers$ TrigMana | SVars$ EffMana,RemoveMe | Duration$ Permanent | Name$ Chancellor of the Tangle effect SVar:TrigMana:Mode$ Phase | Phase$ Main1 | ValidPlayer$ You | Execute$ EffMana | TriggerDescription$ At the beginning of your first main phase, add G to your mana pool. SVar:EffMana:AB$ Mana | Cost$ 0 | Produced$ G | SubAbility$ RemoveMe SVar:RemoveMe:DB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Command | Destination$ Exile diff --git a/res/cardsfolder/g/gemstone_caverns.txt b/res/cardsfolder/g/gemstone_caverns.txt index 620f4364de0..a83edbddb4c 100644 --- a/res/cardsfolder/g/gemstone_caverns.txt +++ b/res/cardsfolder/g/gemstone_caverns.txt @@ -1,7 +1,11 @@ Name:Gemstone Caverns ManaCost:no cost Types:Legendary Land +K:MayEffectFromOpeningHand:FromOpeningHand:!PlayFirst Text:If CARDNAME is in your opening hand and you're not playing first, you may begin the game with CARDNAME on the battlefield with a luck counter on it. If you do, exile a card from your hand. +SVar:FromOpeningHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SubAbility$ LuckCounter | SpellDescription$ If CARDNAME is in your opening hand and you're not playing first, you may begin the game with CARDNAME on the battlefield with a luck counter on it. +SVar:LuckCounter:DB$ PutCounter | CounterType$ LUCK | CounterNum$ 1 | Defined$ Self | SubAbility$ ExileFromHand +SVar:ExileFromHand:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SpellDescription$ If you do, exile a card from your hand. A:AB$ Mana | Cost$ T | Produced$ 1 | ConditionCheckSVar$ CheckCounter | ConditionSVarCompare$ EQ0 | SubAbility$ DBMana | SpellDescription$ Add 1 to your mana pool. If CARDNAME has a luck counter on it, instead add one mana of any color to your mana pool. SVar:DBMana:DB$ Mana | Produced$ Any | ConditionCheckSVar$ CheckCounter | ConditionSVarCompare$ GE1 SVar:CheckCounter:Count$CardCounters.LUCK diff --git a/res/cardsfolder/l/leyline_of_anticipation.txt b/res/cardsfolder/l/leyline_of_anticipation.txt index 225d2dbeef3..522d297b4e9 100644 --- a/res/cardsfolder/l/leyline_of_anticipation.txt +++ b/res/cardsfolder/l/leyline_of_anticipation.txt @@ -2,6 +2,8 @@ Name:Leyline of Anticipation ManaCost:2 U U Types:Enchantment Text:If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ You | AddKeyword$ You may cast nonland cards as though they had flash. | Description$ You may cast nonland cards as though they had flash. SVar:NonStackingEffect:True SVar:Picture:http://www.wizards.com/global/images/magic/general/leyline_of_anticipation.jpg diff --git a/res/cardsfolder/l/leyline_of_lifeforce.txt b/res/cardsfolder/l/leyline_of_lifeforce.txt index ce9efe38b71..8d0e770ec0b 100644 --- a/res/cardsfolder/l/leyline_of_lifeforce.txt +++ b/res/cardsfolder/l/leyline_of_lifeforce.txt @@ -2,6 +2,8 @@ Name:Leyline of Lifeforce ManaCost:2 G G Types:Enchantment Text:If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ Creature | AddHiddenKeyword$ CARDNAME can't be countered. | AffectedZone$ Stack | Description$ Creature spells can't be countered. SVar:NonStackingEffect:True SVar:RemRandomDeck:True diff --git a/res/cardsfolder/l/leyline_of_lightning.txt b/res/cardsfolder/l/leyline_of_lightning.txt index e69e74c5524..d8a2715b24a 100644 --- a/res/cardsfolder/l/leyline_of_lightning.txt +++ b/res/cardsfolder/l/leyline_of_lightning.txt @@ -2,6 +2,8 @@ Name:Leyline of Lightning ManaCost:2 R R Types:Enchantment Text:If Leyline of Lightning is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. T:Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDealDamage | TriggerDescription$ Whenever you cast a spell, you may pay 1. If you do, CARDNAME deals 1 damage to target player. SVar:TrigDealDamage:AB$DealDamage | Cost$ 1 | ValidTgts$ Player | TgtPrompt$ Select target player | NumDmg$ 1 SVar:RemRandomDeck:True diff --git a/res/cardsfolder/l/leyline_of_punishment.txt b/res/cardsfolder/l/leyline_of_punishment.txt index dec16e272de..8145aebe23d 100644 --- a/res/cardsfolder/l/leyline_of_punishment.txt +++ b/res/cardsfolder/l/leyline_of_punishment.txt @@ -2,6 +2,8 @@ Name:Leyline of Punishment ManaCost:2 R R Types:Enchantment Text:If Leyline of Punishment is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ Player | AddKeyword$ You can't gain life. | Description$ Players can't gain life. S:Mode$ Continuous | GlobalRule$ Damage can't be prevented. | Description$ Damage can't be prevented. SVar:NonStackingEffect:True diff --git a/res/cardsfolder/l/leyline_of_sanctity.txt b/res/cardsfolder/l/leyline_of_sanctity.txt index f5afc77dc27..7d84df07216 100644 --- a/res/cardsfolder/l/leyline_of_sanctity.txt +++ b/res/cardsfolder/l/leyline_of_sanctity.txt @@ -2,6 +2,8 @@ Name:Leyline of Sanctity ManaCost:2 W W Types:Enchantment Text:If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ You | AddKeyword$ Hexproof | Description$ You have hexproof. (You can't be the target of spells or abilities your opponents control.) SVar:NonStackingEffect:True SVar:Picture:http://www.wizards.com/global/images/magic/general/leyline_of_sanctity.jpg diff --git a/res/cardsfolder/l/leyline_of_singularity.txt b/res/cardsfolder/l/leyline_of_singularity.txt index 5d977155fbf..6528e2ec72f 100644 --- a/res/cardsfolder/l/leyline_of_singularity.txt +++ b/res/cardsfolder/l/leyline_of_singularity.txt @@ -2,6 +2,8 @@ Name:Leyline of Singularity ManaCost:2 U U Types:Enchantment Text:If Leyline of Singularity is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ Permanent.nonLand | AddType$ Legendary | Description$ All nonland permanents are legendary. SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/leyline_of_singularity.jpg diff --git a/res/cardsfolder/l/leyline_of_the_meek.txt b/res/cardsfolder/l/leyline_of_the_meek.txt index 272870b4b3b..6949b37ee7c 100644 --- a/res/cardsfolder/l/leyline_of_the_meek.txt +++ b/res/cardsfolder/l/leyline_of_the_meek.txt @@ -2,6 +2,8 @@ Name:Leyline of the Meek ManaCost:2 W W Types:Enchantment Text:If Leyline of the Meek is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ Creature.token | AddPower$ 1 | AddToughness$ 1 | Description$ Creature tokens get +1/+1. SVar:PlayMain1:TRUE SVar:RemRandomDeck:True diff --git a/res/cardsfolder/l/leyline_of_the_void.txt b/res/cardsfolder/l/leyline_of_the_void.txt index 7c8f1c78b9a..da1fb4ce351 100644 --- a/res/cardsfolder/l/leyline_of_the_void.txt +++ b/res/cardsfolder/l/leyline_of_the_void.txt @@ -2,6 +2,8 @@ Name:Leyline of the Void ManaCost:2 B B Types:Enchantment Text:If Leyline of the Void is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Destination$ Graveyard | ValidCard$ Card.nonToken+OppOwn | ReplaceWith$ Exile | Description$ If a card would be put into an opponent's graveyard from anywhere, exile it instead. SVar:Exile:AB$ ChangeZone | Cost$ 0 | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard SVar:NonStackingEffect:True diff --git a/res/cardsfolder/l/leyline_of_vitality.txt b/res/cardsfolder/l/leyline_of_vitality.txt index 21cc8432db8..ebd9fce9a57 100644 --- a/res/cardsfolder/l/leyline_of_vitality.txt +++ b/res/cardsfolder/l/leyline_of_vitality.txt @@ -2,6 +2,8 @@ Name:Leyline of Vitality ManaCost:2 G G Types:Enchantment Text:If Leyline of Vitality is in your opening hand, you may begin the game with it on the battlefield. +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If CARDNAME is in your opening hand, you may begin the game with it on the battlefield. S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddToughness$ 1 | Description$ Creatures you control get +0/+1. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigGainLife | TriggerDescription$ Whenever a creature enters the battlefield under your control, you may gain 1 life. SVar:TrigGainLife:AB$GainLife | Cost$ 0 | Defined$ You | LifeAmount$ 1 diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java index 52682724267..13e498a638c 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java @@ -733,9 +733,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { break; } - // card has to be on battlefield or in own hand - boolean canUseInputToSelectCard = origin.size() == 1 && ( origin.get(0) == ZoneType.Battlefield || origin.get(0) == ZoneType.Hand && player == decider); - Card c; if (sa.hasParam("AtRandom")) { c = Aggregates.random(fetchList); @@ -743,6 +740,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect { c = fetchList.get(0); } else { boolean mustChoose = sa.hasParam("Mandatory"); + // card has to be on battlefield or in own hand + boolean canUseInputToSelectCard = origin.size() == 1 && ( origin.get(0) == ZoneType.Battlefield || origin.get(0) == ZoneType.Hand && player == decider); if( canUseInputToSelectCard ) { InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, fetchList); inp.setCancelAllowed(!mustChoose); diff --git a/src/main/java/forge/card/ability/effects/RevealEffect.java b/src/main/java/forge/card/ability/effects/RevealEffect.java index 723ddd7f664..2f0fbc0fde0 100644 --- a/src/main/java/forge/card/ability/effects/RevealEffect.java +++ b/src/main/java/forge/card/ability/effects/RevealEffect.java @@ -46,6 +46,8 @@ public class RevealEffect extends SpellAbilityEffect { revealed.add(Aggregates.random(cardsInHand)); } + } else if (sa.hasParam("Defined")) { + revealed.addAll(AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa)); } else { List valid = new ArrayList(cardsInHand); diff --git a/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java b/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java index d177e9da286..088af87b190 100644 --- a/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java +++ b/src/main/java/forge/card/spellability/HumanPlaySpellAbility.java @@ -79,6 +79,7 @@ public class HumanPlaySpellAbility { if (isFree || this.payment.isFullyPaid()) { if (skipStack) { + game.getStack().unfreezeStack(); AbilityUtils.resolve(this.ability); } else { this.enusureAbilityHasDescription(this.ability); diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index e0c4a34f7b2..9b136a1aa04 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -40,7 +40,6 @@ import forge.CounterType; import forge.FThreads; import forge.GameEntity; import forge.GameLogEntryType; -import forge.Singletons; import forge.card.CardType; import forge.card.TriggerReplacementBase; import forge.card.ability.AbilityFactory; @@ -59,7 +58,6 @@ import forge.card.trigger.Trigger; import forge.card.trigger.TriggerType; import forge.card.trigger.ZCTrigger; import forge.game.ai.ComputerUtil; -import forge.game.ai.ComputerUtilCard; import forge.game.event.GameEventCardDestroyed; import forge.game.event.GameEventCardRegenerated; import forge.game.event.GameEventCardSacrificed; @@ -75,8 +73,6 @@ import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; -import forge.gui.GuiDialog; -import forge.gui.input.InputSelectCardsFromList; import forge.util.Aggregates; import forge.util.maps.CollectionSuppliers; import forge.util.maps.HashMapOfLists; @@ -1420,103 +1416,6 @@ public class GameAction { } } - private void handleLeylinesAndChancellors(final Player first) { - for (Player p : game.getPlayers()) { - final List openingHand = new ArrayList(p.getCardsIn(ZoneType.Hand)); - - for (final Card c : openingHand) { - // check c.isInZone(ZoneType.Hand) because Gemstone Caverns would exile a card - if (!c.isInZone(ZoneType.Hand)) { - continue; - } - if (p.isHuman()) { - for (String kw : c.getKeyword()) { - if (kw.startsWith("MayEffectFromOpeningHand")) { - final String effName = kw.split(":")[1]; - - final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c); - effect.setActivatingPlayer(p); - if (GuiDialog.confirm(c, "Use " + c +"'s ability?")) { - // If we ever let the AI memorize cards in the players - // hand, this would be a place to do so. - HumanPlay.playSpellAbilityNoStack(p, effect); - } - } - } - if (c.getName().startsWith("Leyline of")) { - if (GuiDialog.confirm(c, "Use " + c + "'s ability?")) { - game.getAction().moveToPlay(c); - } - } - if (c.getName().equals("Gemstone Caverns") && !p.equals(first) - && GuiDialog.confirm(c, "Use " + c + "'s ability?")) { - c.addCounter(CounterType.LUCK, 1, true); - game.getAction().moveToPlay(c); - List remainingHand = new ArrayList(p.getCardsIn(ZoneType.Hand)); - InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, remainingHand); - inp.setCancelAllowed(false); - inp.setMessage("Choose a card in you hand to exile"); - Singletons.getControl().getInputQueue().setInputAndWait(inp); - Card exiled = inp.getSelected().get(0); - game.getAction().exile(exiled); - } - } else { // Computer Leylines & Chancellors - if (!c.getName().startsWith("Leyline of") && !c.getName().equals("Gemstone Caverns")) { - for (String kw : c.getKeyword()) { - if (kw.startsWith("MayEffectFromOpeningHand")) { - final String effName = kw.split(":")[1]; - - final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c); - effect.setActivatingPlayer(p); - // Is there a better way for the AI to decide this? - if (effect.doTrigger(false, p)) { - GuiDialog.message("Computer reveals " + c.getName() + "(" + c.getUniqueNumber() + ")."); - ComputerUtil.playNoStack(p, effect, game); - } - } - } - } - if (c.getName().startsWith("Leyline of") - && !(c.getName().startsWith("Leyline of Singularity") - && (Iterables.any(game.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Leyline of Singularity"))))) { - game.getAction().moveToPlay(c); - //ga.checkStateEffects(); - } - if (c.getName().equals("Gemstone Caverns") && !p.equals(first) - && !Iterables.any(game.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Gemstone Caverns"))) { - c.addCounter(CounterType.LUCK, 1, true); - game.getAction().moveToPlay(c); - List remainingHand = new ArrayList(p.getCardsIn(ZoneType.Hand)); - Card exiled = ComputerUtilCard.getWorstAI(remainingHand); - game.getAction().exile(exiled); - } - } - } - } - } - - private Player determineFirstTurnPlayer(final GameOutcome lastGameOutcome) { - // Only cut/coin toss if it's the first game of the match - Player goesFirst = null; - - boolean isFirstGame = lastGameOutcome == null; - if (isFirstGame) { - game.fireEvent(new GameEventFlipCoin()); // Play the Flip Coin sound - goesFirst = Aggregates.random(game.getPlayers()); - } else { - for(Player p : game.getPlayers()) { - if(!lastGameOutcome.isWinner(p.getLobbyPlayer())) { - goesFirst = p; - break; - } - } - } - - boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(isFirstGame); - goesFirst = willPlay ? goesFirst : goesFirst.getOpponent(); - return goesFirst; - } - public void startGame() { Player first = determineFirstTurnPlayer(game.getMatch().getLastGameOutcome()); @@ -1540,7 +1439,7 @@ public class GameAction { if(game.getType() == GameType.Planechase) first.initPlane(); - handleLeylinesAndChancellors(first); + runOpeningHandActions(first); checkStateEffects(); // Run Trigger beginning of the game @@ -1557,6 +1456,28 @@ public class GameAction { game.fireEvent(new GameEventGameFinished()); } + private Player determineFirstTurnPlayer(final GameOutcome lastGameOutcome) { + // Only cut/coin toss if it's the first game of the match + Player goesFirst = null; + + boolean isFirstGame = lastGameOutcome == null; + if (isFirstGame) { + game.fireEvent(new GameEventFlipCoin()); // Play the Flip Coin sound + goesFirst = Aggregates.random(game.getPlayers()); + } else { + for(Player p : game.getPlayers()) { + if(!lastGameOutcome.isWinner(p.getLobbyPlayer())) { + goesFirst = p; + break; + } + } + } + + boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(isFirstGame); + goesFirst = willPlay ? goesFirst : goesFirst.getOpponent(); + return goesFirst; + } + private void performMulligans(final Player firstPlayer, final boolean isCommander) { List whoCanMulligan = Lists.newArrayList(game.getPlayers()); int offset = whoCanMulligan.indexOf(firstPlayer); @@ -1630,6 +1551,45 @@ public class GameAction { } } + private void runOpeningHandActions(final Player first) { + Player takesAction = first; + do { + List usableFromOpeningHand = new ArrayList(); + + // Select what can be activated from a given hand + for (final Card c : takesAction.getCardsIn(ZoneType.Hand)) { + for (String kw : c.getKeyword()) { + if (kw.startsWith("MayEffectFromOpeningHand")) { + String[] split = kw.split(":"); + final String effName = split[1]; + if ( split.length > 2 && split[2].equalsIgnoreCase("!PlayFirst") && first == takesAction) + continue; + + final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c); + effect.setActivatingPlayer(takesAction); + + usableFromOpeningHand.add(effect); + } + } + } + + // Players are supposed to return the effects in an order they want those to be resolved (Rule 103.5) + usableFromOpeningHand = takesAction.getController().chooseSaToActivateFromOpeningHand(usableFromOpeningHand); + + for(final SpellAbility sa : usableFromOpeningHand ) { + if (!takesAction.getZone(ZoneType.Hand).contains(sa.getSourceCard())) + continue; + + if (takesAction.isHuman()) + HumanPlay.playSpellAbilityNoStack(takesAction, sa); + else + ComputerUtil.playNoStack(takesAction, sa, game); + } + takesAction = game.getNextPlayerAfter(takesAction); + } while( takesAction != first ); + // state effects are checked only when someone gets priority + } + // Invokes given runnable in Game thread pool - used to start game and perform actions from UI (when game-0 waits for input) public void invoke(final Runnable proc) { if( FThreads.isGameThread() ) { diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index 3ba44b020a3..aabee1cce52 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -904,5 +904,45 @@ public class AiController { } return toExile; } + + public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) { + // AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns) + + List result = new ArrayList(); + for(SpellAbility sa : usableFromOpeningHand) { + // Is there a better way for the AI to decide this? + if (sa.doTrigger(false, player)) { + result.add(sa); + } + } + + boolean hasLeyline1 = false; + SpellAbility saGemstones = null; + + for(int i = 0; i < result.size(); i++) { + SpellAbility sa = result.get(i); + + String srcName = sa.getSourceCard().getName(); + if("Gemstone Caverns".equals(srcName)) { + if(saGemstones == null) + saGemstones = sa; + else + result.remove(i--); + } else if ("Leyline of Singularity".equals(srcName)) { + if(!hasLeyline1) + hasLeyline1 = true; + else + result.remove(i--); + } + } + + // Play them last + if( saGemstones != null ) { + result.remove(saGemstones); + result.add(saGemstones); + } + + return result; + } } diff --git a/src/main/java/forge/game/phase/CombatUtil.java b/src/main/java/forge/game/phase/CombatUtil.java index 22933536e79..20cbf5383b1 100644 --- a/src/main/java/forge/game/phase/CombatUtil.java +++ b/src/main/java/forge/game/phase/CombatUtil.java @@ -49,9 +49,7 @@ import forge.game.Game; import forge.game.GlobalRuleChange; import forge.game.player.Player; import forge.game.player.PlayerController.ManaPaymentPurpose; -import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; -import forge.gui.GuiChoose; import forge.gui.GuiDialog; import forge.gui.framework.EDocID; import forge.gui.framework.SDisplayUtil; diff --git a/src/main/java/forge/game/player/PlayerActionConfirmMode.java b/src/main/java/forge/game/player/PlayerActionConfirmMode.java index 55b6290012c..b6a37379248 100644 --- a/src/main/java/forge/game/player/PlayerActionConfirmMode.java +++ b/src/main/java/forge/game/player/PlayerActionConfirmMode.java @@ -7,5 +7,6 @@ package forge.game.player; public enum PlayerActionConfirmMode { Random, BraidOfFire, + FromOpeningHand; } \ No newline at end of file diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 5636c0e3787..dcd169f03c4 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -129,6 +129,7 @@ public abstract class PlayerController { public abstract List chooseCardsToDelve(int colorLessAmount, List grave); public abstract List chooseCardsToRevealFromHand(int min, int max, List valid); public abstract List chooseCardsToDiscardUnlessType(int min, List hand, String param, SpellAbility sa); + public abstract List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand); public abstract Mana chooseManaFromPool(List manaChoices); public abstract String chooseSomeType(String kindOfType, String aiLogic, List validTypes, List invalidTypes); diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index c790b848847..0bfdee1e112 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -325,4 +325,10 @@ public class PlayerControllerAi extends PlayerController { } return false; } + + @Override + public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) { + // AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns) + return brains.chooseSaToActivateFromOpeningHand(usableFromOpeningHand); + } } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 1e6d9a73ef7..598154030b2 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -539,4 +539,27 @@ public class PlayerControllerHuman extends PlayerController { public boolean payManaOptional(Card c, Cost attackCost, String prompt, ManaPaymentPurpose purpose) { return HumanPlay.payCostDuringAbilityResolve(player, c, attackCost, null); } + + + /* (non-Javadoc) + * @see forge.game.player.PlayerController#chooseSaToActivateFromOpeningHand(java.util.List) + */ + @Override + public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) { + List srcCards = new ArrayList(); + for(SpellAbility sa : usableFromOpeningHand) { + srcCards.add(sa.getSourceCard()); + } + List chosen = GuiChoose.order("Choose cards to activate from opening hand", "Activate first", -1, srcCards, null, null); + List result = new ArrayList(); + for(Card c : chosen) { + for(SpellAbility sa : usableFromOpeningHand) { + if ( sa.getSourceCard() == c ) { + result.add(sa); + break; + } + } + } + return result; + } }