mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
merge latest trunk
This commit is contained in:
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ Release Notes:
|
|||||||
|
|
||||||
New Cards:
|
New Cards:
|
||||||
|
|
||||||
|
Noggin Whack
|
||||||
|
Scythe Specter
|
||||||
|
Sasaya, Orochi Ascendant
|
||||||
|
|
||||||
|
|
||||||
Known Issues:
|
Known Issues:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
12
res/cardsfolder/n/noggin_whack.txt
Normal file
12
res/cardsfolder/n/noggin_whack.txt
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
14
res/cardsfolder/s/scythe_specter.txt
Normal file
14
res/cardsfolder/s/scythe_specter.txt
Normal 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
|
||||||
@@ -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]
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
41
res/quest/precons/Boros Battalion.dck
Normal file
41
res/quest/precons/Boros Battalion.dck
Normal 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]
|
||||||
41
res/quest/precons/Dimir Dementia.dck
Normal file
41
res/quest/precons/Dimir Dementia.dck
Normal 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]
|
||||||
42
res/quest/precons/Gruul Goliaths.dck
Normal file
42
res/quest/precons/Gruul Goliaths.dck
Normal 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]
|
||||||
41
res/quest/precons/Orzhov Oppression.dck
Normal file
41
res/quest/precons/Orzhov Oppression.dck
Normal 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]
|
||||||
41
res/quest/precons/Simic Synthesis.dck
Normal file
41
res/quest/precons/Simic Synthesis.dck
Normal 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]
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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   HashMap<String, String>
|
* @param mapParams   HashMap<String, String>
|
||||||
* @param host   Card
|
* @param host   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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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   HashMap<String, String>
|
* @param mapParams   HashMap<String, String>
|
||||||
* @param host   Card
|
* @param host   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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/main/java/forge/game/event/MulliganEvent.java
Normal file
15
src/main/java/forge/game/event/MulliganEvent.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user