merge latest trunk

This commit is contained in:
myk
2013-03-16 16:12:07 +00:00
64 changed files with 842 additions and 597 deletions

9
.gitattributes vendored
View File

@@ -7242,6 +7242,7 @@ res/cardsfolder/n/noble_templar.txt svneol=native#text/plain
res/cardsfolder/n/noble_vestige.txt svneol=native#text/plain res/cardsfolder/n/noble_vestige.txt svneol=native#text/plain
res/cardsfolder/n/nocturnal_raid.txt svneol=native#text/plain res/cardsfolder/n/nocturnal_raid.txt svneol=native#text/plain
res/cardsfolder/n/noetic_scales.txt svneol=native#text/plain res/cardsfolder/n/noetic_scales.txt svneol=native#text/plain
res/cardsfolder/n/noggin_whack.txt -text
res/cardsfolder/n/noggle_bandit.txt svneol=native#text/plain res/cardsfolder/n/noggle_bandit.txt svneol=native#text/plain
res/cardsfolder/n/noggle_bridgebreaker.txt svneol=native#text/plain res/cardsfolder/n/noggle_bridgebreaker.txt svneol=native#text/plain
res/cardsfolder/n/noggle_hedge_mage.txt svneol=native#text/plain res/cardsfolder/n/noggle_hedge_mage.txt svneol=native#text/plain
@@ -8976,6 +8977,7 @@ res/cardsfolder/s/sarcomite_myr.txt svneol=native#text/plain
res/cardsfolder/s/sarkhan_the_mad.txt svneol=native#text/plain res/cardsfolder/s/sarkhan_the_mad.txt svneol=native#text/plain
res/cardsfolder/s/sarkhan_vol.txt svneol=native#text/plain res/cardsfolder/s/sarkhan_vol.txt svneol=native#text/plain
res/cardsfolder/s/sarpadian_empires_vol_vii.txt svneol=native#text/plain res/cardsfolder/s/sarpadian_empires_vol_vii.txt svneol=native#text/plain
res/cardsfolder/s/sasaya_orochi_ascendant_sasayas_essence.txt -text
res/cardsfolder/s/savaen_elves.txt -text res/cardsfolder/s/savaen_elves.txt -text
res/cardsfolder/s/savage_beating.txt -text res/cardsfolder/s/savage_beating.txt -text
res/cardsfolder/s/savage_conception.txt svneol=native#text/plain res/cardsfolder/s/savage_conception.txt svneol=native#text/plain
@@ -9119,6 +9121,7 @@ res/cardsfolder/s/scuttling_death.txt svneol=native#text/plain
res/cardsfolder/s/scuzzback_marauders.txt svneol=native#text/plain res/cardsfolder/s/scuzzback_marauders.txt svneol=native#text/plain
res/cardsfolder/s/scuzzback_scrapper.txt svneol=native#text/plain res/cardsfolder/s/scuzzback_scrapper.txt svneol=native#text/plain
res/cardsfolder/s/scythe_of_the_wretched.txt -text res/cardsfolder/s/scythe_of_the_wretched.txt -text
res/cardsfolder/s/scythe_specter.txt -text
res/cardsfolder/s/scythe_tiger.txt -text res/cardsfolder/s/scythe_tiger.txt -text
res/cardsfolder/s/sea_drake.txt svneol=native#text/plain res/cardsfolder/s/sea_drake.txt svneol=native#text/plain
res/cardsfolder/s/sea_eagle.txt svneol=native#text/plain res/cardsfolder/s/sea_eagle.txt svneol=native#text/plain
@@ -12859,6 +12862,7 @@ res/quest/precons/Battle[!!-~]Surge.dck -text
res/quest/precons/Blood[!!-~]And[!!-~]Fire.dck -text res/quest/precons/Blood[!!-~]And[!!-~]Fire.dck -text
res/quest/precons/Boggart[!!-~]Feast.dck -text res/quest/precons/Boggart[!!-~]Feast.dck -text
res/quest/precons/Bomber.dck -text res/quest/precons/Bomber.dck -text
res/quest/precons/Boros[!!-~]Battalion.dck -text
res/quest/precons/Bound[!!-~]by[!!-~]Strength.dck -text res/quest/precons/Bound[!!-~]by[!!-~]Strength.dck -text
res/quest/precons/Breath[!!-~]of[!!-~]Fire.dck -text res/quest/precons/Breath[!!-~]of[!!-~]Fire.dck -text
res/quest/precons/Carnival[!!-~]of[!!-~]Blood.dck -text res/quest/precons/Carnival[!!-~]of[!!-~]Blood.dck -text
@@ -12878,6 +12882,7 @@ res/quest/precons/Decay.dck -text
res/quest/precons/Deep[!!-~]Freeze.dck -text res/quest/precons/Deep[!!-~]Freeze.dck -text
res/quest/precons/Depths[!!-~]of[!!-~]Power.dck -text res/quest/precons/Depths[!!-~]of[!!-~]Power.dck -text
res/quest/precons/Devouring[!!-~]Skies.dck -text res/quest/precons/Devouring[!!-~]Skies.dck -text
res/quest/precons/Dimir[!!-~]Dementia.dck -text
res/quest/precons/Disrupter.dck -text res/quest/precons/Disrupter.dck -text
res/quest/precons/Domain.dck -text res/quest/precons/Domain.dck -text
res/quest/precons/Doom[!!-~]Inevitable.dck -text res/quest/precons/Doom[!!-~]Inevitable.dck -text
@@ -12905,6 +12910,7 @@ res/quest/precons/Grave[!!-~]Power.dck -text
res/quest/precons/Grixis[!!-~]Shambling[!!-~]Army.dck -text res/quest/precons/Grixis[!!-~]Shambling[!!-~]Army.dck -text
res/quest/precons/Grixis[!!-~]Undead.dck -text res/quest/precons/Grixis[!!-~]Undead.dck -text
res/quest/precons/Groundbreaker.dck -text res/quest/precons/Groundbreaker.dck -text
res/quest/precons/Gruul[!!-~]Goliaths.dck -text
res/quest/precons/Heavy[!!-~]Hitters.dck -text res/quest/precons/Heavy[!!-~]Hitters.dck -text
res/quest/precons/Hold[!!-~]the[!!-~]Line.dck -text res/quest/precons/Hold[!!-~]the[!!-~]Line.dck -text
res/quest/precons/Infect[!!-~]and[!!-~]Defile.dck -text res/quest/precons/Infect[!!-~]and[!!-~]Defile.dck -text
@@ -12937,6 +12943,7 @@ res/quest/precons/Mysterious[!!-~]Realms.dck -text
res/quest/precons/Mystical[!!-~]Might.dck -text res/quest/precons/Mystical[!!-~]Might.dck -text
res/quest/precons/Naya[!!-~]Behemoths.dck -text res/quest/precons/Naya[!!-~]Behemoths.dck -text
res/quest/precons/Naya[!!-~]Domain.dck -text res/quest/precons/Naya[!!-~]Domain.dck -text
res/quest/precons/Orzhov[!!-~]Oppression.dck -text
res/quest/precons/Path[!!-~]of[!!-~]Blight.dck -text res/quest/precons/Path[!!-~]of[!!-~]Blight.dck -text
res/quest/precons/Phyrexian[!!-~]Assault.dck -text res/quest/precons/Phyrexian[!!-~]Assault.dck -text
res/quest/precons/Phyrexian[!!-~]Poison.dck -text res/quest/precons/Phyrexian[!!-~]Poison.dck -text
@@ -12963,6 +12970,7 @@ res/quest/precons/Sacrificial[!!-~]Bam.dck -text
res/quest/precons/Selesnya[!!-~]Surge.dck -text res/quest/precons/Selesnya[!!-~]Surge.dck -text
res/quest/precons/Selesnya[!!-~]United.dck -text res/quest/precons/Selesnya[!!-~]United.dck -text
res/quest/precons/Shamanism.dck -text res/quest/precons/Shamanism.dck -text
res/quest/precons/Simic[!!-~]Synthesis.dck -text
res/quest/precons/Sky[!!-~]Slam.dck -text res/quest/precons/Sky[!!-~]Slam.dck -text
res/quest/precons/Slaughterhouse.dck -text res/quest/precons/Slaughterhouse.dck -text
res/quest/precons/Sleeper.dck -text res/quest/precons/Sleeper.dck -text
@@ -13850,6 +13858,7 @@ src/main/java/forge/game/event/FlipCoinEvent.java -text
src/main/java/forge/game/event/LandPlayedEvent.java -text src/main/java/forge/game/event/LandPlayedEvent.java -text
src/main/java/forge/game/event/LifeLossEvent.java -text src/main/java/forge/game/event/LifeLossEvent.java -text
src/main/java/forge/game/event/ManaBurnEvent.java -text src/main/java/forge/game/event/ManaBurnEvent.java -text
src/main/java/forge/game/event/MulliganEvent.java -text
src/main/java/forge/game/event/PoisonCounterEvent.java -text src/main/java/forge/game/event/PoisonCounterEvent.java -text
src/main/java/forge/game/event/SetTappedEvent.java -text src/main/java/forge/game/event/SetTappedEvent.java -text
src/main/java/forge/game/event/ShuffleEvent.java -text src/main/java/forge/game/event/ShuffleEvent.java -text

View File

@@ -11,7 +11,9 @@ Release Notes:
New Cards: New Cards:
Noggin Whack
Scythe Specter
Sasaya, Orochi Ascendant
Known Issues: Known Issues:

View File

@@ -3,7 +3,8 @@ ManaCost:3 R
Types:Creature Goblin Rogue Shaman Types:Creature Goblin Rogue Shaman
PT:1/1 PT:1/1
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | CombatDamage$ True | Execute$ TrigSac | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player sacrifices a permanent. T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | CombatDamage$ True | Execute$ TrigSac | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player sacrifices a permanent.
SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ TriggeredTarget | SacValid$ Permanent SVar:TrigSac:AB$ Sacrifice | Cost$ 0 | Defined$ TriggeredTarget | SacValid$ Permanent
SVar:MustBeBlocked:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/akki_underminer.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/akki_underminer.jpg
Oracle:Whenever Akki Underminer deals combat damage to a player, that player sacrifices a permanent. Oracle:Whenever Akki Underminer deals combat damage to a player, that player sacrifices a permanent.
SetInfo:CHK Uncommon SetInfo:CHK Uncommon

View File

@@ -2,6 +2,8 @@ Name:Chains of Mephistopheles
ManaCost:1 B ManaCost:1 B
Types:Enchantment Types:Enchantment
Text:If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard. Text:If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard.
SVar:MillOne:DB$ Mill | NumCards$ 1
SVar:DiscardOne:DB$ Discard | Mandatory$ True | NumCards$ 1 | Mode$ TgtChoose
SVar:RemRandomDeck:True SVar:RemRandomDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/chains_of_mephistopheles.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/chains_of_mephistopheles.jpg
Oracle:If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard. Oracle:If a player would draw a card except the first one he or she draws in his or her draw step each turn, that player discards a card instead. If the player discards a card this way, he or she draws a card. If the player doesn't discard a card this way, he or she puts the top card of his or her library into his or her graveyard.

View File

@@ -6,8 +6,10 @@ S:Mode$ ReduceCost | EffectZone$ Command | ValidCard$ Card.Red | Type$ Spell | A
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ DBPutCounter | TriggerDescription$ Whenever you roll Chaos, put X +1/+1 counters on target creature, where X is that creature's converted mana cost. T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ DBPutCounter | TriggerDescription$ Whenever you roll Chaos, put X +1/+1 counters on target creature, where X is that creature's converted mana cost.
T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ RolledWalk | Secondary$ True | TriggerDescription$ Whenever you roll Planeswalk, put this card on the bottom of its owner's planar deck face down, then move the top card of your planar deck off that planar deck and turn it face up T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ RolledWalk | Secondary$ True | TriggerDescription$ Whenever you roll Planeswalk, put this card on the bottom of its owner's planar deck face down, then move the top card of your planar deck off that planar deck and turn it face up
SVar:RolledWalk:AB$ Planeswalk | Cost$ 0 SVar:RolledWalk:AB$ Planeswalk | Cost$ 0
SVar:DBPutCounter:DB$PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ X A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice.
SVar:X:Targeted$CardManaCost SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ Y
SVar:Y:Targeted$CardManaCost
SVar:X:Count$RolledThisTurn
SVar:Picture:http://www.wizards.com/global/images/magic/general/feeding_grounds.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/feeding_grounds.jpg
Oracle:Red spells cost {1} less to cast.\nGreen spells cost {1} less to cast.\nWhenever you roll {C}, put X +1/+1 counters on target creature, where X is that creature's converted mana cost. Oracle:Red spells cost {1} less to cast.\nGreen spells cost {1} less to cast.\nWhenever you roll {C}, put X +1/+1 counters on target creature, where X is that creature's converted mana cost.
SetInfo:HOP Common SetInfo:HOP Common

View File

@@ -5,8 +5,8 @@ PT:4/4
K:Flying K:Flying
T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, you may exile CARDNAME. If you do, return it to the battlefield under its owner's control at the beginning of the next end step. T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, you may exile CARDNAME. If you do, return it to the battlefield under its owner's control at the beginning of the next end step.
SVar:TrigExile:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Battlefield | Destination$ Exile | SubAbility$ DelTrig SVar:TrigExile:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Battlefield | Destination$ Exile | SubAbility$ DelTrig
SVar:DelTrig:DB$DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | TriggerDescription$ Return CARDNAME to the battlefield. SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | TriggerDescription$ Return CARDNAME to the battlefield.
SVar:TrigReturn:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Exile | Destination$ Battlefield SVar:TrigReturn:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Exile | Destination$ Battlefield
SVar:RemAIDeck:True SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/hikari_twilight_guardian.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/hikari_twilight_guardian.jpg
Oracle:Flying\nWhenever you cast a Spirit or Arcane spell, you may exile Hikari, Twilight Guardian. If you do, return it to the battlefield under its owner's control at the beginning of the next end step. Oracle:Flying\nWhenever you cast a Spirit or Arcane spell, you may exile Hikari, Twilight Guardian. If you do, return it to the battlefield under its owner's control at the beginning of the next end step.

View File

@@ -5,7 +5,9 @@ S:Mode$ Continuous | EffectZone$ Command | Affected$ Permanent | AddHiddenKeywor
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ DBFetch | TriggerDescription$ Whenever you roll Chaos, you may search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library. T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ DBFetch | TriggerDescription$ Whenever you roll Chaos, you may search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library.
T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ RolledWalk | Secondary$ True | TriggerDescription$ Whenever you roll Planeswalk, put this card on the bottom of its owner's planar deck face down, then move the top card of your planar deck off that planar deck and turn it face up T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ RolledWalk | Secondary$ True | TriggerDescription$ Whenever you roll Planeswalk, put this card on the bottom of its owner's planar deck face down, then move the top card of your planar deck off that planar deck and turn it face up
SVar:RolledWalk:AB$ Planeswalk | Cost$ 0 SVar:RolledWalk:AB$ Planeswalk | Cost$ 0
A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice.
SVar:DBFetch:DB$ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 3 SVar:DBFetch:DB$ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 3
SVar:Picture:http://www.wizards.com/global/images/magic/general/horizon_boughs.jpg SVar:X:Count$RolledThisTurn
SVar:Picture:http://www.cardforge.org/fpics/lq_planes_promos/horizon_boughs.jpg
Oracle:All permanents untap during each player's untap step.\nWhenever you roll {C}, you may search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library. Oracle:All permanents untap during each player's untap step.\nWhenever you roll {C}, you may search your library for up to three basic land cards, put them onto the battlefield tapped, then shuffle your library.
SetInfo:HOP Common SetInfo:HOP Common

View File

@@ -0,0 +1,12 @@
Name:Noggin Whack
ManaCost:2 B B
Types:Tribal Sorcery Rogue
A:SP$ Reveal | Cost$ 2 B B | ValidTgts$ Player | IsCurse$ True | NumCards$ 3 | RememberRevealed$ True | SubAbility$ DBChoose | SpellDescription$ Target player reveals three cards from his or her hand. You choose two of them. That player discards those cards.
A:SP$ Reveal | Cost$ 1 B | Activation$ Prowl | PrecostDesc$ Prowl | ValidTgts$ Player | IsCurse$ True | NumCards$ 3 | RememberRevealed$ True | SubAbility$ DBChoose | SpellDescription$ (You may cast this for its prowl cost if you dealt combat damage to a player this turn withRogue.)
SVar:DBChoose:DB$ ChooseCard | Amount$ 2 | Choices$ Card.IsRemembered | ChoiceZone$ Hand | Defined$ You | Mandatory$ True | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Mode$ Defined | DefinedCards$ ChosenCard | Defined$ Targeted | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/noggin_whack.jpg
Oracle:Prowl {1}{B} (You may cast this for its prowl cost if you dealt combat damage to a player this turn with a Rogue.)\nTarget player reveals three cards from his or her hand. You choose two of them. That player discards those cards.
SetInfo:MOR Uncommon

View File

@@ -6,8 +6,8 @@ A:AB$ ChooseSource | Cost$ T | Choices$ Card | RememberChosen$ True | AILogic$ N
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature | TgtPrompt$ Select target creature to redirect the damage from | ReplacementEffects$ SelflessCombat,SelflessNonCombat | Triggers$ OutOfSight | SVars$ CombatDmg,NonCombatDmg,ExileEffect,X | References$ SelflessCombat,SelflessNonCombat,OutOfSight,CombatDmg,NonCombatDmg,ExileEffect,X | Duration$ HostLeavesOrEOT | RememberObjects$ Remembered | ImprintCards$ Targeted | SubAbility$ DBCleanup | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 SVar:DBEffect:DB$ Effect | ValidTgts$ Creature | TgtPrompt$ Select target creature to redirect the damage from | ReplacementEffects$ SelflessCombat,SelflessNonCombat | Triggers$ OutOfSight | SVars$ CombatDmg,NonCombatDmg,ExileEffect,X | References$ SelflessCombat,SelflessNonCombat,OutOfSight,CombatDmg,NonCombatDmg,ExileEffect,X | Duration$ HostLeavesOrEOT | RememberObjects$ Remembered | ImprintCards$ Targeted | SubAbility$ DBCleanup | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1
SVar:SelflessCombat:Event$ DamageDone | ValidTarget$ Creature.IsImprinted | ValidSource$ Card.IsRemembered | IsCombat$ True | ReplaceWith$ CombatDmg | Description$ All damage that would be dealt to target creature this turn by a source of your choice is dealt to Oracle's Attendants instead. SVar:SelflessCombat:Event$ DamageDone | ValidTarget$ Creature.IsImprinted | ValidSource$ Card.IsRemembered | IsCombat$ True | ReplaceWith$ CombatDmg | Description$ All damage that would be dealt to target creature this turn by a source of your choice is dealt to Oracle's Attendants instead.
SVar:SelflessNonCombat:Event$ DamageDone | ValidTarget$ Creature.IsImprinted | ValidSource$ Card.IsRemembered | IsCombat$ False | ReplaceWith$ NonCombatDmg | Secondary$ True | Description$ All damage that would be dealt to target creature this turn by a source of your choice is dealt to Oracle's Attendants instead. SVar:SelflessNonCombat:Event$ DamageDone | ValidTarget$ Creature.IsImprinted | ValidSource$ Card.IsRemembered | IsCombat$ False | ReplaceWith$ NonCombatDmg | Secondary$ True | Description$ All damage that would be dealt to target creature this turn by a source of your choice is dealt to Oracle's Attendants instead.
SVar:ShamanCombatDmg:AB$ DealDamage | Cost$ 0 | Defined$ EffectSource | DamageSource$ ReplacedSource | CombatDamage$ True | NumDmg$ X SVar:CombatDmg:AB$ DealDamage | Cost$ 0 | Defined$ EffectSource | DamageSource$ ReplacedSource | CombatDamage$ True | NumDmg$ X
SVar:ShamanNonCombatDmg:AB$ DealDamage | Cost$ 0 | Defined$ EffectSource | DamageSource$ ReplacedSource | NumDmg$ X SVar:NonCombatDmg:AB$ DealDamage | Cost$ 0 | Defined$ EffectSource | DamageSource$ ReplacedSource | NumDmg$ X
SVar:OutOfSight:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Defined$ Imprinted | Execute$ ExileEffect | Static$ True SVar:OutOfSight:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Defined$ Imprinted | Execute$ ExileEffect | Static$ True
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile | Static$ True SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile | Static$ True
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -11,6 +11,6 @@ T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ Rolle
SVar:RolledWalk:AB$ Planeswalk | Cost$ 0 SVar:RolledWalk:AB$ Planeswalk | Cost$ 0
A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice. A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice.
SVar:X:Count$RolledThisTurn SVar:X:Count$RolledThisTurn
SVar:Picture:http://www.wizards.com/global/images/magic/general/ravens_run.jpg SVar:Picture:http://www.cardforge.org/fpics/lq_planes_promos/ravens_run.jpg
Oracle:All creatures have wither. (They deal damage to creatures in the form of -1/-1 counters.)\nWhenever you roll {C}, put a -1/-1 counter on target creature, two -1/-1 counters on another target creature, and three -1/-1 counters on a third target creature. Oracle:All creatures have wither. (They deal damage to creatures in the form of -1/-1 counters.)\nWhenever you roll {C}, put a -1/-1 counter on target creature, two -1/-1 counters on another target creature, and three -1/-1 counters on a third target creature.
SetInfo:HOP Common SetInfo:HOP Common

View File

@@ -0,0 +1,25 @@
Name:Sasaya, Orochi Ascendant
ManaCost:1 G G
Types:Legendary Creature Snake Monk
PT:2/3
A:AB$ SetState | Cost$ Reveal<1/Hand> | Defined$ Self | Mode$ Flip | ConditionCheckSVar$ CheckHandLand | ConditionSVarCompare$ GE7 | SpellDescription$ If you have seven or more land cards in your hand, flip CARDNAME.
SVar:CheckHandLand:Count$ValidHand Land.YouCtrl
AlternateMode:Flip
SVar:Picture:http://www.wizards.com/global/images/magic/general/sasaya_orochi_ascendant.jpg
Oracle:Reveal your hand: If you have seven or more land cards in your hand, flip CARDNAME.
ALTERNATE
Name:Sasaya's Essence
ManaCost:1 G G
Colors:green
Types:Legendary Enchantment
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana to your mana pool of any type that land produced.
SVar:TrigMana:AB$ Pump | Cost$ 0 | RememberObjects$ TriggeredCard | SubAbility$ DBRepeat
SVar:DBRepeat:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Land.YouCtrl+IsNotRemembered+sharesNameWith Remembered | RepeatSubAbility$ DBManaReflect | SubAbility$ DBCleanup
SVar:DBManaReflect:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Imprinted | ReflectProperty$ Produced | Defined$ You
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/sasayas_essence.jpg
Oracle:Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana to your mana pool of any type that land produced.
SetInfo:SOK Rare

View File

@@ -0,0 +1,14 @@
Name:Scythe Specter
ManaCost:4 B B
Types:Creature Specter
PT:4/4
K:Flying
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDiscard | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, each opponent discards a card. Each player who discarded a card with the highest converted mana cost among cards discarded this way loses life equal to that converted mana cost.
SVar:TrigDiscard:AB$ Discard | Cost$ 0 | Mode$ TgtChoose | NumCards$ 1 | Defined$ Player.Opponent | RememberDiscarded$ True | SubAbility$ DBRepeatLoseLife
SVar:DBRepeatLoseLife:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Card.greatestRememberedCMC | Zone$ Battlefield,Graveyard,Exile,Library,Hand | RepeatSubAbility$ DBLoseLife | SubAbility$ DBCleanup
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ X | References$ X | Defined$ ImprintedController
SVar:X:Imprinted$CardManaCost
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/scythe_specter.jpg
Oracle:Flying\nWhenever Scythe Specter deals combat damage to a player, each opponent discards a card. Each player who discarded a card with the highest converted mana cost among cards discarded this way loses life equal to that converted mana cost.
SetInfo:COM Rare

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Spirit
PT:2/2 PT:2/2
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Battlefield | ValidCard$ Creature.powerLE1+YouOwn | OptionalDecider$ You | DelayedTrigger$ DelTrig | TriggerDescription$ Whenever a creature with power 1 or less is put into your graveyard from the battlefield, you may return that card to the battlefield under your control at the beginning of the next end step if CARDNAME is still on the battlefield. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Battlefield | ValidCard$ Creature.powerLE1+YouOwn | OptionalDecider$ You | DelayedTrigger$ DelTrig | TriggerDescription$ Whenever a creature with power 1 or less is put into your graveyard from the battlefield, you may return that card to the battlefield under your control at the beginning of the next end step if CARDNAME is still on the battlefield.
SVar:DelTrig:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | Execute$ TrigReturn | IsPresent$ Card.Self | PresentZone$ Battlefield | TriggerDescription$ Return creature to the battlefield. SVar:DelTrig:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | Execute$ TrigReturn | IsPresent$ Card.Self | PresentZone$ Battlefield | TriggerDescription$ Return creature to the battlefield.
SVar:TrigReturn:AB$ChangeZone | Cost$ 0 | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard SVar:TrigReturn:AB$ ChangeZone | Cost$ 0 | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Static$ True | Execute$ DBCleanup T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Static$ True | Execute$ DBCleanup
SVar:DBCleanup:DB$Cleanup | ClearTriggered$ True SVar:DBCleanup:DB$Cleanup | ClearTriggered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/shirei_shizos_caretaker.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/shirei_shizos_caretaker.jpg

View File

@@ -6,7 +6,7 @@ K:etbCounter:P1P1:1
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ At the beginning of your upkeep, you may put a +1/+1 counter on CARDNAME. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ At the beginning of your upkeep, you may put a +1/+1 counter on CARDNAME.
T:Mode$ Attacks | ValidCard$ Card.Self | DelayedTrigger$ DelTrigDestroy | TriggerDescription$ When CARDNAME attacks or blocks, destroy it at end of combat. T:Mode$ Attacks | ValidCard$ Card.Self | DelayedTrigger$ DelTrigDestroy | TriggerDescription$ When CARDNAME attacks or blocks, destroy it at end of combat.
T:Mode$ Blocks | ValidCard$ Card.Self | DelayedTrigger$ DelTrigDestroy | Secondary$ True | TriggerDescription$ When CARDNAME attacks or blocks, destroy it at end of combat. T:Mode$ Blocks | ValidCard$ Card.Self | DelayedTrigger$ DelTrigDestroy | Secondary$ True | TriggerDescription$ When CARDNAME attacks or blocks, destroy it at end of combat.
SVar:TrigPutCounter:AB$PutCounter | Cost$ 0 | Defined$ Self | CounterNum$ 1 | CounterType$ P1P1 SVar:TrigPutCounter:AB$ PutCounter | Cost$ 0 | Defined$ Self | CounterNum$ 1 | CounterType$ P1P1
SVar:DelTrigDestroy:Mode$ Phase | Phase$ EndCombat | Execute$ TrigDestroy | TriggerDescription$ Destroy CARDNAME at end of combat. SVar:DelTrigDestroy:Mode$ Phase | Phase$ EndCombat | Execute$ TrigDestroy | TriggerDescription$ Destroy CARDNAME at end of combat.
SVar:TrigDestroy:AB$Destroy | Cost$ 0 | Defined$ Self SVar:TrigDestroy:AB$Destroy | Cost$ 0 | Defined$ Self
SVar:Picture:http://www.wizards.com/global/images/magic/general/vebulid.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/vebulid.jpg

View File

@@ -3,7 +3,7 @@
Name=Albert Einstein 3 Name=Albert Einstein 3
Title=Albert Einstein Title=Albert Einstein
Difficulty=hard Difficulty=hard
Description=WG deck with Garruk Wildspeaker, Needle Storm and Retribution of the Meek Description=WG Retribution of the Meek deck
Icon=Albert Einstein.jpg Icon=Albert Einstein.jpg
Deck Type=constructed Deck Type=constructed
[main] [main]
@@ -14,24 +14,27 @@ Deck Type=constructed
5 Forest|M12 5 Forest|M12
1 Mox Pearl 1 Mox Pearl
1 Mox Emerald 1 Mox Emerald
2 Savannah Lions 1 Savannah Lions
1 Elite Vanguard 1 Elite Vanguard
3 Dryad Militant
1 Isamaru, Hound of Konda 1 Isamaru, Hound of Konda
1 Weathered Wayfarer 1 Weathered Wayfarer
1 Gaddock Teeg 1 Gaddock Teeg
1 Kataki, War's Wage 1 Kataki, War's Wage
2 Accorder Paladin 2 Accorder Paladin
4 Watchwolf 4 Watchwolf
1 Call of the Conclave
4 Kitchen Finks 4 Kitchen Finks
4 Wilt-Leaf Cavaliers 4 Wilt-Leaf Cavaliers
1 Mirri, Cat Warrior 1 Mirri, Cat Warrior
2 Garruk Wildspeaker 1 Garruk Wildspeaker
1 Master of the Wild Hunt 1 Master of the Wild Hunt
3 Mirran Crusader 2 Mirran Crusader
1 Ancient Spider 1 Ancient Spider
1 Hero of Bladehold 1 Hero of Bladehold
1 Aura Shards 1 Aura Shards
4 Retribution of the Meek 4 Retribution of the Meek
4 Swords to Plowshares 2 Swords to Plowshares
2 Needle Storm 2 Path to Exile
1 Needle Storm
[sideboard] [sideboard]

View File

@@ -3,7 +3,7 @@
Name=Elrond 2 Name=Elrond 2
Title=Elrond Title=Elrond
Difficulty=medium Difficulty=medium
Description=RGW Aura deck with Rabid Wombat Description=RGW Aura deck with Aura Gnarlid and Rabid Wombat
Icon=Elrond.jpg Icon=Elrond.jpg
Deck Type=constructed Deck Type=constructed
[main] [main]

View File

@@ -3,7 +3,7 @@
Name=Elrond 3 Name=Elrond 3
Title=Elrond Title=Elrond
Difficulty=hard Difficulty=hard
Description=RGW Aura deck with Kor Spiritdancer Description=RGW Aura deck with Aura Gnarlid, Uril, the Miststalker and Kor Spiritdancer
Icon=Elrond.jpg Icon=Elrond.jpg
Deck Type=constructed Deck Type=constructed
[main] [main]

View File

@@ -0,0 +1,41 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Boros Battalion
Description=Join the Boros Legion and become part of a unified attack force! The "Boros Battalion" deck hammers that point home by way of the battalion mechanic. Whenever you attack with a creature that has battalion and at least two other creatures, an effect that makes your assault even more powerful happens!
Deck Type=constructed
Set=GTC
Image=boros_battalion.jpg
[main]
1 Boros Guildgate|GTC
12 Mountain|RTR
12 Plains|RTR
1 Armored Transport|GTC
1 Bomber Corps|GTC
1 Boros Elite|GTC
2 Canyon Minotaur|M13
1 Court Street Denizen|GTC
2 Daring Skyjek|GTC
1 Ember Beast|GTC
1 Firefist Striker|GTC
1 Firemane Avenger|GTC
2 Fortress Cyclops|GTC
1 Foundry Champion|GTC
1 Ordruun Veteran|GTC
2 Skyknight Legionnaire|GTC
1 Sunhome Guildmage|GTC
2 Warclamp Mastiff|M13
2 Warmind Infantry|GTC
3 Wojek Halberdiers|GTC
1 Act of Treason|GTC
1 Aerial Maneuver|GTC
2 Arrows of Justice|GTC
2 Boros Keyrune|GTC
1 Mark for Death|GTC
1 Mugging|GTC
1 Righteous Charge|GTC
1 Shielded Passage|GTC
[sideboard]

View File

@@ -0,0 +1,41 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Dimir Dementia
Description=House Dimir values secrecy above all, entrusting only its own agents with valuable codes and information. Whenever you cast a card with cipher, you can exile it, encoding it on a creature you control. Then, whenever the creature slides through and damages a player, you'll get to cast that card again!
Deck Type=constructed
Set=GTC
Image=boros_battalion.jpg
[main]
1 Dimir Guildgate|GTC
12 Island|RTR
13 Swamp|RTR
2 Balustrade Spy|GTC
1 Consuming Aberration|GTC
2 Deathcult Rogue|GTC
2 Dinrova Horror|GTC
1 Duskmantle Guildmage|GTC
3 Gutter Skulk|GTC
1 Incursion Specialist|GTC
1 Jace's Phantasm|M13
1 Mindeye Drake|GTC
1 Mortus Strider|GTC
1 Sage's Row Denizen|GTC
1 Vedalken Entrancer|M13
1 Welkin Tern|M13
2 Wight of Precinct Six|GTC
1 Coerced Confession|GTC
2 Death's Approach|GTC
2 Dimir Keyrune|GTC
2 Grisly Spectacle|GTC
1 Last Thoughts|GTC
1 Midnight Recovery|GTC
1 Paranoid Delusions|GTC
1 Rise from the Grave|M13
1 Shadow Slice|GTC
1 Totally Lost|GTC
1 Whispering Madness|GTC
[sideboard]

View File

@@ -0,0 +1,42 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Gruul Goliaths
Description=Join the Boros Legion and become part of a unified attack force! The "Boros Battalion" deck hammers that point home by way of the battalion mechanic. Whenever you attack with a creature that has battalion and at least two other creatures, an effect that makes your assault even more powerful happens!
Deck Type=constructed
Set=GTC
Image=gruul_goliaths.jpg
[main]
13 Forest|RTR
1 Gruul Guildgate|GTC
12 Mountain|RTR
1 Arbor Elf|M13
2 Centaur Courser|M13
2 Disciple of the Old Ways|GTC
1 Duskdale Wurm|M13
2 Fire Elemental|M13
1 Foundry Street Denizen|GTC
1 Ghor-Clan Rampager|GTC
1 Gruul Ragebeast|GTC
1 Primal Huntbeast|M13
1 Ripscale Predator|GTC
1 Rubblehulk|GTC
2 Ruination Wurm|GTC
1 Scab-Clan Charger|GTC
1 Skarrg Guildmage|GTC
1 Skinbrand Goblin|GTC
1 Slaughterhorn|GTC
2 Viashino Shanktail|GTC
2 Zhur-Taa Swine|GTC
1 Alpha Authority|GTC
2 Ground Assault|GTC
2 Gruul Keyrune|GTC
1 Pit Fight|GTC
1 Predator's Rapport|GTC
1 Ranger's Path|M13
1 Verdant Haven|GTC
1 Volcanic Geyser|M13
[sideboard]

View File

@@ -0,0 +1,41 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Orzhov Oppression
Description=The Orzhov Syndicate's underhanded dealings wear down your opponents, whittling away their life totals until you have it all. Cards with the extort mechanic allow you to pay B/W whenever you cast a spell to "drain" each opponent for 1 life. Better yet, the ability works in multiples! Get more and more creatures from the "Orzhov Oppression" deck on the battlefield and you can take larger and larger bites out of opposing life totals every time you cast a spell.
Deck Type=constructed
Set=GTC
Image=orzhov_opression.jpg
[main]
1 Orzhov Guildgate|GTC
12 Plains|RTR
13 Swamp|RTR
2 Basilica Guards|GTC
3 Basilica Screecher|GTC
1 Guardian Lions|M13
1 High Priest of Penance|GTC
2 Kingpin's Pet|GTC
1 Knight of Obligation|GTC
1 Shadow Alley Denizen|GTC
2 Silvercoat Lion|M13
1 Smog Elemental|GTC
2 Syndicate Enforcer|GTC
1 Tormented Soul|M13
1 Treasury Thrull|GTC
1 Vizkopa Guildmage|GTC
1 Zombie Goliath|M13
2 Angelic Edict|GTC
1 Blood Reckoning|M13
2 Dying Wish|GTC
1 Executioner's Swing|GTC
1 Gift of Orzhova|GTC
1 Jayemdae Tome|M13
1 Murder|M13
1 One Thousand Lashes|GTC
2 Orzhov Keyrune|GTC
1 Purge the Profane|GTC
1 Rain of Blades|M13
[sideboard]

View File

@@ -0,0 +1,41 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Simic Synthesis
Description=The Simic Combine is always evolving<6E>literally! The evolve mechanic means your creatures are constantly growing into new and unusual forms. A creature with evolve gets a +1/+1 counter if a larger creature (meaning it has a greater power or a greater toughness) enters the battlefield under your control. After enough evolutions, even the smallest creature can sit on top of the food chain.
Deck Type=constructed
Set=GTC
Image=simic_synthesis.jpg
[main]
12 Forest|RTR
13 Island|RTR
1 Simic Guildgate|GTC
2 Adaptive Snapjaw|GTC
1 Chronomaton|M13
1 Cloudfin Raptor|GTC
2 Crocanura|GTC
1 Crowned Ceratok|GTC
2 Drakewing Krasis|GTC
1 Elusive Krasis|GTC
1 Fathom Mage|GTC
1 Frilled Oculus|GTC
1 Ivy Lane Denizen|GTC
2 Kraken Hatchling|M13
1 Leyline Phantom|GTC
1 Merfolk of the Depths|GTC
1 Sapphire Drake|GTC
2 Shambleshark|GTC
1 Zameck Guildmage|GTC
2 Bioshift|GTC
2 Encrust|M13
2 Forced Adaptation|GTC
1 Hindervines|GTC
2 Simic Keyrune|GTC
1 Sleep|M13
1 Tower Defense|GTC
1 Unexpected Results|GTC
1 Urban Evolution|GTC
[sideboard]

View File

@@ -7075,6 +7075,27 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
} }
} }
} else if (property.startsWith("greatestRememberedCMC")) {
final List<Card> list = new ArrayList<Card>();
for (final Object o : source.getRemembered()) {
if (o instanceof Card) {
list.add(Singletons.getModel().getGame().getCardState((Card) o));
}
}
if (!list.contains(this)) {
return false;
}
for (final Card crd : list) {
if (crd.getRules() != null && crd.getRules().getSplitType() == CardSplitType.Split) {
if (crd.getCMC(Card.SplitCMCMode.LeftSplitCMC) > this.getCMC() || crd.getCMC(Card.SplitCMCMode.RightSplitCMC) > this.getCMC()) {
return false;
}
} else {
if (crd.getCMC() > this.getCMC()) {
return false;
}
}
}
} else if (property.startsWith("lowestCMC")) { } else if (property.startsWith("lowestCMC")) {
final List<Card> list = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield); final List<Card> list = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield);
for (final Card crd : list) { for (final Card crd : list) {
@@ -8067,7 +8088,7 @@ public class Card extends GameEntity implements Comparable<Card> {
for (final Card ca : Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield)) { for (final Card ca : Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield)) {
for (final ReplacementEffect re : ca.getReplacementEffects()) { for (final ReplacementEffect re : ca.getReplacementEffects()) {
HashMap<String, String> params = re.getMapParams(); Map<String, String> params = re.getMapParams();
if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) { if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) {
continue; continue;
} }
@@ -8989,7 +9010,7 @@ public class Card extends GameEntity implements Comparable<Card> {
* the rE * the rE
*/ */
public void addReplacementEffect(final ReplacementEffect replacementEffect) { public void addReplacementEffect(final ReplacementEffect replacementEffect) {
final ReplacementEffect replacementEffectCopy = replacementEffect.getCopy(); final ReplacementEffect replacementEffectCopy = replacementEffect.getCopy(); // doubtful - every caller provides a newly parsed instance, why copy?
replacementEffectCopy.setHostCard(this); replacementEffectCopy.setHostCard(this);
this.getCharacteristics().getReplacementEffects().add(replacementEffectCopy); this.getCharacteristics().getReplacementEffects().add(replacementEffectCopy);
} }
@@ -9110,7 +9131,7 @@ public class Card extends GameEntity implements Comparable<Card> {
*/ */
public boolean hasETBReplacement() { public boolean hasETBReplacement() {
for (final ReplacementEffect re : getReplacementEffects()) { for (final ReplacementEffect re : getReplacementEffects()) {
final HashMap<String, String> params = re.getMapParams(); final Map<String, String> params = re.getMapParams();
if (!(re instanceof ReplaceMoved)) { if (!(re instanceof ReplaceMoved)) {
continue; continue;
} }

View File

@@ -28,6 +28,7 @@ import forge.card.MagicColor;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -269,7 +270,7 @@ public final class CardUtil {
} }
} }
} else if (reflectProperty.equals("Produced")) { } else if (reflectProperty.equals("Produced")) {
final String producedColors = (String) abMana.getTriggeringObject("Produced"); final String producedColors = abMana instanceof AbilitySub ? (String) abMana.getRootAbility().getTriggeringObject("Produced") : (String) abMana.getTriggeringObject("Produced");
for (final String col : Constant.Color.ONLY_COLORS) { for (final String col : Constant.Color.ONLY_COLORS) {
final String s = MagicColor.toShortString(col); final String s = MagicColor.toShortString(col);
if (producedColors.contains(s)) { if (producedColors.contains(s)) {

View File

@@ -19,6 +19,8 @@ package forge;
import java.util.EnumSet; import java.util.EnumSet;
import com.google.common.collect.ImmutableList;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
@@ -46,7 +48,7 @@ public enum Color {
/** The Blue. */ /** The Blue. */
Blue(16); Blue(16);
public static final Color[] WUBRG = new Color[] { White, Blue, Black, Red, Green }; public static final ImmutableList<Color> WUBRG = ImmutableList.of( White, Blue, Black, Red, Green );
@SuppressWarnings("unused") @SuppressWarnings("unused")
private int flag = 0; private int flag = 0;
@@ -128,7 +130,7 @@ public enum Color {
final EnumSet<Color> colors = EnumSet.of(Color.Colorless); final EnumSet<Color> colors = EnumSet.of(Color.Colorless);
for( int i = 0; i < MagicColor.NUMBER_OR_COLORS; i++ ) { for( int i = 0; i < MagicColor.NUMBER_OR_COLORS; i++ ) {
if( cc.hasAnyColor(MagicColor.WUBRG[i]) ) if( cc.hasAnyColor(MagicColor.WUBRG[i]) )
colors.add(Color.WUBRG[i]); colors.add(Color.WUBRG.get(i));
} }
if (colors.size() > 1) { if (colors.size() > 1) {
colors.remove(Color.Colorless); colors.remove(Color.Colorless);

View File

@@ -731,6 +731,10 @@ public class AbilityUtils {
players.add(((Card) rem).getController()); players.add(((Card) rem).getController());
} }
} }
} else if (defined.equals("ImprintedController")) {
for (final Card rem : card.getImprinted()) {
players.add(rem.getController());
}
} else if (defined.startsWith("Triggered")) { } else if (defined.startsWith("Triggered")) {
final SpellAbility root = sa.getRootAbility(); final SpellAbility root = sa.getRootAbility();
Object o = null; Object o = null;

View File

@@ -786,10 +786,11 @@ public class AttachAi extends SpellAbilityAi {
//only add useful keywords unless P/T bonus is significant //only add useful keywords unless P/T bonus is significant
if (totToughness + totPower < 4 && !keywords.isEmpty()) { if (totToughness + totPower < 4 && !keywords.isEmpty()) {
final int pow = totPower;
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return containsUsefulKeyword(keywords, c, sa); return containsUsefulKeyword(keywords, c, sa, pow);
} }
}); });
} }
@@ -805,18 +806,25 @@ public class AttachAi extends SpellAbilityAi {
if (attachSource.isAura() && !attachSource.getName().equals("Daybreak Coronet")) { if (attachSource.isAura() && !attachSource.getName().equals("Daybreak Coronet")) {
// TODO For Auras like Rancor, that aren't as likely to lead to // TODO For Auras like Rancor, that aren't as likely to lead to
// card disadvantage, this check should be skipped // card disadvantage, this check should be skipped
prefList = CardLists.filter(prefList, Predicates.not(Presets.ENCHANTED)); prefList = CardLists.filter(prefList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return !c.isEnchanted() || c.hasKeyword("Hexproof");
}
});
} }
if (!grantingAbilities && keywords.isEmpty()) { if (!grantingAbilities) {
// Probably prefer to Enchant Creatures that Can Attack // Probably prefer to Enchant Creatures that Can Attack
// Filter out creatures that can't Attack or have Defender // Filter out creatures that can't Attack or have Defender
if (keywords.isEmpty()) {
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return !c.isCreature() || CombatUtil.canAttackNextTurn(c); return !c.isCreature() || CombatUtil.canAttackNextTurn(c);
} }
}); });
}
c = ComputerUtilCard.getBestAI(prefList); c = ComputerUtilCard.getBestAI(prefList);
} else { } else {
// If we grant abilities, we may want to put it on something Weak? // If we grant abilities, we may want to put it on something Weak?
@@ -955,9 +963,9 @@ public class AttachAi extends SpellAbilityAi {
* @param sa SpellAbility * @param sa SpellAbility
* @return true, if successful * @return true, if successful
*/ */
private static boolean containsUsefulKeyword(final ArrayList<String> keywords, final Card card, final SpellAbility sa) { private static boolean containsUsefulKeyword(final ArrayList<String> keywords, final Card card, final SpellAbility sa, final int powerBonus) {
for (final String keyword : keywords) { for (final String keyword : keywords) {
if (isUsefulAttachKeyword(keyword, card, sa)) { if (isUsefulAttachKeyword(keyword, card, sa, powerBonus)) {
return true; return true;
} }
} }
@@ -993,7 +1001,7 @@ public class AttachAi extends SpellAbilityAi {
* @param sa SpellAbility * @param sa SpellAbility
* @return true, if is useful keyword * @return true, if is useful keyword
*/ */
private static boolean isUsefulAttachKeyword(final String keyword, final Card card, final SpellAbility sa) { private static boolean isUsefulAttachKeyword(final String keyword, final Card card, final SpellAbility sa, final int powerBonus) {
final PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler(); final PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler();
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
return false; return false;
@@ -1006,14 +1014,14 @@ public class AttachAi extends SpellAbilityAi {
|| keyword.equals("CARDNAME can't be blocked by more than one creature.")); || keyword.equals("CARDNAME can't be blocked by more than one creature."));
// give evasive keywords to creatures that can attack and deal damage // give evasive keywords to creatures that can attack and deal damage
if (evasive) { if (evasive) {
if (card.getNetCombatDamage() <= 0 if (card.getNetCombatDamage() + powerBonus <= 0
|| !CombatUtil.canAttackNextTurn(card) || !CombatUtil.canAttackNextTurn(card)
|| !CombatUtil.canBeBlocked(card)) { || !CombatUtil.canBeBlocked(card)) {
return false; return false;
} }
} else if (keyword.equals("Haste")) { } else if (keyword.equals("Haste")) {
if (!card.hasSickness() || !ph.isPlayerTurn(sa.getActivatingPlayer()) || card.isTapped() if (!card.hasSickness() || !ph.isPlayerTurn(sa.getActivatingPlayer()) || card.isTapped()
|| card.getNetCombatDamage() <= 0 || card.getNetCombatDamage() + powerBonus <= 0
|| card.hasKeyword("CARDNAME can attack as though it had haste.") || card.hasKeyword("CARDNAME can attack as though it had haste.")
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| !CombatUtil.canAttackNextTurn(card)) { || !CombatUtil.canAttackNextTurn(card)) {
@@ -1022,22 +1030,22 @@ public class AttachAi extends SpellAbilityAi {
} else if (keyword.endsWith("Indestructible")) { } else if (keyword.endsWith("Indestructible")) {
return true; return true;
} else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) { } else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) {
if (card.getNetCombatDamage() <= 0 if (card.getNetCombatDamage() + powerBonus <= 0
|| ((!CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card)) || ((!CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card))
&& !CombatUtil.canBlock(card, true))) { && !CombatUtil.canBlock(card, true))) {
return false; return false;
} }
} else if (keyword.equals("Double Strike") || keyword.equals("Lifelink")) { } else if (keyword.equals("Double Strike") || keyword.equals("Lifelink")) {
if (card.getNetCombatDamage() <= 0 if (card.getNetCombatDamage() + powerBonus <= 0
|| (!CombatUtil.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) { || (!CombatUtil.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) {
return false; return false;
} }
} else if (keyword.equals("First Strike")) { } else if (keyword.equals("First Strike")) {
if (card.getNetCombatDamage() <= 0 || card.hasKeyword("Double Strike")) { if (card.getNetCombatDamage() + powerBonus <= 0 || card.hasKeyword("Double Strike")) {
return false; return false;
} }
} else if (keyword.startsWith("Flanking")) { } else if (keyword.startsWith("Flanking")) {
if (card.getNetCombatDamage() <= 0 if (card.getNetCombatDamage() + powerBonus <= 0
|| !CombatUtil.canAttackNextTurn(card) || !CombatUtil.canAttackNextTurn(card)
|| !CombatUtil.canBeBlocked(card)) { || !CombatUtil.canBeBlocked(card)) {
return false; return false;
@@ -1048,18 +1056,18 @@ public class AttachAi extends SpellAbilityAi {
return false; return false;
} }
} else if (keyword.equals("Trample")) { } else if (keyword.equals("Trample")) {
if (card.getNetCombatDamage() <= 1 if (card.getNetCombatDamage() + powerBonus <= 1
|| !CombatUtil.canBeBlocked(card) || !CombatUtil.canBeBlocked(card)
|| !CombatUtil.canAttackNextTurn(card)) { || !CombatUtil.canAttackNextTurn(card)) {
return false; return false;
} }
} else if (keyword.equals("Infect")) { } else if (keyword.equals("Infect")) {
if (card.getNetCombatDamage() <= 0 if (card.getNetCombatDamage() + powerBonus <= 0
|| !CombatUtil.canAttackNextTurn(card)) { || !CombatUtil.canAttackNextTurn(card)) {
return false; return false;
} }
} else if (keyword.equals("Vigilance")) { } else if (keyword.equals("Vigilance")) {
if (card.getNetCombatDamage() <= 0 if (card.getNetCombatDamage() + powerBonus <= 0
|| !CombatUtil.canAttackNextTurn(card) || !CombatUtil.canAttackNextTurn(card)
|| !CombatUtil.canBlock(card, true)) { || !CombatUtil.canBlock(card, true)) {
return false; return false;

View File

@@ -424,8 +424,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* a {@link forge.CardList} object. * a {@link forge.CardList} object.
* @return a {@link forge.Card} object. * @return a {@link forge.Card} object.
*/ */
private static Card basicManaFixing(final Player ai, final List<Card> list) { // Search for a private static Card basicManaFixing(final Player ai, final List<Card> list) { // Search for a Basic Land
// Basic Land
final List<Card> combined = new ArrayList<Card>(ai.getCardsIn(ZoneType.Battlefield)); final List<Card> combined = new ArrayList<Card>(ai.getCardsIn(ZoneType.Battlefield));
combined.addAll(ai.getCardsIn(ZoneType.Hand)); combined.addAll(ai.getCardsIn(ZoneType.Hand));
@@ -458,6 +457,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
result = CardLists.getType(list, minType); result = CardLists.getType(list, minType);
} }
// pick dual lands if available
if (Iterables.any(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS))) {
result = CardLists.filter(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
}
return result.get(0); return result.get(0);
} }
@@ -1173,7 +1177,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} else if (origin.contains(ZoneType.Library) } else if (origin.contains(ZoneType.Library)
&& (type.contains("Basic") || areAllBasics(type))) { && (type.contains("Basic") || areAllBasics(type))) {
c = basicManaFixing(ai, fetchList); c = basicManaFixing(ai, fetchList);
} else if (ZoneType.Hand.equals(destination) && CardLists.getNotType(fetchList, "Creature").size() == 0) { } else if (ZoneType.Hand.equals(destination) && CardLists.getNotType(fetchList, "Creature").isEmpty()) {
c = chooseCreature(ai, fetchList); c = chooseCreature(ai, fetchList);
} else if (ZoneType.Battlefield.equals(destination) || ZoneType.Graveyard.equals(destination)) { } else if (ZoneType.Battlefield.equals(destination) || ZoneType.Graveyard.equals(destination)) {
if (!activator.equals(ai) && sa.hasParam("GainControl")) { if (!activator.equals(ai) && sa.hasParam("GainControl")) {
@@ -1190,7 +1194,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
// Does AI need a land? // Does AI need a land?
List<Card> hand = ai.getCardsIn(ZoneType.Hand); List<Card> hand = ai.getCardsIn(ZoneType.Hand);
if (CardLists.filter(hand, Presets.LANDS).size() == 0 && CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() < 4) { if (CardLists.filter(hand, Presets.LANDS).isEmpty() && CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() < 4) {
boolean canCastSomething = false; boolean canCastSomething = false;
for (Card cardInHand : hand) { for (Card cardInHand : hand) {
canCastSomething |= ComputerUtilMana.payManaCost(cardInHand.getFirstSpellAbility(), ai, true, 0, false); canCastSomething |= ComputerUtilMana.payManaCost(cardInHand.getFirstSpellAbility(), ai, true, 0, false);

View File

@@ -2,6 +2,9 @@ package forge.card.ability.effects;
import java.util.HashMap; import java.util.HashMap;
import javax.swing.JOptionPane;
import forge.Card;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
@@ -9,6 +12,10 @@ import forge.card.ability.SpellAbilityEffect;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.GameAction;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
public class ClashEffect extends SpellAbilityEffect { public class ClashEffect extends SpellAbilityEffect {
@@ -25,7 +32,7 @@ public class ClashEffect extends SpellAbilityEffect {
*/ */
@Override @Override
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final boolean victory = sa.getSourceCard().getController().clashWithOpponent(sa.getSourceCard()); final boolean victory = clashWithOpponent(sa.getSourceCard());
// Run triggers // Run triggers
final HashMap<String, Object> runParams = new HashMap<String, Object>(); final HashMap<String, Object> runParams = new HashMap<String, Object>();
@@ -56,4 +63,85 @@ public class ClashEffect extends SpellAbilityEffect {
Singletons.getModel().getGame().getTriggerHandler().runTrigger(TriggerType.Clashed, runParams, false); Singletons.getModel().getGame().getTriggerHandler().runTrigger(TriggerType.Clashed, runParams, false);
} }
/**
* <p>
* clashWithOpponent.
* </p>
*
* @param source
* a {@link forge.Card} object.
* @return a boolean.
*/
public final boolean clashWithOpponent(final Card source) {
/*
* Each clashing player reveals the top card of his or her library, then
* puts that card on the top or bottom. A player wins if his or her card
* had a higher mana cost.
*
* Clash you win or win you don't. There is no tie.
*/
final Player player = source.getController();
final Player opponent = player.getOpponent();
final ZoneType lib = ZoneType.Library;
final PlayerZone pLib = player.getZone(lib);
final PlayerZone oLib = opponent.getZone(lib);
final StringBuilder reveal = new StringBuilder();
Card pCard = null;
Card oCard = null;
if (pLib.size() > 0) {
pCard = pLib.get(0);
}
if (oLib.size() > 0) {
oCard = oLib.get(0);
}
if ((pLib.size() == 0) && (oLib.size() == 0)) {
return false;
} else if (pLib.isEmpty()) {
clashMoveToTopOrBottom(opponent, oCard);
return false;
} else if (oLib.isEmpty()) {
clashMoveToTopOrBottom(player, pCard);
return true;
} else {
final int pCMC = pCard.getCMC();
final int oCMC = oCard.getCMC();
// TODO: Split cards will return two CMC values, so both players may become winners of clash
reveal.append(player).append(" reveals: ").append(pCard.getName()).append(". CMC = ").append(pCMC);
reveal.append("\r\n");
reveal.append(opponent).append(" reveals: ").append(oCard.getName()).append(". CMC = ").append(oCMC);
reveal.append("\r\n\r\n");
if (pCMC > oCMC) {
reveal.append(player).append(" wins clash.");
} else {
reveal.append(player).append(" loses clash.");
}
JOptionPane.showMessageDialog(null, reveal.toString(), source.getName(), JOptionPane.PLAIN_MESSAGE);
clashMoveToTopOrBottom(player, pCard);
clashMoveToTopOrBottom(opponent, oCard);
// JOptionPane.showMessageDialog(null, reveal.toString(),
// source.getName(), JOptionPane.PLAIN_MESSAGE);
return pCMC > oCMC;
}
}
public final void clashMoveToTopOrBottom(final Player p, final Card c) {
GameAction action = p.getGame().getAction();
boolean putOnTop = p.getController().willPutCardOnTop(c);
if ( putOnTop )
action.moveToLibrary(c);
else
action.moveToBottomOfLibrary(c);
// computer just puts the card back until such time it can make a smarter decision
}
} }

View File

@@ -100,9 +100,7 @@ public class EffectEffect extends SpellAbilityEffect {
final Card eff = new Card(); final Card eff = new Card();
eff.setName(name); eff.setName(name);
eff.addType("Effect"); // Or Emblem eff.addType("Effect"); // Or Emblem
eff.setToken(true); // Set token to true, so when leaving play it gets eff.setToken(true); // Set token to true, so when leaving play it gets nuked
// nuked
eff.addController(controller);
eff.setOwner(controller); eff.setOwner(controller);
eff.setImageKey(sa.hasParam("Image") ? sa.getParam("Image") : hostCard.getImageKey()); eff.setImageKey(sa.hasParam("Image") ? sa.getParam("Image") : hostCard.getImageKey());
eff.setColor(hostCard.getColor()); eff.setColor(hostCard.getColor());

View File

@@ -151,8 +151,6 @@ public class PlayEffect extends SpellAbilityEffect {
} }
if (sa.hasParam("CopyCard")) { if (sa.hasParam("CopyCard")) {
tgtCard = CardDb.getCard(tgtCard).toForgeCard(sa.getActivatingPlayer()); tgtCard = CardDb.getCard(tgtCard).toForgeCard(sa.getActivatingPlayer());
// when copying something stolen:
tgtCard.addController(sa.getActivatingPlayer());
tgtCard.setToken(true); tgtCard.setToken(true);
tgtCard.setCopiedSpell(true); tgtCard.setCopiedSpell(true);

View File

@@ -1,5 +1,6 @@
package forge.card.ability.effects; package forge.card.ability.effects;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -39,8 +40,12 @@ public class RepeatEachEffect extends SpellAbilityEffect {
List<Card> repeatCards = null; List<Card> repeatCards = null;
if (sa.hasParam("RepeatCards")) { if (sa.hasParam("RepeatCards")) {
ZoneType zone = sa.hasParam("Zone") ? ZoneType.smartValueOf(sa.getParam("Zone")) : ZoneType.Battlefield; List<ZoneType> zone = new ArrayList<ZoneType>();
if (sa.hasParam("Zone")) {
zone = ZoneType.listValueOf(sa.getParam("Zone"));
} else {
zone.add(ZoneType.Battlefield);
}
repeatCards = CardLists.getValidCards(game.getCardsIn(zone), repeatCards = CardLists.getValidCards(game.getCardsIn(zone),
sa.getParam("RepeatCards"), source.getController(), source); sa.getParam("RepeatCards"), source.getController(), source);
loopOverCards = true; loopOverCards = true;

View File

@@ -1,12 +1,19 @@
package forge.card.ability.effects; package forge.card.ability.effects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.SpellAbilityEffect; import forge.card.ability.SpellAbilityEffect;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
public class ScryEffect extends SpellAbilityEffect { public class ScryEffect extends SpellAbilityEffect {
@@ -42,7 +49,45 @@ public class ScryEffect extends SpellAbilityEffect {
for (final Player p : tgtPlayers) { for (final Player p : tgtPlayers) {
if ((tgt == null) || p.canBeTargetedBy(sa)) { if ((tgt == null) || p.canBeTargetedBy(sa)) {
p.scry(num); scry(p, num);
}
}
}
/**
* <p>
* scry.
* </p>
*
* @param numScry
* a int.
*/
public final void scry(Player p, int numScry) {
final List<Card> topN = new ArrayList<Card>();
final PlayerZone library = p.getZone(ZoneType.Library);
numScry = Math.min(numScry, library.size());
if ( numScry == 0 )
return;
for (int i = 0; i < numScry; i++) {
topN.add(library.get(i));
}
ImmutablePair<List<Card>, List<Card>> lists = p.getController().arrangeForScry(topN);
List<Card> toTop = lists.getLeft();
List<Card> toBottom = lists.getRight();
if ( null != toBottom) {
for(Card c : toBottom) {
p.getGame().getAction().moveToBottomOfLibrary(c);
}
}
if ( null != toTop ) {
Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus.
for(Card c : toTop) {
p.getGame().getAction().moveToLibrary(c);
} }
} }
} }

View File

@@ -3146,7 +3146,6 @@ public class CardFactoryUtil {
eff.addType("Effect"); // Or Emblem eff.addType("Effect"); // Or Emblem
eff.setToken(true); // Set token to true, so when leaving eff.setToken(true); // Set token to true, so when leaving
// play it gets nuked // play it gets nuked
eff.addController(card.getController());
eff.setOwner(card.getController()); eff.setOwner(card.getController());
eff.setColor(card.getColor()); eff.setColor(card.getColor());
eff.setImmutable(true); eff.setImmutable(true);

View File

@@ -17,7 +17,7 @@
*/ */
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
@@ -36,7 +36,7 @@ public class ReplaceDamage extends ReplacementEffect {
* @param map the map * @param map the map
* @param host the host * @param host the host
*/ */
public ReplaceDamage(HashMap<String, String> map, Card host) { public ReplaceDamage(Map<String, String> map, Card host) {
super(map, host); super(map, host);
} }
@@ -44,7 +44,7 @@ public class ReplaceDamage extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("DamageDone")) { if (!runParams.get("Event").equals("DamageDone")) {
return false; return false;
} }
@@ -110,7 +110,7 @@ public class ReplaceDamage extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("DamageAmount", runParams.get("DamageAmount")); sa.setReplacingObject("DamageAmount", runParams.get("DamageAmount"));
sa.setReplacingObject("Target", runParams.get("Affected")); sa.setReplacingObject("Target", runParams.get("Affected"));
sa.setReplacingObject("Source", runParams.get("DamageSource")); sa.setReplacingObject("Source", runParams.get("DamageSource"));

View File

@@ -17,7 +17,7 @@
*/ */
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -34,7 +34,7 @@ public class ReplaceDiscard extends ReplacementEffect {
* @param params the params * @param params the params
* @param host the host * @param host the host
*/ */
public ReplaceDiscard(final HashMap<String, String> params, final Card host) { public ReplaceDiscard(final Map<String, String> params, final Card host) {
super(params, host); super(params, host);
} }
@@ -42,7 +42,7 @@ public class ReplaceDiscard extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("Discard")) { if (!runParams.get("Event").equals("Discard")) {
return false; return false;
} }
@@ -86,7 +86,7 @@ public class ReplaceDiscard extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Card")); sa.setReplacingObject("Card", runParams.get("Card"));
sa.setReplacingObject("Player", runParams.get("Affected")); sa.setReplacingObject("Player", runParams.get("Affected"));
} }

View File

@@ -17,7 +17,7 @@
*/ */
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
@@ -33,7 +33,7 @@ public class ReplaceDraw extends ReplacementEffect {
* @param params the params * @param params the params
* @param host the host * @param host the host
*/ */
public ReplaceDraw(final HashMap<String, String> params, final Card host) { public ReplaceDraw(final Map<String, String> params, final Card host) {
super(params, host); super(params, host);
} }
@@ -41,7 +41,7 @@ public class ReplaceDraw extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("Draw")) { if (!runParams.get("Event").equals("Draw")) {
return false; return false;
} }

View File

@@ -17,7 +17,7 @@
*/ */
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -34,7 +34,7 @@ public class ReplaceGainLife extends ReplacementEffect {
* @param map the map * @param map the map
* @param host the host * @param host the host
*/ */
public ReplaceGainLife(HashMap<String, String> map, Card host) { public ReplaceGainLife(Map<String, String> map, Card host) {
super(map, host); super(map, host);
} }
@@ -42,7 +42,7 @@ public class ReplaceGainLife extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("GainLife")) { if (!runParams.get("Event").equals("GainLife")) {
return false; return false;
} }
@@ -71,7 +71,7 @@ public class ReplaceGainLife extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("LifeGained", runParams.get("LifeGained")); sa.setReplacingObject("LifeGained", runParams.get("LifeGained"));
} }

View File

@@ -1,6 +1,6 @@
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
@@ -16,7 +16,7 @@ public class ReplaceGameLoss extends ReplacementEffect {
* @param map the map * @param map the map
* @param host the host * @param host the host
*/ */
public ReplaceGameLoss(HashMap<String, String> map, Card host) { public ReplaceGameLoss(Map<String, String> map, Card host) {
super(map, host); super(map, host);
} }
@@ -24,7 +24,7 @@ public class ReplaceGameLoss extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("GameLoss")) { if (!runParams.get("Event").equals("GameLoss")) {
return false; return false;
} }

View File

@@ -1,6 +1,6 @@
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -18,7 +18,7 @@ public class ReplaceMoved extends ReplacementEffect {
* @param mapParams &emsp; HashMap<String, String> * @param mapParams &emsp; HashMap<String, String>
* @param host &emsp; Card * @param host &emsp; Card
*/ */
public ReplaceMoved(final HashMap<String, String> mapParams, final Card host) { public ReplaceMoved(final Map<String, String> mapParams, final Card host) {
super(mapParams, host); super(mapParams, host);
} }
@@ -26,7 +26,7 @@ public class ReplaceMoved extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("Moved")) { if (!runParams.get("Event").equals("Moved")) {
return false; return false;
} }
@@ -66,7 +66,7 @@ public class ReplaceMoved extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Affected")); sa.setReplacingObject("Card", runParams.get("Affected"));
} }

View File

@@ -17,7 +17,7 @@
*/ */
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
@@ -33,7 +33,7 @@ public class ReplaceSetInMotion extends ReplacementEffect {
* @param params the params * @param params the params
* @param host the host * @param host the host
*/ */
public ReplaceSetInMotion(final HashMap<String, String> params, final Card host) { public ReplaceSetInMotion(final Map<String, String> params, final Card host) {
super(params, host); super(params, host);
} }
@@ -41,7 +41,7 @@ public class ReplaceSetInMotion extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("SetInMotion")) { if (!runParams.get("Event").equals("SetInMotion")) {
return false; return false;
} }

View File

@@ -1,6 +1,6 @@
package forge.card.replacement; package forge.card.replacement;
import java.util.HashMap; import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -17,7 +17,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
* @param mapParams &emsp; HashMap<String, String> * @param mapParams &emsp; HashMap<String, String>
* @param host &emsp; Card * @param host &emsp; Card
*/ */
public ReplaceTurnFaceUp(final HashMap<String, String> mapParams, final Card host) { public ReplaceTurnFaceUp(final Map<String, String> mapParams, final Card host) {
super(mapParams, host); super(mapParams, host);
} }
@@ -25,7 +25,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
*/ */
@Override @Override
public boolean canReplace(HashMap<String, Object> runParams) { public boolean canReplace(Map<String, Object> runParams) {
if (!runParams.get("Event").equals("TurnFaceUp")) { if (!runParams.get("Event").equals("TurnFaceUp")) {
return false; return false;
} }
@@ -53,7 +53,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect {
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
public void setReplacingObjects(HashMap<String, Object> runParams, SpellAbility sa) { public void setReplacingObjects(Map<String, Object> runParams, SpellAbility sa) {
sa.setReplacingObject("Card", runParams.get("Affected")); sa.setReplacingObject("Card", runParams.get("Affected"));
} }

View File

@@ -118,7 +118,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
} }
/** The map params, denoting what to replace. */ /** The map params, denoting what to replace. */
private HashMap<String, String> mapParams = new HashMap<String, String>(); private Map<String, String> mapParams = new HashMap<String, String>();
/** /**
* <p> * <p>
@@ -127,7 +127,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* *
* @return a {@link java.util.HashMap} object. * @return a {@link java.util.HashMap} object.
*/ */
public final HashMap<String, String> getMapParams() { public final Map<String, String> getMapParams() {
return this.mapParams; return this.mapParams;
} }
@@ -137,7 +137,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* @param mapParams * @param mapParams
* the mapParams to set * the mapParams to set
*/ */
public final void setMapParams(final HashMap<String, String> mapParams) { public final void setMapParams(final Map<String, String> mapParams) {
this.mapParams = mapParams; this.mapParams = mapParams;
} }
@@ -148,7 +148,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* the run params * the run params
* @return true, if successful * @return true, if successful
*/ */
public abstract boolean canReplace(final HashMap<String, Object> runParams); public abstract boolean canReplace(final Map<String, Object> runParams);
/** /**
* To string. * To string.
@@ -219,7 +219,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* @param spellAbility * @param spellAbility
* the SpellAbility * the SpellAbility
*/ */
public void setReplacingObjects(final HashMap<String, Object> runParams, final SpellAbility spellAbility) { public void setReplacingObjects(final Map<String, Object> runParams, final SpellAbility spellAbility) {
// Should be overridden by replacers that need it. // Should be overridden by replacers that need it.
} }
@@ -231,7 +231,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
* @param host * @param host
* the host * the host
*/ */
public ReplacementEffect(final HashMap<String, String> map, final Card host) { public ReplacementEffect(final Map<String, String> map, final Card host) {
this.setMapParams(map); this.setMapParams(map);
this.setHostCard(host); this.setHostCard(host);
} }

View File

@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import forge.Card; import forge.Card;
import forge.Singletons; import forge.Singletons;
@@ -34,6 +35,7 @@ import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.GuiDialog; import forge.gui.GuiDialog;
import forge.util.FileSection;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
@@ -174,9 +176,9 @@ public class ReplacementHandler {
* @param replacementEffect * @param replacementEffect
* the replacement effect to run * the replacement effect to run
*/ */
private ReplacementResult executeReplacement(final HashMap<String, Object> runParams, private ReplacementResult executeReplacement(final Map<String, Object> runParams,
final ReplacementEffect replacementEffect, final Player decider, final GameState game) { final ReplacementEffect replacementEffect, final Player decider, final GameState game) {
final HashMap<String, String> mapParams = replacementEffect.getMapParams(); final Map<String, String> mapParams = replacementEffect.getMapParams();
SpellAbility effectSA = null; SpellAbility effectSA = null;
@@ -257,7 +259,7 @@ public class ReplacementHandler {
* @return A finished instance * @return A finished instance
*/ */
public static ReplacementEffect parseReplacement(final String repParse, final Card host) { public static ReplacementEffect parseReplacement(final String repParse, final Card host) {
final HashMap<String, String> mapParams = ReplacementHandler.parseParams(repParse); final Map<String, String> mapParams = FileSection.parseToMap(repParse, "$", "|");
return ReplacementHandler.parseReplacement(mapParams, host); return ReplacementHandler.parseReplacement(mapParams, host);
} }
@@ -272,7 +274,7 @@ public class ReplacementHandler {
* The card that hosts the replacement effect * The card that hosts the replacement effect
* @return The finished instance * @return The finished instance
*/ */
public static ReplacementEffect parseReplacement(final HashMap<String, String> mapParams, final Card host) { public static ReplacementEffect parseReplacement(final Map<String, String> mapParams, final Card host) {
ReplacementEffect ret = null; ReplacementEffect ret = null;
final String eventToReplace = mapParams.get("Event"); final String eventToReplace = mapParams.get("Event");
@@ -301,45 +303,4 @@ public class ReplacementHandler {
return ret; return ret;
} }
/**
* <p>
* parseParams.
* </p>
*
* @param repParse
* a {@link java.lang.String} object.
* @return a {@link java.util.HashMap} object.
*/
private static HashMap<String, String> parseParams(final String repParse) {
final HashMap<String, String> mapParams = new HashMap<String, String>();
if (repParse.length() == 0) {
throw new RuntimeException("ReplacementFactory : registerTrigger -- trigParse too short");
}
final String[] params = repParse.split("\\|");
for (int i = 0; i < params.length; i++) {
params[i] = params[i].trim();
}
for (final String param : params) {
final String[] splitParam = param.split("\\$");
for (int i = 0; i < splitParam.length; i++) {
splitParam[i] = splitParam[i].trim();
}
if (splitParam.length != 2) {
final StringBuilder sb = new StringBuilder();
sb.append("ReplacementFactory Parsing Error in registerTrigger() : Split length of ");
sb.append(param).append(" is not 2.");
throw new RuntimeException(sb.toString());
}
mapParams.put(splitParam[0], splitParam[1]);
}
return mapParams;
}
} }

View File

@@ -20,6 +20,7 @@ package forge.card.spellability;
import java.security.InvalidParameterException; import java.security.InvalidParameterException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -498,7 +499,7 @@ public class SpellPermanent extends Spell {
for (final ReplacementEffect re : card.getReplacementEffects()) { for (final ReplacementEffect re : card.getReplacementEffects()) {
// These Replacements all care for ETB effects // These Replacements all care for ETB effects
final HashMap<String, String> params = re.getMapParams(); final Map<String, String> params = re.getMapParams();
if (!(re instanceof ReplaceMoved)) { if (!(re instanceof ReplaceMoved)) {
continue; continue;
} }

View File

@@ -405,6 +405,10 @@ public class WrappedAbility extends Ability implements ISpellAbility {
return; return;
} }
} }
} else {
if (triggerParams.containsKey("DelayedTrigger")) {
//TODO: The only card with an optional delayed trigger is Shirei, Shizo's Caretaker,
// needs to be expanded when a more difficult cards comes up
} else { } else {
ArrayList<Object> tgts = null; ArrayList<Object> tgts = null;
// make sure the targets won't change // make sure the targets won't change
@@ -412,7 +416,7 @@ public class WrappedAbility extends Ability implements ISpellAbility {
tgts = new ArrayList<Object>(sa.getTarget().getTargetChoices().getTargets()); tgts = new ArrayList<Object>(sa.getTarget().getTargetChoices().getTargets());
} }
// This isn't quite right, but better than canPlayAI // This isn't quite right, but better than canPlayAI
if (!sa.doTrigger(this.isMandatory(), (AIPlayer)decider)) { if (!sa.doTrigger(this.isMandatory(), (AIPlayer) decider)) {
return; return;
} }
if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) { if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) {
@@ -423,6 +427,7 @@ public class WrappedAbility extends Ability implements ISpellAbility {
} }
} }
} }
}
if (getActivatingPlayer().isHuman()) { if (getActivatingPlayer().isHuman()) {
game.getActionPlay().playSpellAbilityNoStack(getActivatingPlayer(), sa, true); game.getActionPlay().playSpellAbilityNoStack(getActivatingPlayer(), sa, true);

View File

@@ -84,9 +84,9 @@ public class InputMulligan extends Input {
@Override @Override
public final void selectButtonCancel() { public final void selectButtonCancel() {
final Player humanPlayer = Singletons.getControl().getPlayer(); final Player humanPlayer = Singletons.getControl().getPlayer();
final int newHand = humanPlayer.doMulligan(); humanPlayer.doMulligan();
if (newHand == 0) { if (humanPlayer.getCardsIn(ZoneType.Hand).isEmpty()) {
this.end(); this.end();
} else { } else {
ButtonUtil.enableAllFocusOk(); ButtonUtil.enableAllFocusOk();

View File

@@ -332,7 +332,6 @@ public class GameNew {
for (final IPaperCard cp : cards) { for (final IPaperCard cp : cards) {
Card c = cp.toForgeCard(player); Card c = cp.toForgeCard(player);
c.setOwner(player); c.setOwner(player);
c.addController(player);
bf.add(c, false); bf.add(c, false);
c.setSickness(true); c.setSickness(true);
c.setStartsGameInPlay(true); c.setStartsGameInPlay(true);

View File

@@ -118,8 +118,12 @@ public class GameState {
endOfTurn = new EndOfTurn(this); endOfTurn = new EndOfTurn(this);
endOfCombat = new EndOfCombat(this); endOfCombat = new EndOfCombat(this);
if ( match0.getGameType() == GameType.Quest)
events.register(Singletons.getModel().getQuest()); // this one listens to player's mulligans ATM
events.register(Singletons.getControl().getSoundSystem()); events.register(Singletons.getControl().getSoundSystem());
events.register(gameLog); events.register(gameLog);
} }
/** /**

View File

@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Random; import java.util.Random;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
@@ -1222,4 +1223,19 @@ public class ComputerUtil {
return (handList.size() > AI_MULLIGAN_THRESHOLD) && hasLittleCmc0Cards; return (handList.size() > AI_MULLIGAN_THRESHOLD) && hasLittleCmc0Cards;
} }
public static boolean scryWillMoveCardToBottomOfLibrary(AIPlayer player, Card c) {
boolean bottom = false;
if (c.isBasicLand()) {
List<Card> bl = player.getCardsIn(ZoneType.Battlefield);
int nBasicLands = Iterables.size(Iterables.filter(bl, CardPredicates.Presets.BASIC_LANDS));
bottom = nBasicLands > 5; // if control more than 5 Basic land, probably don't need more
} else if (c.isCreature()) {
List<Card> cl = player.getCardsIn(ZoneType.Battlefield);
cl = CardLists.filter(cl, CardPredicates.Presets.CREATURES);
bottom = cl.size() > 5; // if control more than 5 Creatures, probably don't need more
}
return bottom;
}
} }

View File

@@ -124,14 +124,14 @@ public class ComputerUtilCard {
*/ */
public static Card getBestLandAI(final List<Card> list) { public static Card getBestLandAI(final List<Card> list) {
final List<Card> land = CardLists.filter(list, CardPredicates.Presets.LANDS); final List<Card> land = CardLists.filter(list, CardPredicates.Presets.LANDS);
if (!(land.size() > 0)) { if (land.isEmpty()) {
return null; return null;
} }
// prefer to target non basic lands // prefer to target non basic lands
final List<Card> nbLand = CardLists.filter(land, Predicates.not(CardPredicates.Presets.BASIC_LANDS)); final List<Card> nbLand = CardLists.filter(land, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
if (nbLand.size() > 0) { if (!nbLand.isEmpty()) {
// TODO - Rank non basics? // TODO - Rank non basics?
return Aggregates.random(nbLand); return Aggregates.random(nbLand);
} }
@@ -243,11 +243,11 @@ public class ComputerUtilCard {
public static Card getBestAI(final List<Card> list) { public static Card getBestAI(final List<Card> list) {
// Get Best will filter by appropriate getBest list if ALL of the list // Get Best will filter by appropriate getBest list if ALL of the list
// is of that type // is of that type
if (CardLists.getNotType(list, "Creature").size() == 0) { if (CardLists.getNotType(list, "Creature").isEmpty()) {
return ComputerUtilCard.getBestCreatureAI(list); return ComputerUtilCard.getBestCreatureAI(list);
} }
if (CardLists.getNotType(list, "Land").size() == 0) { if (CardLists.getNotType(list, "Land").isEmpty()) {
return getBestLandAI(list); return getBestLandAI(list);
} }

View File

@@ -1695,15 +1695,19 @@ public class ComputerUtilCombat {
Card lastBlocker = null; Card lastBlocker = null;
for (final Card b : block) { for (final Card b : block) {
lastBlocker = b; lastBlocker = b;
final int enoughDamageToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true); final int dmgToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true);
if (enoughDamageToKill <= dmgCanDeal) { if (dmgToKill <= dmgCanDeal) {
damageMap.put(b, enoughDamageToKill); damageMap.put(b, dmgToKill);
dmgCanDeal -= enoughDamageToKill; dmgCanDeal -= dmgToKill;
} else { } else {
damageMap.put(b, dmgCanDeal); // if it can't be killed choose the minimum damage
dmgCanDeal = 0; int dmg = Math.min(b.getLethalDamage(), dmgCanDeal);
damageMap.put(b, dmg);
dmgCanDeal -= dmg;
if (dmgCanDeal <= 0) {
break; break;
} }
}
} // for } // for
if (dmgCanDeal > 0 ) { // if any damage left undistributed, if (dmgCanDeal > 0 ) { // if any damage left undistributed,
@@ -1829,7 +1833,7 @@ public class ComputerUtilCombat {
// Predict replacement effects // Predict replacement effects
for (final Card ca : game.getCardsIn(ZoneType.Battlefield)) { for (final Card ca : game.getCardsIn(ZoneType.Battlefield)) {
for (final ReplacementEffect re : ca.getReplacementEffects()) { for (final ReplacementEffect re : ca.getReplacementEffects()) {
HashMap<String, String> params = re.getMapParams(); Map<String, String> params = re.getMapParams();
if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) { if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) {
continue; continue;
} }

View File

@@ -0,0 +1,15 @@
package forge.game.event;
import forge.game.player.Player;
/**
* TODO: Write javadoc for this type.
*
*/
public class MulliganEvent extends Event {
public final Player player;
public MulliganEvent(Player p) {
player = p;
}
}

View File

@@ -265,7 +265,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) { if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) {
this.setPlayersPriorityPermission(false); this.setPlayersPriorityPermission(false);
} else { } else {
this.getPlayerTurn().drawCards(1, true); this.getPlayerTurn().drawCard();
} }
break; break;

View File

@@ -20,7 +20,6 @@ package forge.game.player;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import com.google.common.collect.Iterables;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
@@ -135,41 +134,6 @@ public class AIPlayer extends Player {
// ///////////////////////// // /////////////////////////
/** {@inheritDoc} */
@Override
protected final void doScry(final List<Card> topN, final int n) {
int num = n;
for (int i = 0; i < num; i++) {
boolean bottom = false;
if (topN.get(i).isBasicLand()) {
List<Card> bl = this.getCardsIn(ZoneType.Battlefield);
int nBasicLands = Iterables.size(Iterables.filter(bl, CardPredicates.Presets.BASIC_LANDS));
bottom = nBasicLands > 5; // if control more than 5 Basic land,
// probably don't need more
} else if (topN.get(i).isCreature()) {
List<Card> cl = this.getCardsIn(ZoneType.Battlefield);
cl = CardLists.filter(cl, CardPredicates.Presets.CREATURES);
bottom = cl.size() > 5; // if control more than 5 Creatures,
// probably don't need more
}
if (bottom) {
final Card c = topN.get(i);
game.getAction().moveToBottomOfLibrary(c);
// topN.remove(c);
}
}
num = topN.size();
// put the rest on top in random order
for (int i = 0; i < num; i++) {
final Random rndm = MyRandom.getRandom();
final int r = rndm.nextInt(topN.size());
final Card c = topN.get(r);
game.getAction().moveToLibrary(c);
topN.remove(r);
}
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void sacrificePermanent(final String prompt, final List<Card> choices) { public final void sacrificePermanent(final String prompt, final List<Card> choices) {
@@ -180,25 +144,6 @@ public class AIPlayer extends Player {
} }
} }
/** {@inheritDoc} */
@Override
protected final void clashMoveToTopOrBottom(final Card c) {
// computer just puts the card back until such time it can make a
// smarter decision
game.getAction().moveToLibrary(c);
}
/*
* (non-Javadoc)
*
* @see forge.Player#discard_Chains_of_Mephistopheles()
*/
@Override
protected final void discardChainsOfMephistopheles() {
this.discard(1, null);
this.drawCard();
}
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.game.player.Player#getType() * @see forge.game.player.Player#getType()
*/ */

View File

@@ -24,13 +24,9 @@ import forge.Singletons;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.Input;
import forge.game.GameState; import forge.game.GameState;
import forge.game.GameType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.GuiDialog; import forge.gui.GuiDialog;
import forge.gui.match.CMatchUI;
import forge.quest.QuestController;
import forge.quest.bazaar.QuestItemType;
/** /**
* <p> * <p>
@@ -103,44 +99,6 @@ public class HumanPlayer extends Player {
} }
} }
/*
* (non-Javadoc)
*
* @see forge.Player#discard_Chains_of_Mephistopheles()
*/
/**
*
*/
@Override
protected final void discardChainsOfMephistopheles() {
Singletons.getModel().getMatch().getInput().setInputInterrupt(PlayerUtil.inputChainsDiscard());
}
/** {@inheritDoc} */
@Override
protected final void doScry(final List<Card> topN, final int n) {
int num = n;
for (int i = 0; i < num; i++) {
final Card c = GuiChoose.oneOrNone("Put on bottom of library.", topN);
if (c != null) {
topN.remove(c);
game.getAction().moveToBottomOfLibrary(c);
} else {
// no card chosen for the bottom
break;
}
}
num = topN.size();
for (int i = 0; i < num; i++) {
final Card c = GuiChoose.one("Put on top of library.", topN);
if (c != null) {
topN.remove(c);
game.getAction().moveToLibrary(c);
}
// no else - a card must have been chosen
}
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void sacrificePermanent(final String prompt, final List<Card> choices) { public final void sacrificePermanent(final String prompt, final List<Card> choices) {
@@ -148,21 +106,6 @@ public class HumanPlayer extends Player {
Singletons.getModel().getMatch().getInput().setInput(in); Singletons.getModel().getMatch().getInput().setInput(in);
} }
/** {@inheritDoc} */
@Override
protected final void clashMoveToTopOrBottom(final Card c) {
String choice = "";
final String[] choices = { "top", "bottom" };
CMatchUI.SINGLETON_INSTANCE.setCard(c);
choice = GuiChoose.one(c.getName() + " - Top or bottom of Library", choices);
if (choice.equals("bottom")) {
game.getAction().moveToBottomOfLibrary(c);
} else {
game.getAction().moveToLibrary(c);
}
}
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.game.player.Player#getType() * @see forge.game.player.Player#getType()
*/ */
@@ -171,19 +114,6 @@ public class HumanPlayer extends Player {
return PlayerType.HUMAN; return PlayerType.HUMAN;
} }
@Override
public final int doMulligan() {
int newHand = super.doMulligan();
final QuestController quest = Singletons.getModel().getQuest();
final boolean isQuest = Singletons.getModel().getMatch().getGameType().equals(GameType.Quest);
if (isQuest && quest.getAssets().hasItem(QuestItemType.SLEIGHT) && (getStats().getMulliganCount() == 1)) {
drawCard();
newHand++;
getStats().notifyOpeningHandSize(newHand);
}
return newHand;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.game.player.Player#getController() * @see forge.game.player.Player#getController()
*/ */

View File

@@ -26,7 +26,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import javax.swing.JOptionPane;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@@ -42,6 +41,8 @@ import forge.Constant.Preferences;
import forge.CounterType; import forge.CounterType;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityUtils;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
@@ -50,6 +51,7 @@ import forge.card.replacement.ReplacementResult;
import forge.card.spellability.Ability; import forge.card.spellability.Ability;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.card.staticability.StaticAbility; import forge.card.staticability.StaticAbility;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
@@ -61,9 +63,11 @@ import forge.game.event.CardDiscardedEvent;
import forge.game.event.DrawCardEvent; import forge.game.event.DrawCardEvent;
import forge.game.event.LandPlayedEvent; import forge.game.event.LandPlayedEvent;
import forge.game.event.LifeLossEvent; import forge.game.event.LifeLossEvent;
import forge.game.event.MulliganEvent;
import forge.game.event.PoisonCounterEvent; import forge.game.event.PoisonCounterEvent;
import forge.game.event.ShuffleEvent; import forge.game.event.ShuffleEvent;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZone;
import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.PlayerZoneBattlefield;
import forge.game.zone.Zone; import forge.game.zone.Zone;
@@ -124,6 +128,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
/** The num drawn this turn. */ /** The num drawn this turn. */
private int numDrawnThisTurn = 0; private int numDrawnThisTurn = 0;
private int numDrawnThisDrawStep = 0;
/** The slowtrip list. */ /** The slowtrip list. */
private List<Card> slowtripList = new ArrayList<Card>(); private List<Card> slowtripList = new ArrayList<Card>();
@@ -191,6 +196,10 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
this.setName(lobbyPlayer.getName()); this.setName(lobbyPlayer.getName());
} }
public GameState getGame() { // I'll probably regret about this
return game;
}
public final PlayerStatistics getStats() { public final PlayerStatistics getStats() {
return stats; return stats;
} }
@@ -1224,16 +1233,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
return this.drawCards(1); return this.drawCards(1);
} }
/**
* <p>
* drawCards.
* </p>
*
* @return a List<Card> of cards actually drawn
*/
public final List<Card> drawCards() {
return this.drawCards(1);
}
/** /**
* <p> * <p>
@@ -1244,70 +1243,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
*/ */
public abstract boolean dredge(); public abstract boolean dredge();
/**
* <p>
* drawCards.
* </p>
*
* @param n
* a int.
* @return a List<Card> of cards actually drawn
*/
public final List<Card> drawCards(final int n) {
return this.drawCards(n, false);
}
/**
* <p>
* drawCards.
* </p>
*
* @param n
* a int.
* @param firstFromDraw
* true if this is the card drawn from that player's draw step
* each turn
* @return a List<Card> of cards actually drawn
*/
public final List<Card> drawCards(final int n, final boolean firstFromDraw) {
final List<Card> drawn = new ArrayList<Card>();
if (!this.canDraw()) {
return drawn;
}
for (int i = 0; i < n; i++) {
// TODO: multiple replacements need to be selected by the controller
if (this.getDredge().size() != 0) {
if (this.dredge()) {
continue;
}
}
if (!firstFromDraw && game.isCardInPlay("Chains of Mephistopheles")) {
if (!this.getZone(ZoneType.Hand).isEmpty()) {
if (this.isHuman()) {
this.discardChainsOfMephistopheles();
} else { // Computer
this.discard(1, null);
// true causes this code not to be run again
drawn.addAll(this.drawCards(1, true));
}
} else {
this.mill(1);
}
} else {
drawn.addAll(this.doDraw());
}
}
// Play the Draw sound
Singletons.getModel().getGame().getEvents().post(new DrawCardEvent());
return drawn;
}
/** /**
* *
* TODO Write javadoc for this method. * TODO Write javadoc for this method.
@@ -1318,20 +1253,53 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
* a GamePlayerRating object * a GamePlayerRating object
* @return an int * @return an int
*/ */
public int doMulligan() { public void doMulligan() {
final List<Card> hand = new ArrayList<Card>(getCardsIn(ZoneType.Hand)); final List<Card> hand = new ArrayList<Card>(getCardsIn(ZoneType.Hand));
for (final Card c : hand) { for (final Card c : hand) {
game.getAction().moveToLibrary(c); game.getAction().moveToLibrary(c);
} }
shuffle(); shuffle();
final int newHand = hand.size() - 1; drawCards(hand.size() - 1);
for (int i = 0; i < newHand; i++) {
drawCard(); game.getEvents().post(new MulliganEvent(this)); // quest listener may interfere here
} final int newHand = getCardsIn(ZoneType.Hand).size();
game.getGameLog().add("Mulligan", this + " has mulliganed down to " + newHand + " cards.", 0); game.getGameLog().add("Mulligan", this + " has mulliganed down to " + newHand + " cards.", 0);
stats.notifyHasMulliganed(); stats.notifyHasMulliganed();
stats.notifyOpeningHandSize(newHand); stats.notifyOpeningHandSize(newHand);
return newHand; }
/**
* <p>
* drawCards.
* </p>
*
* @param n
* a int.
* @return a List<Card> of cards actually drawn
*/
public final List<Card> drawCards(final int n) {
final List<Card> drawn = new ArrayList<Card>();
if (!this.canDraw()) {
return drawn;
}
for (int i = 0; i < n; i++) {
// TODO: multiple replacements need to be selected by the controller
if (!this.getDredge().isEmpty()) {
if (this.dredge()) {
continue;
}
}
drawn.addAll(this.doDraw());
}
// Play the Draw sound
game.getEvents().post(new DrawCardEvent());
return drawn;
} }
/** /**
@@ -1354,7 +1322,46 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
return drawn; return drawn;
} }
if (library.size() != 0) { // ======== Chains of Mephistopheles hardcode. =========
// This card requires player to either discard a card, and then he may proceed drawing, or mill 1 - then no draw will happen
// It's oracle-worded as a replacement effect ("If a player would draw a card ... discards a card instead") (rule 419.1a)
// Yet, gatherer's rulings read: The effect is cumulative. If there are two of these on the battlefield, each of them will modify each draw
// That means player isn't supposed to choose one replacement effect out of two (generated by Chains Of Mephistopheles), but both happen.
// So it's not a common replacement effect and has to handled by special code.
// This is why the code is placed after any other replacement effects could affect the draw event.
List<Card> chainsList = null;
for(Card c: game.getCardsInGame()) {
if ( c.getName().equals("Chains of Mephistopheles") ) {
if ( null == chainsList )
chainsList = new ArrayList<Card>();
chainsList.add(c);
}
}
if (chainsList != null && (numDrawnThisDrawStep > 0 || !game.getPhaseHandler().is(PhaseType.DRAW))) {
for(Card c : chainsList) {
// I have to target this player - don't know how to do it.
Target target = new Target(c, null, "");
target.addTarget(this);
if (getCardsIn(ZoneType.Hand).isEmpty()) {
SpellAbility saMill = AbilityFactory.getAbility(c.getSVar("MillOne"), c);
saMill.setActivatingPlayer(c.getController());
saMill.setTarget(target);
AbilityUtils.resolve(saMill, false);
return drawn; // Draw is cancelled
} else {
SpellAbility saDiscard = AbilityFactory.getAbility(c.getSVar("DiscardOne"), c);
saDiscard.setActivatingPlayer(c.getController());
saDiscard.setTarget(target);
AbilityUtils.resolve(saDiscard, false);
}
}
}
// End of = Chains of Mephistopheles hardcode. =========
if (!library.isEmpty()) {
Card c = library.get(0); Card c = library.get(0);
c = game.getAction().moveToHand(c); c = game.getAction().moveToHand(c);
@@ -1377,6 +1384,8 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
this.setLastDrawnCard(c); this.setLastDrawnCard(c);
c.setDrawnThisTurn(true); c.setDrawnThisTurn(true);
this.numDrawnThisTurn++; this.numDrawnThisTurn++;
if ( game.getPhaseHandler().is(PhaseType.DRAW))
this.numDrawnThisDrawStep++;
// Miracle draws // Miracle draws
if (this.numDrawnThisTurn == 1 && game.getPhaseHandler().getTurn() != 0) { if (this.numDrawnThisTurn == 1 && game.getPhaseHandler().getTurn() != 0) {
@@ -1556,6 +1565,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
*/ */
public final void resetNumDrawnThisTurn() { public final void resetNumDrawnThisTurn() {
this.numDrawnThisTurn = 0; this.numDrawnThisTurn = 0;
this.numDrawnThisDrawStep = 0;
} }
/** /**
@@ -1575,11 +1585,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
// / // /
// ////////////////////////////// // //////////////////////////////
/**
* Discard_ chains_of_ mephistopheles.
*/
protected abstract void discardChainsOfMephistopheles();
/** /**
* <p> * <p>
* discard. * discard.
@@ -1843,35 +1848,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
// ////////////////////////////// // //////////////////////////////
// ////////////////////////////// // //////////////////////////////
/**
* <p>
* doScry.
* </p>
*
* @param topN
* a {@link forge.CardList} object.
* @param n
* a int.
*/
protected abstract void doScry(List<Card> topN, int n);
/**
* <p>
* scry.
* </p>
*
* @param numScry
* a int.
*/
public final void scry(int numScry) {
final List<Card> topN = new ArrayList<Card>();
final PlayerZone library = this.getZone(ZoneType.Library);
numScry = Math.min(numScry, library.size());
for (int i = 0; i < numScry; i++) {
topN.add(library.get(i));
}
this.doScry(topN, topN.size());
}
// ///////////////////////////// // /////////////////////////////
@@ -2696,87 +2673,6 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
this.lifeLostThisTurn = n; this.lifeLostThisTurn = n;
} }
// //////////////////////////////
//
// Clash
//
// ///////////////////////////////
/**
* <p>
* clashWithOpponent.
* </p>
*
* @param source
* a {@link forge.Card} object.
* @return a boolean.
*/
public final boolean clashWithOpponent(final Card source) {
/*
* Each clashing player reveals the top card of his or her library, then
* puts that card on the top or bottom. A player wins if his or her card
* had a higher mana cost.
*
* Clash you win or win you don't. There is no tie.
*/
final Player player = source.getController();
final Player opponent = player.getOpponent();
final ZoneType lib = ZoneType.Library;
final PlayerZone pLib = player.getZone(lib);
final PlayerZone oLib = opponent.getZone(lib);
final StringBuilder reveal = new StringBuilder();
Card pCard = null;
Card oCard = null;
if (pLib.size() > 0) {
pCard = pLib.get(0);
}
if (oLib.size() > 0) {
oCard = oLib.get(0);
}
if ((pLib.size() == 0) && (oLib.size() == 0)) {
return false;
} else if (pLib.size() == 0) {
opponent.clashMoveToTopOrBottom(oCard);
return false;
} else if (oLib.size() == 0) {
player.clashMoveToTopOrBottom(pCard);
return true;
} else {
final int pCMC = pCard.getCMC();
final int oCMC = oCard.getCMC();
reveal.append(player).append(" reveals: ").append(pCard.getName()).append(". CMC = ").append(pCMC);
reveal.append("\r\n");
reveal.append(opponent).append(" reveals: ").append(oCard.getName()).append(". CMC = ").append(oCMC);
reveal.append("\r\n\r\n");
if (pCMC > oCMC) {
reveal.append(player).append(" wins clash.");
} else {
reveal.append(player).append(" loses clash.");
}
JOptionPane.showMessageDialog(null, reveal.toString(), source.getName(), JOptionPane.PLAIN_MESSAGE);
player.clashMoveToTopOrBottom(pCard);
opponent.clashMoveToTopOrBottom(oCard);
// JOptionPane.showMessageDialog(null, reveal.toString(),
// source.getName(), JOptionPane.PLAIN_MESSAGE);
return pCMC > oCMC;
}
}
/**
* <p>
* clashMoveToTopOrBottom.
* </p>
*
* @param c
* a {@link forge.Card} object.
*/
protected abstract void clashMoveToTopOrBottom(Card c);
/** /**
* a Player or Planeswalker that this Player must attack if able in an * a Player or Planeswalker that this Player must attack if able in an
* upcoming combat. This is cleared at the end of each combat. * upcoming combat. This is cleared at the end of each combat.

View File

@@ -3,6 +3,8 @@ package forge.game.player;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.GameEntity; import forge.GameEntity;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -104,4 +106,6 @@ public abstract class PlayerController {
/** Shows the card to this player*/ /** Shows the card to this player*/
public abstract void reveal(String string, List<Card> cards, ZoneType zone, Player owner); public abstract void reveal(String string, List<Card> cards, ZoneType zone, Player owner);
public abstract ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN);
public abstract boolean willPutCardOnTop(Card c);
} }

View File

@@ -1,8 +1,12 @@
package forge.game.player; package forge.game.player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.GameEntity; import forge.GameEntity;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
@@ -228,4 +232,27 @@ public class PlayerControllerAi extends PlayerController {
// We don't know how to reveal cards to AI // We don't know how to reveal cards to AI
} }
@Override
public ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN) {
List<Card> toBottom = new ArrayList<Card>();
List<Card> toTop = new ArrayList<Card>();
for (Card c: topN) {
if (ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c))
toBottom.add(c);
else
toTop.add(c);
}
// put the rest on top in random order
Collections.shuffle(toTop);
return ImmutablePair.of(toTop, toBottom);
}
@Override
public boolean willPutCardOnTop(Card c) {
return true; // AI does not know what will happen next (another clash or that would become his topdeck)
}
} }

View File

@@ -8,6 +8,7 @@ import java.util.Map;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.GameEntity; import forge.GameEntity;
@@ -288,4 +289,33 @@ public class PlayerControllerHuman extends PlayerController {
message = String.format("Looking at %s's %s", owner, zone); message = String.format("Looking at %s's %s", owner, zone);
GuiChoose.oneOrNone(message, cards); GuiChoose.oneOrNone(message, cards);
} }
@Override
public ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN) {
List<Card> toBottom = null;
List<Card> toTop = null;
if (topN.size() == 1) {
if (willPutCardOnTop(topN.get(0)))
toTop = topN;
else
toBottom = topN;
} else {
toBottom = GuiChoose.order("Select cards to be put on the bottom of your library", "Cards to put on the bottom", -1, topN, null, null);
topN.removeAll(toBottom);
if ( topN.isEmpty() )
toTop = null;
else if ( topN.size() == 1 )
toTop = topN;
else
toTop = GuiChoose.order("Arrange cards to be put on top of your library", "Cards arranged", 0, topN, null, null);
}
return ImmutablePair.of(toTop, toBottom);
}
@Override
public boolean willPutCardOnTop(Card c) {
return GuiDialog.confirm(c, "Where will you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} );
}
} }

View File

@@ -153,45 +153,6 @@ public final class PlayerUtil {
return target; return target;
} // input_discard() } // input_discard()
/**
* <p>
* input_chainsDiscard.
* </p>
*
* @return a {@link forge.control.input.Input} object.
*/
public static Input inputChainsDiscard() {
final Input target = new Input() {
private static final long serialVersionUID = 2856894846224546303L;
@Override
public void showMessage() {
if (Singletons.getControl().getPlayer().getZone(ZoneType.Hand).size() == 0) {
this.stop();
}
CMatchUI.SINGLETON_INSTANCE.showMessage("Chains of Mephistopheles:\n" + "Select a card to discard");
ButtonUtil.disableAll();
}
@Override
public void selectCard(final Card card) {
Zone zone = Singletons.getModel().getGame().getZoneOf(card);
if (zone.is(ZoneType.Hand)) {
card.getController().discard(card, null);
this.done();
}
}
private void done() {
this.stop();
// hack to not trigger Chains of Mephistopheles recursively
Singletons.getControl().getPlayer().drawCards(1, true);
}
};
return target;
} // input_chainsDiscard()
/** /**
* <p> * <p>
* input_sacrificePermanent. * input_sacrificePermanent.

View File

@@ -15,35 +15,18 @@ import forge.util.MyRandom;
*/ */
public class GuiDialog { public class GuiDialog {
/** private static final String[] defaultConfirmOptions = { "Yes", "No" };
* <p>
* showYesNoDialog.
* </p>
*
* @param c
* a {@link forge.Card} object.
* @param question
* a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean confirm(final Card c, final String question) { public static boolean confirm(final Card c, final String question) {
return GuiDialog.confirm(c, question, true); return GuiDialog.confirm(c, question, true, null);
}
public static boolean confirm(final Card c, final String question, final boolean defaultChoice) {
return GuiDialog.confirm(c, question, defaultChoice, null);
}
public static boolean confirm(final Card c, final String question, String[] options) {
return GuiDialog.confirm(c, question, true, options);
} }
/** public static boolean confirm(final Card c, String question, final boolean defaultIsYes, final String[] options) {
* <p>
* showYesNoDialog.
* </p>
*
* @param c
* a {@link forge.Card} object.
* @param question
* a {@link java.lang.String} object.
* @param defaultNo
* true if the default option should be "No", false otherwise
* @return a boolean.
*/
public static boolean confirm(final Card c, String question, final boolean defaultChoice) {
CMatchUI.SINGLETON_INSTANCE.setCard(c); CMatchUI.SINGLETON_INSTANCE.setCard(c);
final StringBuilder title = new StringBuilder(); final StringBuilder title = new StringBuilder();
if ( c != null) if ( c != null)
@@ -54,13 +37,10 @@ public class GuiDialog {
} }
int answer; int answer;
if (!defaultChoice) {
final Object[] options = { "Yes", "No" }; String[] opts = options == null ? defaultConfirmOptions : options;
answer = JOptionPane.showOptionDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION, answer = JOptionPane.showOptionDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION,
JOptionPane.PLAIN_MESSAGE, null, options, options[1]); JOptionPane.QUESTION_MESSAGE, null, opts, opts[defaultIsYes ? 0 : 1]);
} else {
answer = JOptionPane.showConfirmDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION);
}
return answer == JOptionPane.YES_OPTION; return answer == JOptionPane.YES_OPTION;
} }

View File

@@ -120,7 +120,6 @@ public final class GuiDisplayUtil {
final Card dummy = new Card(); final Card dummy = new Card();
final Player human = Singletons.getControl().getPlayer(); final Player human = Singletons.getControl().getPlayer();
dummy.setOwner(human); dummy.setOwner(human);
dummy.addController(human);
Map<String, String> produced = new HashMap<String, String>(); Map<String, String> produced = new HashMap<String, String>();
produced.put("Produced", "W W W W W W W U U U U U U U B B B B B B B G G G G G G G R R R R R R R 7"); produced.put("Produced", "W W W W W W W U U U U U U U B B B B B B B G G G G G G G R R R R R R R 7");
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced); final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);

View File

@@ -21,14 +21,19 @@ import java.io.File;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.eventbus.Subscribe;
import forge.Singletons; import forge.Singletons;
import forge.deck.Deck; import forge.deck.Deck;
import forge.game.GameFormat; import forge.game.GameFormat;
import forge.game.event.Event;
import forge.game.event.MulliganEvent;
import forge.game.player.PlayerType;
import forge.item.CardPrinted; import forge.item.CardPrinted;
import forge.item.PreconDeck; import forge.item.PreconDeck;
import forge.properties.NewConstants; import forge.properties.NewConstants;
import forge.quest.bazaar.QuestBazaarManager; import forge.quest.bazaar.QuestBazaarManager;
import forge.quest.bazaar.QuestItemType;
import forge.quest.bazaar.QuestPetStorage; import forge.quest.bazaar.QuestPetStorage;
import forge.quest.data.GameFormatQuest; import forge.quest.data.GameFormatQuest;
import forge.quest.data.QuestAchievements; import forge.quest.data.QuestAchievements;
@@ -434,4 +439,16 @@ public class QuestController {
return unlocksAvaliable > unlocksSpent ? Math.min(unlocksAvaliable - unlocksSpent, cntLocked) : 0; return unlocksAvaliable > unlocksSpent ? Math.min(unlocksAvaliable - unlocksSpent, cntLocked) : 0;
} }
@Subscribe
public void receiveGameEvent(Event ev) { // Receives events only during quest games
if ( ev instanceof MulliganEvent ) {
MulliganEvent mev = (MulliganEvent)ev;
// First mulligan is free
if (mev.player.getType() == PlayerType.HUMAN && getAssets().hasItem(QuestItemType.SLEIGHT) && mev.player.getStats().getMulliganCount() == 0) {
mev.player.drawCard();
}
}
}
} }