From 69661360d768c66d4422f71fced65372eb739e54 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 02:09:00 +0000 Subject: [PATCH 01/27] - Fixed Oracle's Attendants --- res/cardsfolder/o/oracles_attendants.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/cardsfolder/o/oracles_attendants.txt b/res/cardsfolder/o/oracles_attendants.txt index 36b8048c864..8d08e3fc634 100644 --- a/res/cardsfolder/o/oracles_attendants.txt +++ b/res/cardsfolder/o/oracles_attendants.txt @@ -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: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:ShamanCombatDmg: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:CombatDmg:AB$ DealDamage | Cost$ 0 | Defined$ EffectSource | DamageSource$ ReplacedSource | CombatDamage$ True | 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:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile | Static$ True SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True From 6adbbd75fde18386fed292bf6ca65e98b006d4cb Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 03:56:30 +0000 Subject: [PATCH 02/27] - Added "MustBeBlocked" to Akki Underminer --- res/cardsfolder/a/akki_underminer.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/cardsfolder/a/akki_underminer.txt b/res/cardsfolder/a/akki_underminer.txt index 6b5484c6b29..cc347662048 100644 --- a/res/cardsfolder/a/akki_underminer.txt +++ b/res/cardsfolder/a/akki_underminer.txt @@ -3,7 +3,8 @@ ManaCost:3 R Types:Creature Goblin Rogue Shaman 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. -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 Oracle:Whenever Akki Underminer deals combat damage to a player, that player sacrifices a permanent. SetInfo:CHK Uncommon \ No newline at end of file From 12098550d7c9cde9694c918d65d464327ea42c12 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 04:25:26 +0000 Subject: [PATCH 03/27] - Added Noggin Whack --- .gitattributes | 1 + res/cardsfolder/n/noggin_whack.txt | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 res/cardsfolder/n/noggin_whack.txt diff --git a/.gitattributes b/.gitattributes index 89a662c56f2..aa42fcd906d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7244,6 +7244,7 @@ res/cardsfolder/n/noble_templar.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/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_bridgebreaker.txt svneol=native#text/plain res/cardsfolder/n/noggle_hedge_mage.txt svneol=native#text/plain diff --git a/res/cardsfolder/n/noggin_whack.txt b/res/cardsfolder/n/noggin_whack.txt new file mode 100644 index 00000000000..6f7f0b8cad6 --- /dev/null +++ b/res/cardsfolder/n/noggin_whack.txt @@ -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 \ No newline at end of file From d48f0a9184910ca6ae81ab3ec0e347b493b13c73 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 06:41:02 +0000 Subject: [PATCH 04/27] - Added Scythe Specter --- .gitattributes | 1 + res/cardsfolder/s/scythe_specter.txt | 14 +++++++++++++ src/main/java/forge/Card.java | 21 +++++++++++++++++++ .../java/forge/card/ability/AbilityUtils.java | 4 ++++ .../ability/effects/RepeatEachEffect.java | 9 ++++++-- 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 res/cardsfolder/s/scythe_specter.txt diff --git a/.gitattributes b/.gitattributes index aa42fcd906d..6949ac4ed07 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9122,6 +9122,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_scrapper.txt svneol=native#text/plain 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/sea_drake.txt svneol=native#text/plain res/cardsfolder/s/sea_eagle.txt svneol=native#text/plain diff --git a/res/cardsfolder/s/scythe_specter.txt b/res/cardsfolder/s/scythe_specter.txt new file mode 100644 index 00000000000..5a5dad80826 --- /dev/null +++ b/res/cardsfolder/s/scythe_specter.txt @@ -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 \ No newline at end of file diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index d9d2909b8d6..aaca6ed70c3 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -7102,6 +7102,27 @@ public class Card extends GameEntity implements Comparable { } } } + } else if (property.startsWith("greatestRememberedCMC")) { + final List list = new ArrayList(); + 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")) { final List list = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield); for (final Card crd : list) { diff --git a/src/main/java/forge/card/ability/AbilityUtils.java b/src/main/java/forge/card/ability/AbilityUtils.java index 09e877e7fa4..c466e5318bd 100644 --- a/src/main/java/forge/card/ability/AbilityUtils.java +++ b/src/main/java/forge/card/ability/AbilityUtils.java @@ -731,6 +731,10 @@ public class AbilityUtils { 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")) { final SpellAbility root = sa.getRootAbility(); Object o = null; diff --git a/src/main/java/forge/card/ability/effects/RepeatEachEffect.java b/src/main/java/forge/card/ability/effects/RepeatEachEffect.java index 7d4eaea5d9c..9dd4c4b151a 100644 --- a/src/main/java/forge/card/ability/effects/RepeatEachEffect.java +++ b/src/main/java/forge/card/ability/effects/RepeatEachEffect.java @@ -1,5 +1,6 @@ package forge.card.ability.effects; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -39,8 +40,12 @@ public class RepeatEachEffect extends SpellAbilityEffect { List repeatCards = null; if (sa.hasParam("RepeatCards")) { - ZoneType zone = sa.hasParam("Zone") ? ZoneType.smartValueOf(sa.getParam("Zone")) : ZoneType.Battlefield; - + List zone = new ArrayList(); + if (sa.hasParam("Zone")) { + zone = ZoneType.listValueOf(sa.getParam("Zone")); + } else { + zone.add(ZoneType.Battlefield); + } repeatCards = CardLists.getValidCards(game.getCardsIn(zone), sa.getParam("RepeatCards"), source.getController(), source); loopOverCards = true; From c9a6e55b66dcd3c085125469fe7e254b7c7e1502 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 07:00:30 +0000 Subject: [PATCH 05/27] - Fixed Feeding Grounds and Horizon Boughs --- res/cardsfolder/f/feeding_grounds.txt | 1 + res/cardsfolder/h/horizon_boughs.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/res/cardsfolder/f/feeding_grounds.txt b/res/cardsfolder/f/feeding_grounds.txt index 8f1f7a66620..67d00d043bc 100644 --- a/res/cardsfolder/f/feeding_grounds.txt +++ b/res/cardsfolder/f/feeding_grounds.txt @@ -6,6 +6,7 @@ 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$ 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 +A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice. SVar:DBPutCounter:DB$PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ X SVar:X:Targeted$CardManaCost SVar:Picture:http://www.wizards.com/global/images/magic/general/feeding_grounds.jpg diff --git a/res/cardsfolder/h/horizon_boughs.txt b/res/cardsfolder/h/horizon_boughs.txt index 8267b36226e..8ab7ebaef78 100644 --- a/res/cardsfolder/h/horizon_boughs.txt +++ b/res/cardsfolder/h/horizon_boughs.txt @@ -5,6 +5,7 @@ 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$ 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 +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:Picture:http://www.wizards.com/global/images/magic/general/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. From 576b2f555789723591b8020cf89d1fa3a7be1eab Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 07:09:52 +0000 Subject: [PATCH 06/27] - Added some missing SVars of the last commit --- res/cardsfolder/f/feeding_grounds.txt | 5 +++-- res/cardsfolder/h/horizon_boughs.txt | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/res/cardsfolder/f/feeding_grounds.txt b/res/cardsfolder/f/feeding_grounds.txt index 67d00d043bc..ec93f1e7a57 100644 --- a/res/cardsfolder/f/feeding_grounds.txt +++ b/res/cardsfolder/f/feeding_grounds.txt @@ -7,8 +7,9 @@ T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ DBPutCount 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 A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice. -SVar:DBPutCounter:DB$PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ X -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 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 \ No newline at end of file diff --git a/res/cardsfolder/h/horizon_boughs.txt b/res/cardsfolder/h/horizon_boughs.txt index 8ab7ebaef78..ea179c0ef38 100644 --- a/res/cardsfolder/h/horizon_boughs.txt +++ b/res/cardsfolder/h/horizon_boughs.txt @@ -7,6 +7,7 @@ T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ Rolle 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:X:Count$RolledThisTurn SVar:Picture:http://www.wizards.com/global/images/magic/general/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. SetInfo:HOP Common \ No newline at end of file From 9749394426b90db48d365e391fc97b6e9b8b0912 Mon Sep 17 00:00:00 2001 From: moomarc Date: Sat, 16 Mar 2013 07:44:02 +0000 Subject: [PATCH 07/27] - Fixed broken LQ picture url for Horizon Boughs --- res/cardsfolder/h/horizon_boughs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/cardsfolder/h/horizon_boughs.txt b/res/cardsfolder/h/horizon_boughs.txt index ea179c0ef38..0d110f7f815 100644 --- a/res/cardsfolder/h/horizon_boughs.txt +++ b/res/cardsfolder/h/horizon_boughs.txt @@ -8,6 +8,6 @@ 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:X:Count$RolledThisTurn -SVar:Picture:http://www.wizards.com/global/images/magic/general/horizon_boughs.jpg +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. SetInfo:HOP Common \ No newline at end of file From 23fb1054bae3b99a71562846cb8aede1b51bdb40 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 08:04:20 +0000 Subject: [PATCH 08/27] - The AI will now use the ability of Shirei, Shizo's Caretaker. --- .../h/hikari_twilight_guardian.txt | 4 +-- res/cardsfolder/s/shirei_shizos_caretaker.txt | 2 +- res/cardsfolder/v/vebulid.txt | 2 +- .../forge/card/trigger/WrappedAbility.java | 32 +++++++++++-------- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/res/cardsfolder/h/hikari_twilight_guardian.txt b/res/cardsfolder/h/hikari_twilight_guardian.txt index a5aac3f9d0d..5a2bb1550ca 100644 --- a/res/cardsfolder/h/hikari_twilight_guardian.txt +++ b/res/cardsfolder/h/hikari_twilight_guardian.txt @@ -5,8 +5,8 @@ PT:4/4 K:Flying T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, you may exile CARDNAME. If you do, return it to the battlefield under its owner's control at the beginning of the next end step. SVar:TrigExile:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Battlefield | Destination$ Exile | SubAbility$ DelTrig -SVar:DelTrig:DB$DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | TriggerDescription$ Return CARDNAME to the battlefield. -SVar:TrigReturn:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Exile | Destination$ Battlefield +SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | TriggerDescription$ Return CARDNAME to the battlefield. +SVar:TrigReturn:AB$ ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Exile | Destination$ Battlefield SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/hikari_twilight_guardian.jpg Oracle:Flying\nWhenever you cast a Spirit or Arcane spell, you may exile Hikari, Twilight Guardian. If you do, return it to the battlefield under its owner's control at the beginning of the next end step. diff --git a/res/cardsfolder/s/shirei_shizos_caretaker.txt b/res/cardsfolder/s/shirei_shizos_caretaker.txt index 37409ccf75b..f8709e710c4 100644 --- a/res/cardsfolder/s/shirei_shizos_caretaker.txt +++ b/res/cardsfolder/s/shirei_shizos_caretaker.txt @@ -4,7 +4,7 @@ Types:Legendary Creature Spirit PT:2/2 T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Battlefield | ValidCard$ Creature.powerLE1+YouOwn | OptionalDecider$ You | DelayedTrigger$ DelTrig | TriggerDescription$ Whenever a creature with power 1 or less is put into your graveyard from the battlefield, you may return that card to the battlefield under your control at the beginning of the next end step if CARDNAME is still on the battlefield. SVar:DelTrig:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ Player | Execute$ TrigReturn | IsPresent$ Card.Self | PresentZone$ Battlefield | TriggerDescription$ Return creature to the battlefield. -SVar:TrigReturn:AB$ChangeZone | Cost$ 0 | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard +SVar:TrigReturn:AB$ ChangeZone | Cost$ 0 | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Static$ True | Execute$ DBCleanup SVar:DBCleanup:DB$Cleanup | ClearTriggered$ True SVar:Picture:http://www.wizards.com/global/images/magic/general/shirei_shizos_caretaker.jpg diff --git a/res/cardsfolder/v/vebulid.txt b/res/cardsfolder/v/vebulid.txt index b65fc551642..e750ae8f101 100644 --- a/res/cardsfolder/v/vebulid.txt +++ b/res/cardsfolder/v/vebulid.txt @@ -6,7 +6,7 @@ K:etbCounter:P1P1:1 T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ At the beginning of your upkeep, you may put a +1/+1 counter on CARDNAME. T:Mode$ Attacks | ValidCard$ Card.Self | DelayedTrigger$ DelTrigDestroy | TriggerDescription$ When CARDNAME attacks or blocks, destroy it at end of combat. T:Mode$ Blocks | ValidCard$ Card.Self | DelayedTrigger$ DelTrigDestroy | Secondary$ True | TriggerDescription$ When CARDNAME attacks or blocks, destroy it at end of combat. -SVar:TrigPutCounter:AB$PutCounter | Cost$ 0 | Defined$ Self | CounterNum$ 1 | CounterType$ P1P1 +SVar:TrigPutCounter:AB$ PutCounter | Cost$ 0 | Defined$ Self | CounterNum$ 1 | CounterType$ P1P1 SVar:DelTrigDestroy:Mode$ Phase | Phase$ EndCombat | Execute$ TrigDestroy | TriggerDescription$ Destroy CARDNAME at end of combat. SVar:TrigDestroy:AB$Destroy | Cost$ 0 | Defined$ Self SVar:Picture:http://www.wizards.com/global/images/magic/general/vebulid.jpg diff --git a/src/main/java/forge/card/trigger/WrappedAbility.java b/src/main/java/forge/card/trigger/WrappedAbility.java index cf2b05b8bfa..cd9c3293a71 100644 --- a/src/main/java/forge/card/trigger/WrappedAbility.java +++ b/src/main/java/forge/card/trigger/WrappedAbility.java @@ -7,6 +7,7 @@ import java.util.Map; import forge.Card; import forge.Singletons; +import forge.card.ability.AbilityFactory; import forge.card.ability.ApiType; import forge.card.cost.Cost; import forge.card.mana.ManaCost; @@ -406,19 +407,24 @@ public class WrappedAbility extends Ability implements ISpellAbility { } } } else { - ArrayList tgts = null; - // make sure the targets won't change - if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) { - tgts = new ArrayList(sa.getTarget().getTargetChoices().getTargets()); - } - // This isn't quite right, but better than canPlayAI - if (!sa.doTrigger(this.isMandatory(), (AIPlayer)decider)) { - return; - } - if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) { - for (Object tgt : tgts) { - sa.getTarget().getTargetChoices().clear(); - sa.getTarget().getTargetChoices().addTarget(tgt); + 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 { + ArrayList tgts = null; + // make sure the targets won't change + if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) { + tgts = new ArrayList(sa.getTarget().getTargetChoices().getTargets()); + } + // This isn't quite right, but better than canPlayAI + if (!sa.doTrigger(this.isMandatory(), (AIPlayer) decider)) { + return; + } + if (sa.getTarget() != null && sa.getTarget().getTargetChoices() != null) { + for (Object tgt : tgts) { + sa.getTarget().getTargetChoices().clear(); + sa.getTarget().getTargetChoices().addTarget(tgt); + } } } } From 92a63d23d8aa359b211577dcfebef9f60b50f4b9 Mon Sep 17 00:00:00 2001 From: moomarc Date: Sat, 16 Mar 2013 08:17:05 +0000 Subject: [PATCH 09/27] - added a working LQ pic URL to Raven's Run --- res/cardsfolder/r/ravens_run.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/cardsfolder/r/ravens_run.txt b/res/cardsfolder/r/ravens_run.txt index 17f982d0811..48cbb825067 100644 --- a/res/cardsfolder/r/ravens_run.txt +++ b/res/cardsfolder/r/ravens_run.txt @@ -11,6 +11,6 @@ T:Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ Rolle SVar:RolledWalk:AB$ Planeswalk | Cost$ 0 A:AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | ActivationZone$ Command | SpellDescription$ Roll the planar dice. 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. SetInfo:HOP Common \ No newline at end of file From 92caa7514da4a44ba376be4782a8857a66fb37a0 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 10:01:18 +0000 Subject: [PATCH 10/27] rem warning, replace array with immutable list in Color --- src/main/java/forge/Color.java | 6 ++++-- src/main/java/forge/card/trigger/WrappedAbility.java | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/forge/Color.java b/src/main/java/forge/Color.java index b21807610e0..fb9639d7d89 100644 --- a/src/main/java/forge/Color.java +++ b/src/main/java/forge/Color.java @@ -19,6 +19,8 @@ package forge; import java.util.EnumSet; +import com.google.common.collect.ImmutableList; + import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.ManaCostBeingPaid; @@ -46,7 +48,7 @@ public enum Color { /** The Blue. */ Blue(16); - public static final Color[] WUBRG = new Color[] { White, Blue, Black, Red, Green }; + public static final ImmutableList WUBRG = ImmutableList.of( White, Blue, Black, Red, Green ); @SuppressWarnings("unused") private int flag = 0; @@ -128,7 +130,7 @@ public enum Color { final EnumSet colors = EnumSet.of(Color.Colorless); for( int i = 0; i < MagicColor.NUMBER_OR_COLORS; i++ ) { if( cc.hasAnyColor(MagicColor.WUBRG[i]) ) - colors.add(Color.WUBRG[i]); + colors.add(Color.WUBRG.get(i)); } if (colors.size() > 1) { colors.remove(Color.Colorless); diff --git a/src/main/java/forge/card/trigger/WrappedAbility.java b/src/main/java/forge/card/trigger/WrappedAbility.java index cd9c3293a71..f571fc25b50 100644 --- a/src/main/java/forge/card/trigger/WrappedAbility.java +++ b/src/main/java/forge/card/trigger/WrappedAbility.java @@ -7,7 +7,6 @@ import java.util.Map; import forge.Card; import forge.Singletons; -import forge.card.ability.AbilityFactory; import forge.card.ability.ApiType; import forge.card.cost.Cost; import forge.card.mana.ManaCost; From 4acffc60cd46a14eba49d0e44710bff1daaf875d Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 10:04:39 +0000 Subject: [PATCH 11/27] scry now uses player controllers to learn player's choice, method player.scry() moved to ScryEffect Human uses GuiChoice.order to arrange cards to be put on top or bottom of library (that means less popping windows) AI has a method in ComputerUtil to learn where to put a given card. --- .../card/ability/effects/ScryEffect.java | 40 ++++++++++++++++++- src/main/java/forge/game/ai/ComputerUtil.java | 16 ++++++++ src/main/java/forge/game/player/AIPlayer.java | 36 ----------------- .../java/forge/game/player/HumanPlayer.java | 25 ------------ src/main/java/forge/game/player/Player.java | 32 ++------------- .../forge/game/player/PlayerController.java | 3 ++ .../forge/game/player/PlayerControllerAi.java | 21 ++++++++++ .../game/player/PlayerControllerHuman.java | 9 +++++ 8 files changed, 92 insertions(+), 90 deletions(-) diff --git a/src/main/java/forge/card/ability/effects/ScryEffect.java b/src/main/java/forge/card/ability/effects/ScryEffect.java index 714f389fa39..72e22124e8a 100644 --- a/src/main/java/forge/card/ability/effects/ScryEffect.java +++ b/src/main/java/forge/card/ability/effects/ScryEffect.java @@ -1,12 +1,19 @@ package forge.card.ability.effects; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import forge.Card; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.player.Player; +import forge.game.zone.PlayerZone; +import forge.game.zone.ZoneType; public class ScryEffect extends SpellAbilityEffect { @@ -42,7 +49,38 @@ public class ScryEffect extends SpellAbilityEffect { for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { - p.scry(num); + scry(p, num); + } + } + } + + /** + *

+ * scry. + *

+ * + * @param numScry + * a int. + */ + public final void scry(Player p, int numScry) { + final List topN = new ArrayList(); + final PlayerZone library = p.getZone(ZoneType.Library); + numScry = Math.min(numScry, library.size()); + for (int i = 0; i < numScry; i++) { + topN.add(library.get(i)); + } + + ImmutablePair, List> lists = p.getController().arrangeForScry(topN); + + for(Card c : lists.getRight()) { + p.getGame().getAction().moveToBottomOfLibrary(c); + } + + List toTop = lists.getLeft(); + 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); } } } diff --git a/src/main/java/forge/game/ai/ComputerUtil.java b/src/main/java/forge/game/ai/ComputerUtil.java index 5d3cf8d97f9..b8d8ad5b8cc 100644 --- a/src/main/java/forge/game/ai/ComputerUtil.java +++ b/src/main/java/forge/game/ai/ComputerUtil.java @@ -24,6 +24,7 @@ import java.util.Random; import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import forge.Card; import forge.CardLists; @@ -1223,4 +1224,19 @@ public class ComputerUtil { return (handList.size() > AI_MULLIGAN_THRESHOLD) && hasLittleCmc0Cards; } + + + public static boolean scryWillMoveCardToBottomOfLibrary(AIPlayer player, Card c) { + boolean bottom = false; + if (c.isBasicLand()) { + List 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 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; + } } diff --git a/src/main/java/forge/game/player/AIPlayer.java b/src/main/java/forge/game/player/AIPlayer.java index 066b2482a67..fd928405cc8 100644 --- a/src/main/java/forge/game/player/AIPlayer.java +++ b/src/main/java/forge/game/player/AIPlayer.java @@ -20,7 +20,6 @@ package forge.game.player; import java.util.List; import java.util.Random; -import com.google.common.collect.Iterables; import forge.Card; import forge.CardLists; @@ -135,41 +134,6 @@ public class AIPlayer extends Player { // ///////////////////////// - /** {@inheritDoc} */ - @Override - protected final void doScry(final List topN, final int n) { - int num = n; - for (int i = 0; i < num; i++) { - boolean bottom = false; - if (topN.get(i).isBasicLand()) { - List 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 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} */ @Override public final void sacrificePermanent(final String prompt, final List choices) { diff --git a/src/main/java/forge/game/player/HumanPlayer.java b/src/main/java/forge/game/player/HumanPlayer.java index fe39a66595b..6fc76bb1463 100644 --- a/src/main/java/forge/game/player/HumanPlayer.java +++ b/src/main/java/forge/game/player/HumanPlayer.java @@ -117,31 +117,6 @@ public class HumanPlayer extends Player { Singletons.getModel().getMatch().getInput().setInputInterrupt(PlayerUtil.inputChainsDiscard()); } - /** {@inheritDoc} */ - @Override - protected final void doScry(final List 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} */ @Override public final void sacrificePermanent(final String prompt, final List choices) { diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 4e16c286a29..b6098f654bb 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -192,6 +192,10 @@ public abstract class Player extends GameEntity implements Comparable { this.setName(lobbyPlayer.getName()); } + public GameState getGame() { // I'll probably regret about this + return game; + } + public final PlayerStatistics getStats() { return stats; } @@ -1844,35 +1848,7 @@ public abstract class Player extends GameEntity implements Comparable { // ////////////////////////////// // ////////////////////////////// - /** - *

- * doScry. - *

- * - * @param topN - * a {@link forge.CardList} object. - * @param n - * a int. - */ - protected abstract void doScry(List topN, int n); - /** - *

- * scry. - *

- * - * @param numScry - * a int. - */ - public final void scry(int numScry) { - final List topN = new ArrayList(); - 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()); - } // ///////////////////////////// diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index bc3fc07cb41..4a5352cf301 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -3,6 +3,8 @@ package forge.game.player; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.tuple.ImmutablePair; + import forge.Card; import forge.GameEntity; import forge.card.spellability.SpellAbility; @@ -104,4 +106,5 @@ public abstract class PlayerController { /** Shows the card to this player*/ public abstract void reveal(String string, List cards, ZoneType zone, Player owner); + public abstract ImmutablePair, List> arrangeForScry(List topN); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 696043136cc..c179c49a099 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -1,8 +1,12 @@ package forge.game.player; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.tuple.ImmutablePair; + import forge.Card; import forge.GameEntity; import forge.card.spellability.Spell; @@ -228,4 +232,21 @@ public class PlayerControllerAi extends PlayerController { // We don't know how to reveal cards to AI } + @Override + public ImmutablePair, List> arrangeForScry(List topN) { + List toBottom = new ArrayList(); + List toTop = new ArrayList(); + + 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); + } + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 1bd07544911..4c483c25291 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -8,6 +8,7 @@ import java.util.Map; import javax.swing.JOptionPane; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; import forge.Card; import forge.GameEntity; @@ -288,4 +289,12 @@ public class PlayerControllerHuman extends PlayerController { message = String.format("Looking at %s's %s", owner, zone); GuiChoose.oneOrNone(message, cards); } + + @Override + public ImmutablePair, List> arrangeForScry(List topN) { + List 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); + List toTop = topN.isEmpty() ? null : GuiChoose.order("Arrange cards to be put on top of your library", "Cards arranged", 0, topN, null, null); + return ImmutablePair.of(toTop, toBottom); + } } From ea78324d1539ef891b303212b54e72784d05be1b Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 10:25:48 +0000 Subject: [PATCH 12/27] - Little improvements in AttachAI. --- res/quest/duels/Elrond 2.dck | 2 +- res/quest/duels/Elrond 3.dck | 2 +- .../java/forge/card/ability/ai/AttachAi.java | 21 ++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/res/quest/duels/Elrond 2.dck b/res/quest/duels/Elrond 2.dck index bca769dab27..26d325cc061 100644 --- a/res/quest/duels/Elrond 2.dck +++ b/res/quest/duels/Elrond 2.dck @@ -3,7 +3,7 @@ Name=Elrond 2 Title=Elrond Difficulty=medium -Description=RGW Aura deck with Rabid Wombat +Description=RGW Aura deck with Aura Gnarlid and Rabid Wombat Icon=Elrond.jpg Deck Type=constructed [main] diff --git a/res/quest/duels/Elrond 3.dck b/res/quest/duels/Elrond 3.dck index c9910ae12bb..aa87ff35078 100644 --- a/res/quest/duels/Elrond 3.dck +++ b/res/quest/duels/Elrond 3.dck @@ -3,7 +3,7 @@ Name=Elrond 3 Title=Elrond 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 Deck Type=constructed [main] diff --git a/src/main/java/forge/card/ability/ai/AttachAi.java b/src/main/java/forge/card/ability/ai/AttachAi.java index 6d16b2fe99d..19cb2f2f684 100644 --- a/src/main/java/forge/card/ability/ai/AttachAi.java +++ b/src/main/java/forge/card/ability/ai/AttachAi.java @@ -805,18 +805,25 @@ public class AttachAi extends SpellAbilityAi { if (attachSource.isAura() && !attachSource.getName().equals("Daybreak Coronet")) { // TODO For Auras like Rancor, that aren't as likely to lead to // card disadvantage, this check should be skipped - prefList = CardLists.filter(prefList, Predicates.not(Presets.ENCHANTED)); - } - - if (!grantingAbilities && keywords.isEmpty()) { - // Probably prefer to Enchant Creatures that Can Attack - // Filter out creatures that can't Attack or have Defender prefList = CardLists.filter(prefList, new Predicate() { @Override public boolean apply(final Card c) { - return !c.isCreature() || CombatUtil.canAttackNextTurn(c); + return !c.isEnchanted() || c.hasKeyword("Hexproof"); } }); + } + + if (!grantingAbilities) { + // Probably prefer to Enchant Creatures that Can Attack + // Filter out creatures that can't Attack or have Defender + if (keywords.isEmpty()) { + prefList = CardLists.filter(prefList, new Predicate() { + @Override + public boolean apply(final Card c) { + return !c.isCreature() || CombatUtil.canAttackNextTurn(c); + } + }); + } c = ComputerUtilCard.getBestAI(prefList); } else { // If we grant abilities, we may want to put it on something Weak? From c96304666d430bcf8d8aaf7628d9dc3900f2abe9 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 10:35:38 +0000 Subject: [PATCH 13/27] removed clash code from Player class, since it's referenced only from ClashEffect.java Improved GuiDialog.confirm to accept custom values for choice options Also added default question icon to dialogs (may remove if it's too ugly) decision-making about "where to put the card used to clash" moved to PlayerController* classes --- .../card/ability/effects/ClashEffect.java | 90 ++++++++++++++++++- src/main/java/forge/game/player/AIPlayer.java | 8 -- .../java/forge/game/player/HumanPlayer.java | 16 ---- src/main/java/forge/game/player/Player.java | 82 ----------------- .../forge/game/player/PlayerController.java | 1 + .../forge/game/player/PlayerControllerAi.java | 6 ++ .../game/player/PlayerControllerHuman.java | 6 ++ src/main/java/forge/gui/GuiDialog.java | 50 ++++------- 8 files changed, 117 insertions(+), 142 deletions(-) diff --git a/src/main/java/forge/card/ability/effects/ClashEffect.java b/src/main/java/forge/card/ability/effects/ClashEffect.java index 9bd8d3ad195..965ba615da7 100644 --- a/src/main/java/forge/card/ability/effects/ClashEffect.java +++ b/src/main/java/forge/card/ability/effects/ClashEffect.java @@ -2,6 +2,9 @@ package forge.card.ability.effects; import java.util.HashMap; +import javax.swing.JOptionPane; + +import forge.Card; import forge.Singletons; import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityUtils; @@ -9,6 +12,10 @@ import forge.card.ability.SpellAbilityEffect; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; 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 { @@ -25,7 +32,7 @@ public class ClashEffect extends SpellAbilityEffect { */ @Override public void resolve(SpellAbility sa) { - final boolean victory = sa.getSourceCard().getController().clashWithOpponent(sa.getSourceCard()); + final boolean victory = clashWithOpponent(sa.getSourceCard()); // Run triggers final HashMap runParams = new HashMap(); @@ -56,4 +63,85 @@ public class ClashEffect extends SpellAbilityEffect { Singletons.getModel().getGame().getTriggerHandler().runTrigger(TriggerType.Clashed, runParams, false); } + /** + *

+ * clashWithOpponent. + *

+ * + * @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 + + } + + } diff --git a/src/main/java/forge/game/player/AIPlayer.java b/src/main/java/forge/game/player/AIPlayer.java index fd928405cc8..41271b4b875 100644 --- a/src/main/java/forge/game/player/AIPlayer.java +++ b/src/main/java/forge/game/player/AIPlayer.java @@ -144,14 +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) * diff --git a/src/main/java/forge/game/player/HumanPlayer.java b/src/main/java/forge/game/player/HumanPlayer.java index 6fc76bb1463..dba11169373 100644 --- a/src/main/java/forge/game/player/HumanPlayer.java +++ b/src/main/java/forge/game/player/HumanPlayer.java @@ -29,7 +29,6 @@ import forge.game.GameState; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; -import forge.gui.match.CMatchUI; import forge.quest.QuestController; import forge.quest.bazaar.QuestItemType; @@ -124,21 +123,6 @@ public class HumanPlayer extends Player { 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) * @see forge.game.player.Player#getType() */ diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index b6098f654bb..dd0698f0376 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Random; -import javax.swing.JOptionPane; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -2673,87 +2672,6 @@ public abstract class Player extends GameEntity implements Comparable { this.lifeLostThisTurn = n; } - // ////////////////////////////// - // - // Clash - // - // /////////////////////////////// - - /** - *

- * clashWithOpponent. - *

- * - * @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; - } - } - - /** - *

- * clashMoveToTopOrBottom. - *

- * - * @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 * upcoming combat. This is cleared at the end of each combat. diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 4a5352cf301..49500052dc2 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -107,4 +107,5 @@ public abstract class PlayerController { /** Shows the card to this player*/ public abstract void reveal(String string, List cards, ZoneType zone, Player owner); public abstract ImmutablePair, List> arrangeForScry(List topN); + public abstract boolean willPutCardOnTop(Card c); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index c179c49a099..d6814d8076e 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -249,4 +249,10 @@ public class PlayerControllerAi extends PlayerController { 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) + } + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 4c483c25291..f0b24d33101 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -297,4 +297,10 @@ public class PlayerControllerHuman extends PlayerController { List toTop = topN.isEmpty() ? null : 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 you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} ); + } } diff --git a/src/main/java/forge/gui/GuiDialog.java b/src/main/java/forge/gui/GuiDialog.java index a8f88b14c7a..cbf60ce3deb 100644 --- a/src/main/java/forge/gui/GuiDialog.java +++ b/src/main/java/forge/gui/GuiDialog.java @@ -15,35 +15,18 @@ import forge.util.MyRandom; */ public class GuiDialog { - /** - *

- * showYesNoDialog. - *

- * - * @param c - * a {@link forge.Card} object. - * @param question - * a {@link java.lang.String} object. - * @return a boolean. - */ + private static final String[] defaultConfirmOptions = { "Yes", "No" }; public static boolean confirm(final Card c, final String question) { - return GuiDialog.confirm(c, question, true); + return GuiDialog.confirm(c, question, true, null); } - - /** - *

- * showYesNoDialog. - *

- * - * @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) { + 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) { CMatchUI.SINGLETON_INSTANCE.setCard(c); final StringBuilder title = new StringBuilder(); if ( c != null) @@ -54,14 +37,11 @@ public class GuiDialog { } int answer; - if (!defaultChoice) { - final Object[] options = { "Yes", "No" }; - answer = JOptionPane.showOptionDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION, - JOptionPane.PLAIN_MESSAGE, null, options, options[1]); - } else { - answer = JOptionPane.showConfirmDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION); - } - + + String[] opts = options == null ? defaultConfirmOptions : options; + answer = JOptionPane.showOptionDialog(null, question, title.toString(), JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, opts, opts[defaultIsYes ? 0 : 1]); + return answer == JOptionPane.YES_OPTION; } From c3bb1c17a5aa134ddeccd3634ad483f90a2e81f3 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 10:43:08 +0000 Subject: [PATCH 14/27] a special case for Human scrying with a single card available and no need to arrange --- .../card/ability/effects/ScryEffect.java | 11 +++++---- .../game/player/PlayerControllerHuman.java | 24 +++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/forge/card/ability/effects/ScryEffect.java b/src/main/java/forge/card/ability/effects/ScryEffect.java index 72e22124e8a..4d69e30d91d 100644 --- a/src/main/java/forge/card/ability/effects/ScryEffect.java +++ b/src/main/java/forge/card/ability/effects/ScryEffect.java @@ -71,12 +71,15 @@ public class ScryEffect extends SpellAbilityEffect { } ImmutablePair, List> lists = p.getController().arrangeForScry(topN); - - for(Card c : lists.getRight()) { - p.getGame().getAction().moveToBottomOfLibrary(c); + List toTop = lists.getLeft(); + List toBottom = lists.getRight(); + + if ( null != toBottom) { + for(Card c : toBottom) { + p.getGame().getAction().moveToBottomOfLibrary(c); + } } - List toTop = lists.getLeft(); if ( null != toTop ) { Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. for(Card c : toTop) { diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index f0b24d33101..359b10ad8d4 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -1,6 +1,7 @@ package forge.game.player; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -292,15 +293,30 @@ public class PlayerControllerHuman extends PlayerController { @Override public ImmutablePair, List> arrangeForScry(List topN) { - List 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); - List toTop = topN.isEmpty() ? null : GuiChoose.order("Arrange cards to be put on top of your library", "Cards arranged", 0, topN, null, null); + List toBottom = null; + List 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 you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} ); + return GuiDialog.confirm(c, "Where will you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} ); } } From 2ba9d7bfc83397945cdcd3d0b66ae6fa9639cd76 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 10:44:33 +0000 Subject: [PATCH 15/27] avoid empty scry --- src/main/java/forge/card/ability/effects/ScryEffect.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/forge/card/ability/effects/ScryEffect.java b/src/main/java/forge/card/ability/effects/ScryEffect.java index 4d69e30d91d..34e7af9cd27 100644 --- a/src/main/java/forge/card/ability/effects/ScryEffect.java +++ b/src/main/java/forge/card/ability/effects/ScryEffect.java @@ -66,6 +66,10 @@ public class ScryEffect extends SpellAbilityEffect { final List topN = new ArrayList(); 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)); } From 50799c4f9d140e5c0204d95b8c06fc0a1bfefb98 Mon Sep 17 00:00:00 2001 From: swordshine Date: Sat, 16 Mar 2013 10:45:42 +0000 Subject: [PATCH 16/27] - Added Sasaya, Orochi Ascendant --- .gitattributes | 1 + ...asaya_orochi_ascendant_sasayas_essence.txt | 25 +++++++++++++++++++ src/main/java/forge/CardUtil.java | 3 ++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 res/cardsfolder/s/sasaya_orochi_ascendant_sasayas_essence.txt diff --git a/.gitattributes b/.gitattributes index 6949ac4ed07..1c1b0a532b1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8979,6 +8979,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_vol.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/savage_beating.txt -text res/cardsfolder/s/savage_conception.txt svneol=native#text/plain diff --git a/res/cardsfolder/s/sasaya_orochi_ascendant_sasayas_essence.txt b/res/cardsfolder/s/sasaya_orochi_ascendant_sasayas_essence.txt new file mode 100644 index 00000000000..80332cc029c --- /dev/null +++ b/res/cardsfolder/s/sasaya_orochi_ascendant_sasayas_essence.txt @@ -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 \ No newline at end of file diff --git a/src/main/java/forge/CardUtil.java b/src/main/java/forge/CardUtil.java index 117157d6011..e75017d4a14 100644 --- a/src/main/java/forge/CardUtil.java +++ b/src/main/java/forge/CardUtil.java @@ -34,6 +34,7 @@ import forge.card.MagicColor; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; import forge.card.spellability.AbilityManaPart; +import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.game.player.Player; import forge.game.zone.ZoneType; @@ -495,7 +496,7 @@ public final class CardUtil { } } } 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) { final String s = MagicColor.toShortString(col); if (producedColors.contains(s)) { From 9c4c2328ec6892f6ae731eebae9d0f710fdafb9f Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 10:52:10 +0000 Subject: [PATCH 17/27] - More improvements in AttachAI. --- .../java/forge/card/ability/ai/AttachAi.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/forge/card/ability/ai/AttachAi.java b/src/main/java/forge/card/ability/ai/AttachAi.java index 19cb2f2f684..946c1a242ba 100644 --- a/src/main/java/forge/card/ability/ai/AttachAi.java +++ b/src/main/java/forge/card/ability/ai/AttachAi.java @@ -15,7 +15,6 @@ import forge.CardLists; import forge.CardPredicates; import forge.CardUtil; import forge.Singletons; -import forge.CardPredicates.Presets; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; import forge.card.ability.SpellAbilityAi; @@ -786,10 +785,11 @@ public class AttachAi extends SpellAbilityAi { //only add useful keywords unless P/T bonus is significant if (totToughness + totPower < 4 && !keywords.isEmpty()) { + final int pow = totPower; prefList = CardLists.filter(prefList, new Predicate() { @Override public boolean apply(final Card c) { - return containsUsefulKeyword(keywords, c, sa); + return containsUsefulKeyword(keywords, c, sa, pow); } }); } @@ -962,9 +962,9 @@ public class AttachAi extends SpellAbilityAi { * @param sa SpellAbility * @return true, if successful */ - private static boolean containsUsefulKeyword(final ArrayList keywords, final Card card, final SpellAbility sa) { + private static boolean containsUsefulKeyword(final ArrayList keywords, final Card card, final SpellAbility sa, final int powerBonus) { for (final String keyword : keywords) { - if (isUsefulAttachKeyword(keyword, card, sa)) { + if (isUsefulAttachKeyword(keyword, card, sa, powerBonus)) { return true; } } @@ -1000,7 +1000,7 @@ public class AttachAi extends SpellAbilityAi { * @param sa SpellAbility * @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(); if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { return false; @@ -1013,14 +1013,14 @@ public class AttachAi extends SpellAbilityAi { || keyword.equals("CARDNAME can't be blocked by more than one creature.")); // give evasive keywords to creatures that can attack and deal damage if (evasive) { - if (card.getNetCombatDamage() <= 0 + if (card.getNetCombatDamage() + powerBonus <= 0 || !CombatUtil.canAttackNextTurn(card) || !CombatUtil.canBeBlocked(card)) { return false; } } else if (keyword.equals("Haste")) { 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.") || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) || !CombatUtil.canAttackNextTurn(card)) { @@ -1029,22 +1029,22 @@ public class AttachAi extends SpellAbilityAi { } else if (keyword.endsWith("Indestructible")) { return true; } else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) { - if (card.getNetCombatDamage() <= 0 + if (card.getNetCombatDamage() + powerBonus <= 0 || ((!CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card)) && !CombatUtil.canBlock(card, true))) { return false; } } 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))) { return false; } } 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; } } else if (keyword.startsWith("Flanking")) { - if (card.getNetCombatDamage() <= 0 + if (card.getNetCombatDamage() + powerBonus <= 0 || !CombatUtil.canAttackNextTurn(card) || !CombatUtil.canBeBlocked(card)) { return false; @@ -1055,18 +1055,18 @@ public class AttachAi extends SpellAbilityAi { return false; } } else if (keyword.equals("Trample")) { - if (card.getNetCombatDamage() <= 1 + if (card.getNetCombatDamage() + powerBonus <= 1 || !CombatUtil.canBeBlocked(card) || !CombatUtil.canAttackNextTurn(card)) { return false; } } else if (keyword.equals("Infect")) { - if (card.getNetCombatDamage() <= 0 + if (card.getNetCombatDamage() + powerBonus <= 0 || !CombatUtil.canAttackNextTurn(card)) { return false; } } else if (keyword.equals("Vigilance")) { - if (card.getNetCombatDamage() <= 0 + if (card.getNetCombatDamage() + powerBonus <= 0 || !CombatUtil.canAttackNextTurn(card) || !CombatUtil.canBlock(card, true)) { return false; From b19e5c82d8e1ae70040e6dd583dddb7741157627 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 11:19:24 +0000 Subject: [PATCH 18/27] Quest mulligan code moved out of HumanPlayer QuestController is listening for some events and reacts to human's first mulligan --- .gitattributes | 1 + .../java/forge/control/input/InputMulligan.java | 4 ++-- src/main/java/forge/game/GameState.java | 4 ++++ .../java/forge/game/event/MulliganEvent.java | 15 +++++++++++++++ src/main/java/forge/game/player/HumanPlayer.java | 16 ---------------- src/main/java/forge/game/player/Player.java | 10 ++++++---- .../forge/game/player/PlayerControllerHuman.java | 1 - src/main/java/forge/quest/QuestController.java | 16 ++++++++++++++++ 8 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 src/main/java/forge/game/event/MulliganEvent.java diff --git a/.gitattributes b/.gitattributes index 1c1b0a532b1..22f0ba289df 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14205,6 +14205,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/LifeLossEvent.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/SetTappedEvent.java -text src/main/java/forge/game/event/ShuffleEvent.java -text diff --git a/src/main/java/forge/control/input/InputMulligan.java b/src/main/java/forge/control/input/InputMulligan.java index c1d10ecb0aa..1874503549f 100644 --- a/src/main/java/forge/control/input/InputMulligan.java +++ b/src/main/java/forge/control/input/InputMulligan.java @@ -84,9 +84,9 @@ public class InputMulligan extends Input { @Override public final void selectButtonCancel() { final Player humanPlayer = Singletons.getControl().getPlayer(); - final int newHand = humanPlayer.doMulligan(); + humanPlayer.doMulligan(); - if (newHand == 0) { + if (humanPlayer.getCardsIn(ZoneType.Hand).isEmpty()) { this.end(); } else { ButtonUtil.enableAllFocusOk(); diff --git a/src/main/java/forge/game/GameState.java b/src/main/java/forge/game/GameState.java index 1cf4df89fff..c21dafd7941 100644 --- a/src/main/java/forge/game/GameState.java +++ b/src/main/java/forge/game/GameState.java @@ -118,8 +118,12 @@ public class GameState { endOfTurn = new EndOfTurn(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(gameLog); + } /** diff --git a/src/main/java/forge/game/event/MulliganEvent.java b/src/main/java/forge/game/event/MulliganEvent.java new file mode 100644 index 00000000000..2baa7348645 --- /dev/null +++ b/src/main/java/forge/game/event/MulliganEvent.java @@ -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; + } +} diff --git a/src/main/java/forge/game/player/HumanPlayer.java b/src/main/java/forge/game/player/HumanPlayer.java index dba11169373..49633907fd5 100644 --- a/src/main/java/forge/game/player/HumanPlayer.java +++ b/src/main/java/forge/game/player/HumanPlayer.java @@ -24,13 +24,10 @@ import forge.Card; import forge.Singletons; import forge.card.spellability.SpellAbility; import forge.control.input.Input; -import forge.game.GameType; import forge.game.GameState; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; -import forge.quest.QuestController; -import forge.quest.bazaar.QuestItemType; /** *

@@ -131,19 +128,6 @@ public class HumanPlayer extends Player { 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) * @see forge.game.player.Player#getController() */ diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index dd0698f0376..7705c11f586 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -61,6 +61,7 @@ import forge.game.event.CardDiscardedEvent; import forge.game.event.DrawCardEvent; import forge.game.event.LandPlayedEvent; import forge.game.event.LifeLossEvent; +import forge.game.event.MulliganEvent; import forge.game.event.PoisonCounterEvent; import forge.game.event.ShuffleEvent; import forge.game.phase.PhaseHandler; @@ -1322,20 +1323,21 @@ public abstract class Player extends GameEntity implements Comparable { * a GamePlayerRating object * @return an int */ - public int doMulligan() { + public void doMulligan() { final List hand = new ArrayList(getCardsIn(ZoneType.Hand)); for (final Card c : hand) { game.getAction().moveToLibrary(c); } shuffle(); - final int newHand = hand.size() - 1; - for (int i = 0; i < newHand; i++) { + for (int i = 1; i < hand.size(); i++) { // draws one card less 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); stats.notifyHasMulliganed(); stats.notifyOpeningHandSize(newHand); - return newHand; } /** diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 359b10ad8d4..27ad4cc512d 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -1,7 +1,6 @@ package forge.game.player; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/src/main/java/forge/quest/QuestController.java b/src/main/java/forge/quest/QuestController.java index eba2527010d..67a07172f62 100644 --- a/src/main/java/forge/quest/QuestController.java +++ b/src/main/java/forge/quest/QuestController.java @@ -21,16 +21,21 @@ import java.io.File; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.eventbus.Subscribe; import forge.Singletons; import forge.deck.Deck; import forge.quest.data.GameFormatQuest; 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.PreconDeck; import forge.properties.ForgeProps; import forge.properties.NewConstants; import forge.quest.bazaar.QuestBazaarManager; +import forge.quest.bazaar.QuestItemType; import forge.quest.bazaar.QuestPetStorage; import forge.quest.data.QuestAchievements; import forge.quest.data.QuestAssets; @@ -435,5 +440,16 @@ public class QuestController { 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(); + } + } + } } From 4efcbc331154b6d69946c0aece7199ea6befc098 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 11:49:14 +0000 Subject: [PATCH 19/27] Mulligan - uses drawCards(n) --- src/main/java/forge/game/player/Player.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 7705c11f586..695393e853e 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -1229,16 +1229,6 @@ public abstract class Player extends GameEntity implements Comparable { return this.drawCards(1); } - /** - *

- * drawCards. - *

- * - * @return a List of cards actually drawn - */ - public final List drawCards() { - return this.drawCards(1); - } /** *

@@ -1308,7 +1298,7 @@ public abstract class Player extends GameEntity implements Comparable { } // Play the Draw sound - Singletons.getModel().getGame().getEvents().post(new DrawCardEvent()); + game.getEvents().post(new DrawCardEvent()); return drawn; } @@ -1329,9 +1319,7 @@ public abstract class Player extends GameEntity implements Comparable { game.getAction().moveToLibrary(c); } shuffle(); - for (int i = 1; i < hand.size(); i++) { // draws one card less - drawCard(); - } + drawCards(hand.size() - 1); game.getEvents().post(new MulliganEvent(this)); // quest listener may interfere here final int newHand = getCardsIn(ZoneType.Hand).size(); From b59c47c76696c577b4f48cf4326cdcca7e76d3a7 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 11:55:30 +0000 Subject: [PATCH 20/27] Replacement handlers used hashmap in ctor and method parameters instead of plain map (that is quite sufficient for their needs) --- src/main/java/forge/Card.java | 4 +- .../forge/card/replacement/ReplaceDamage.java | 8 +-- .../card/replacement/ReplaceDiscard.java | 8 +-- .../forge/card/replacement/ReplaceDraw.java | 6 +-- .../card/replacement/ReplaceGainLife.java | 8 +-- .../card/replacement/ReplaceGameLoss.java | 6 +-- .../forge/card/replacement/ReplaceMoved.java | 8 +-- .../card/replacement/ReplaceSetInMotion.java | 6 +-- .../card/replacement/ReplaceTurnFaceUp.java | 8 +-- .../card/replacement/ReplacementEffect.java | 12 ++--- .../card/replacement/ReplacementHandler.java | 51 +++---------------- .../card/spellability/SpellPermanent.java | 3 +- .../forge/game/ai/ComputerUtilCombat.java | 2 +- 13 files changed, 46 insertions(+), 84 deletions(-) diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index aaca6ed70c3..a25749a8a34 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -8115,7 +8115,7 @@ public class Card extends GameEntity implements Comparable { for (final Card ca : Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield)) { for (final ReplacementEffect re : ca.getReplacementEffects()) { - HashMap params = re.getMapParams(); + Map params = re.getMapParams(); if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) { continue; } @@ -9173,7 +9173,7 @@ public class Card extends GameEntity implements Comparable { */ public boolean hasETBReplacement() { for (final ReplacementEffect re : getReplacementEffects()) { - final HashMap params = re.getMapParams(); + final Map params = re.getMapParams(); if (!(re instanceof ReplaceMoved)) { continue; } diff --git a/src/main/java/forge/card/replacement/ReplaceDamage.java b/src/main/java/forge/card/replacement/ReplaceDamage.java index f95376c65a3..29507e9fda1 100644 --- a/src/main/java/forge/card/replacement/ReplaceDamage.java +++ b/src/main/java/forge/card/replacement/ReplaceDamage.java @@ -17,7 +17,7 @@ */ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; import forge.card.cardfactory.CardFactoryUtil; @@ -36,7 +36,7 @@ public class ReplaceDamage extends ReplacementEffect { * @param map the map * @param host the host */ - public ReplaceDamage(HashMap map, Card host) { + public ReplaceDamage(Map map, Card host) { super(map, host); } @@ -44,7 +44,7 @@ public class ReplaceDamage extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("DamageDone")) { return false; } @@ -110,7 +110,7 @@ public class ReplaceDamage extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) */ @Override - public void setReplacingObjects(HashMap runParams, SpellAbility sa) { + public void setReplacingObjects(Map runParams, SpellAbility sa) { sa.setReplacingObject("DamageAmount", runParams.get("DamageAmount")); sa.setReplacingObject("Target", runParams.get("Affected")); sa.setReplacingObject("Source", runParams.get("DamageSource")); diff --git a/src/main/java/forge/card/replacement/ReplaceDiscard.java b/src/main/java/forge/card/replacement/ReplaceDiscard.java index 3b9fe37a0b3..4821e4ef89e 100644 --- a/src/main/java/forge/card/replacement/ReplaceDiscard.java +++ b/src/main/java/forge/card/replacement/ReplaceDiscard.java @@ -17,7 +17,7 @@ */ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; import forge.card.spellability.SpellAbility; @@ -34,7 +34,7 @@ public class ReplaceDiscard extends ReplacementEffect { * @param params the params * @param host the host */ - public ReplaceDiscard(final HashMap params, final Card host) { + public ReplaceDiscard(final Map params, final Card host) { super(params, host); } @@ -42,7 +42,7 @@ public class ReplaceDiscard extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("Discard")) { return false; } @@ -86,7 +86,7 @@ public class ReplaceDiscard extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) */ @Override - public void setReplacingObjects(HashMap runParams, SpellAbility sa) { + public void setReplacingObjects(Map runParams, SpellAbility sa) { sa.setReplacingObject("Card", runParams.get("Card")); sa.setReplacingObject("Player", runParams.get("Affected")); } diff --git a/src/main/java/forge/card/replacement/ReplaceDraw.java b/src/main/java/forge/card/replacement/ReplaceDraw.java index 5b326e53d33..eedfcb9d44c 100644 --- a/src/main/java/forge/card/replacement/ReplaceDraw.java +++ b/src/main/java/forge/card/replacement/ReplaceDraw.java @@ -17,7 +17,7 @@ */ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; @@ -33,7 +33,7 @@ public class ReplaceDraw extends ReplacementEffect { * @param params the params * @param host the host */ - public ReplaceDraw(final HashMap params, final Card host) { + public ReplaceDraw(final Map params, final Card host) { super(params, host); } @@ -41,7 +41,7 @@ public class ReplaceDraw extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("Draw")) { return false; } diff --git a/src/main/java/forge/card/replacement/ReplaceGainLife.java b/src/main/java/forge/card/replacement/ReplaceGainLife.java index 0de246e5061..d5dd570c472 100644 --- a/src/main/java/forge/card/replacement/ReplaceGainLife.java +++ b/src/main/java/forge/card/replacement/ReplaceGainLife.java @@ -17,7 +17,7 @@ */ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; import forge.card.spellability.SpellAbility; @@ -34,7 +34,7 @@ public class ReplaceGainLife extends ReplacementEffect { * @param map the map * @param host the host */ - public ReplaceGainLife(HashMap map, Card host) { + public ReplaceGainLife(Map map, Card host) { super(map, host); } @@ -42,7 +42,7 @@ public class ReplaceGainLife extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("GainLife")) { return false; } @@ -71,7 +71,7 @@ public class ReplaceGainLife extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) */ @Override - public void setReplacingObjects(HashMap runParams, SpellAbility sa) { + public void setReplacingObjects(Map runParams, SpellAbility sa) { sa.setReplacingObject("LifeGained", runParams.get("LifeGained")); } diff --git a/src/main/java/forge/card/replacement/ReplaceGameLoss.java b/src/main/java/forge/card/replacement/ReplaceGameLoss.java index 50ee58f7f97..fc98590dce3 100644 --- a/src/main/java/forge/card/replacement/ReplaceGameLoss.java +++ b/src/main/java/forge/card/replacement/ReplaceGameLoss.java @@ -1,6 +1,6 @@ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; @@ -16,7 +16,7 @@ public class ReplaceGameLoss extends ReplacementEffect { * @param map the map * @param host the host */ - public ReplaceGameLoss(HashMap map, Card host) { + public ReplaceGameLoss(Map map, Card host) { super(map, host); } @@ -24,7 +24,7 @@ public class ReplaceGameLoss extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("GameLoss")) { return false; } diff --git a/src/main/java/forge/card/replacement/ReplaceMoved.java b/src/main/java/forge/card/replacement/ReplaceMoved.java index d12af072cc9..c8b26d8dda5 100644 --- a/src/main/java/forge/card/replacement/ReplaceMoved.java +++ b/src/main/java/forge/card/replacement/ReplaceMoved.java @@ -1,6 +1,6 @@ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; import forge.card.spellability.SpellAbility; @@ -18,7 +18,7 @@ public class ReplaceMoved extends ReplacementEffect { * @param mapParams   HashMap * @param host   Card */ - public ReplaceMoved(final HashMap mapParams, final Card host) { + public ReplaceMoved(final Map mapParams, final Card host) { super(mapParams, host); } @@ -26,7 +26,7 @@ public class ReplaceMoved extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("Moved")) { return false; } @@ -66,7 +66,7 @@ public class ReplaceMoved extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) */ @Override - public void setReplacingObjects(HashMap runParams, SpellAbility sa) { + public void setReplacingObjects(Map runParams, SpellAbility sa) { sa.setReplacingObject("Card", runParams.get("Affected")); } diff --git a/src/main/java/forge/card/replacement/ReplaceSetInMotion.java b/src/main/java/forge/card/replacement/ReplaceSetInMotion.java index 19ac3f4f57f..e382f40b743 100644 --- a/src/main/java/forge/card/replacement/ReplaceSetInMotion.java +++ b/src/main/java/forge/card/replacement/ReplaceSetInMotion.java @@ -17,7 +17,7 @@ */ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; @@ -33,7 +33,7 @@ public class ReplaceSetInMotion extends ReplacementEffect { * @param params the params * @param host the host */ - public ReplaceSetInMotion(final HashMap params, final Card host) { + public ReplaceSetInMotion(final Map params, final Card host) { super(params, host); } @@ -41,7 +41,7 @@ public class ReplaceSetInMotion extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("SetInMotion")) { return false; } diff --git a/src/main/java/forge/card/replacement/ReplaceTurnFaceUp.java b/src/main/java/forge/card/replacement/ReplaceTurnFaceUp.java index 5bbd31008c5..4f3ddc414ff 100644 --- a/src/main/java/forge/card/replacement/ReplaceTurnFaceUp.java +++ b/src/main/java/forge/card/replacement/ReplaceTurnFaceUp.java @@ -1,6 +1,6 @@ package forge.card.replacement; -import java.util.HashMap; +import java.util.Map; import forge.Card; import forge.card.spellability.SpellAbility; @@ -17,7 +17,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect { * @param mapParams   HashMap * @param host   Card */ - public ReplaceTurnFaceUp(final HashMap mapParams, final Card host) { + public ReplaceTurnFaceUp(final Map mapParams, final Card host) { super(mapParams, host); } @@ -25,7 +25,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap) */ @Override - public boolean canReplace(HashMap runParams) { + public boolean canReplace(Map runParams) { if (!runParams.get("Event").equals("TurnFaceUp")) { return false; } @@ -53,7 +53,7 @@ public class ReplaceTurnFaceUp extends ReplacementEffect { * @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility) */ @Override - public void setReplacingObjects(HashMap runParams, SpellAbility sa) { + public void setReplacingObjects(Map runParams, SpellAbility sa) { sa.setReplacingObject("Card", runParams.get("Affected")); } diff --git a/src/main/java/forge/card/replacement/ReplacementEffect.java b/src/main/java/forge/card/replacement/ReplacementEffect.java index 6e35ad5008f..0ee9b0699d0 100644 --- a/src/main/java/forge/card/replacement/ReplacementEffect.java +++ b/src/main/java/forge/card/replacement/ReplacementEffect.java @@ -118,7 +118,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { } /** The map params, denoting what to replace. */ - private HashMap mapParams = new HashMap(); + private Map mapParams = new HashMap(); /** *

@@ -127,7 +127,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { * * @return a {@link java.util.HashMap} object. */ - public final HashMap getMapParams() { + public final Map getMapParams() { return this.mapParams; } @@ -137,7 +137,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { * @param mapParams * the mapParams to set */ - public final void setMapParams(final HashMap mapParams) { + public final void setMapParams(final Map mapParams) { this.mapParams = mapParams; } @@ -148,7 +148,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { * the run params * @return true, if successful */ - public abstract boolean canReplace(final HashMap runParams); + public abstract boolean canReplace(final Map runParams); /** * To string. @@ -219,7 +219,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { * @param spellAbility * the SpellAbility */ - public void setReplacingObjects(final HashMap runParams, final SpellAbility spellAbility) { + public void setReplacingObjects(final Map runParams, final SpellAbility spellAbility) { // Should be overridden by replacers that need it. } @@ -231,7 +231,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { * @param host * the host */ - public ReplacementEffect(final HashMap map, final Card host) { + public ReplacementEffect(final Map map, final Card host) { this.setMapParams(map); this.setHostCard(host); } diff --git a/src/main/java/forge/card/replacement/ReplacementHandler.java b/src/main/java/forge/card/replacement/ReplacementHandler.java index d85a5ea77b8..3fc979f6080 100644 --- a/src/main/java/forge/card/replacement/ReplacementHandler.java +++ b/src/main/java/forge/card/replacement/ReplacementHandler.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; +import java.util.Map; import forge.Card; import forge.Singletons; @@ -34,6 +35,7 @@ import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; +import forge.util.FileSection; /** * TODO: Write javadoc for this type. @@ -174,9 +176,9 @@ public class ReplacementHandler { * @param replacementEffect * the replacement effect to run */ - private ReplacementResult executeReplacement(final HashMap runParams, + private ReplacementResult executeReplacement(final Map runParams, final ReplacementEffect replacementEffect, final Player decider, final GameState game) { - final HashMap mapParams = replacementEffect.getMapParams(); + final Map mapParams = replacementEffect.getMapParams(); SpellAbility effectSA = null; @@ -257,7 +259,7 @@ public class ReplacementHandler { * @return A finished instance */ public static ReplacementEffect parseReplacement(final String repParse, final Card host) { - final HashMap mapParams = ReplacementHandler.parseParams(repParse); + final Map mapParams = FileSection.parseToMap(repParse, "$", "|"); return ReplacementHandler.parseReplacement(mapParams, host); } @@ -272,7 +274,7 @@ public class ReplacementHandler { * The card that hosts the replacement effect * @return The finished instance */ - public static ReplacementEffect parseReplacement(final HashMap mapParams, final Card host) { + public static ReplacementEffect parseReplacement(final Map mapParams, final Card host) { ReplacementEffect ret = null; final String eventToReplace = mapParams.get("Event"); @@ -301,45 +303,4 @@ public class ReplacementHandler { return ret; } - - /** - *

- * parseParams. - *

- * - * @param repParse - * a {@link java.lang.String} object. - * @return a {@link java.util.HashMap} object. - */ - private static HashMap parseParams(final String repParse) { - final HashMap mapParams = new HashMap(); - - 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; - } } diff --git a/src/main/java/forge/card/spellability/SpellPermanent.java b/src/main/java/forge/card/spellability/SpellPermanent.java index 38422672d12..9a2d11a541e 100644 --- a/src/main/java/forge/card/spellability/SpellPermanent.java +++ b/src/main/java/forge/card/spellability/SpellPermanent.java @@ -20,6 +20,7 @@ package forge.card.spellability; import java.security.InvalidParameterException; import java.util.HashMap; import java.util.List; +import java.util.Map; import com.google.common.base.Supplier; import com.google.common.collect.Iterables; @@ -499,7 +500,7 @@ public class SpellPermanent extends Spell { for (final ReplacementEffect re : card.getReplacementEffects()) { // These Replacements all care for ETB effects - final HashMap params = re.getMapParams(); + final Map params = re.getMapParams(); if (!(re instanceof ReplaceMoved)) { continue; } diff --git a/src/main/java/forge/game/ai/ComputerUtilCombat.java b/src/main/java/forge/game/ai/ComputerUtilCombat.java index 788eec485d1..1bda899a2aa 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCombat.java +++ b/src/main/java/forge/game/ai/ComputerUtilCombat.java @@ -1829,7 +1829,7 @@ public class ComputerUtilCombat { // Predict replacement effects for (final Card ca : game.getCardsIn(ZoneType.Battlefield)) { for (final ReplacementEffect re : ca.getReplacementEffects()) { - HashMap params = re.getMapParams(); + Map params = re.getMapParams(); if (!"DamageDone".equals(params.get("Event")) || !params.containsKey("PreventionEffect")) { continue; } From e6a3fb93a7d8059b506cc6208ed348438e789688 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 12:01:21 +0000 Subject: [PATCH 21/27] - Improved AI distributing Damage. --- .../java/forge/game/ai/ComputerUtilCombat.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/forge/game/ai/ComputerUtilCombat.java b/src/main/java/forge/game/ai/ComputerUtilCombat.java index 1bda899a2aa..df536ac53a4 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCombat.java +++ b/src/main/java/forge/game/ai/ComputerUtilCombat.java @@ -1695,14 +1695,18 @@ public class ComputerUtilCombat { Card lastBlocker = null; for (final Card b : block) { lastBlocker = b; - final int enoughDamageToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true); - if (enoughDamageToKill <= dmgCanDeal) { - damageMap.put(b, enoughDamageToKill); - dmgCanDeal -= enoughDamageToKill; + final int dmgToKill = ComputerUtilCombat.getEnoughDamageToKill(b, dmgCanDeal, attacker, true); + if (dmgToKill <= dmgCanDeal) { + damageMap.put(b, dmgToKill); + dmgCanDeal -= dmgToKill; } else { - damageMap.put(b, dmgCanDeal); - dmgCanDeal = 0; - break; + // if it can't be killed choose the minimum damage + int dmg = Math.min(b.getLethalDamage(), dmgCanDeal); + damageMap.put(b, dmg); + dmgCanDeal -= dmg; + if (dmgCanDeal <= 0) { + break; + } } } // for From ee59663f096af7faaf11c3b3442297e1eb03b337 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 12:23:03 +0000 Subject: [PATCH 22/27] - Updated a quest deck. --- res/quest/duels/Albert Einstein 3.dck | 77 ++++++++++++++------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/res/quest/duels/Albert Einstein 3.dck b/res/quest/duels/Albert Einstein 3.dck index c1b34f004b9..144e9501e47 100644 --- a/res/quest/duels/Albert Einstein 3.dck +++ b/res/quest/duels/Albert Einstein 3.dck @@ -1,37 +1,40 @@ -[duel] -[metadata] -Name=Albert Einstein 3 -Title=Albert Einstein -Difficulty=hard -Description=WG deck with Garruk Wildspeaker, Needle Storm and Retribution of the Meek -Icon=Albert Einstein.jpg -Deck Type=constructed -[main] -4 Savannah -4 Razorverge Thicket -4 Windswept Heath -4 Plains|M12 -5 Forest|M12 -1 Mox Pearl -1 Mox Emerald -2 Savannah Lions -1 Elite Vanguard -1 Isamaru, Hound of Konda -1 Weathered Wayfarer -1 Gaddock Teeg -1 Kataki, War's Wage -2 Accorder Paladin -4 Watchwolf -4 Kitchen Finks -4 Wilt-Leaf Cavaliers -1 Mirri, Cat Warrior -2 Garruk Wildspeaker -1 Master of the Wild Hunt -3 Mirran Crusader -1 Ancient Spider -1 Hero of Bladehold -1 Aura Shards -4 Retribution of the Meek -4 Swords to Plowshares -2 Needle Storm -[sideboard] +[duel] +[metadata] +Name=Albert Einstein 3 +Title=Albert Einstein +Difficulty=hard +Description=WG Retribution of the Meek deck +Icon=Albert Einstein.jpg +Deck Type=constructed +[main] +4 Savannah +4 Razorverge Thicket +4 Windswept Heath +4 Plains|M12 +5 Forest|M12 +1 Mox Pearl +1 Mox Emerald +1 Savannah Lions +1 Elite Vanguard +3 Dryad Militant +1 Isamaru, Hound of Konda +1 Weathered Wayfarer +1 Gaddock Teeg +1 Kataki, War's Wage +2 Accorder Paladin +4 Watchwolf +1 Call of the Conclave +4 Kitchen Finks +4 Wilt-Leaf Cavaliers +1 Mirri, Cat Warrior +1 Garruk Wildspeaker +1 Master of the Wild Hunt +2 Mirran Crusader +1 Ancient Spider +1 Hero of Bladehold +1 Aura Shards +4 Retribution of the Meek +2 Swords to Plowshares +2 Path to Exile +1 Needle Storm +[sideboard] From 03d005ed8e58a7f066733b39c19fb15c82333be7 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 13:03:52 +0000 Subject: [PATCH 23/27] - The AI will now try to fetch duallands with fetchlands. --- .../java/forge/card/ability/ai/ChangeZoneAi.java | 12 ++++++++---- src/main/java/forge/game/ai/ComputerUtilCard.java | 8 ++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 43d741c1196..6bc32e6f9a5 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -424,8 +424,7 @@ public class ChangeZoneAi extends SpellAbilityAi { * a {@link forge.CardList} object. * @return a {@link forge.Card} object. */ - private static Card basicManaFixing(final Player ai, final List list) { // Search for a - // Basic Land + private static Card basicManaFixing(final Player ai, final List list) { // Search for a Basic Land final List combined = new ArrayList(ai.getCardsIn(ZoneType.Battlefield)); combined.addAll(ai.getCardsIn(ZoneType.Hand)); @@ -457,6 +456,11 @@ public class ChangeZoneAi extends SpellAbilityAi { if (minType != null) { 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); } @@ -1173,7 +1177,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } else if (origin.contains(ZoneType.Library) && (type.contains("Basic") || areAllBasics(type))) { 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); } else if (ZoneType.Battlefield.equals(destination) || ZoneType.Graveyard.equals(destination)) { if (!activator.equals(ai) && sa.hasParam("GainControl")) { @@ -1190,7 +1194,7 @@ public class ChangeZoneAi extends SpellAbilityAi { // Does AI need a land? List 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; for (Card cardInHand : hand) { canCastSomething |= ComputerUtilMana.payManaCost(cardInHand.getFirstSpellAbility(), ai, true, 0, false); diff --git a/src/main/java/forge/game/ai/ComputerUtilCard.java b/src/main/java/forge/game/ai/ComputerUtilCard.java index 8185acb6271..b4b3ba56d98 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCard.java +++ b/src/main/java/forge/game/ai/ComputerUtilCard.java @@ -124,14 +124,14 @@ public class ComputerUtilCard { */ public static Card getBestLandAI(final List list) { final List land = CardLists.filter(list, CardPredicates.Presets.LANDS); - if (!(land.size() > 0)) { + if (land.isEmpty()) { return null; } // prefer to target non basic lands final List nbLand = CardLists.filter(land, Predicates.not(CardPredicates.Presets.BASIC_LANDS)); - if (nbLand.size() > 0) { + if (!nbLand.isEmpty()) { // TODO - Rank non basics? return Aggregates.random(nbLand); } @@ -243,11 +243,11 @@ public class ComputerUtilCard { public static Card getBestAI(final List list) { // Get Best will filter by appropriate getBest list if ALL of the list // is of that type - if (CardLists.getNotType(list, "Creature").size() == 0) { + if (CardLists.getNotType(list, "Creature").isEmpty()) { return ComputerUtilCard.getBestCreatureAI(list); } - if (CardLists.getNotType(list, "Land").size() == 0) { + if (CardLists.getNotType(list, "Land").isEmpty()) { return getBestLandAI(list); } From 0b267c3490221f6468a1cbda2cef3d5344a28cad Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 16 Mar 2013 13:09:02 +0000 Subject: [PATCH 24/27] - Added new card names to changes.txt. --- CHANGES.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index bc465235b9e..d5a05c214a1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,7 +11,9 @@ Release Notes: New Cards: - +Noggin Whack +Scythe Specter +Sasaya, Orochi Ascendant Known Issues: From 1c23640e428f8009dfbc59ea48fa14448d25075a Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 14:04:30 +0000 Subject: [PATCH 25/27] - Added the 5 Gatecrash precons by Rodan. --- .gitattributes | 5 +++ res/quest/precons/Boros Battalion.dck | 41 ++++++++++++++++++++++++ res/quest/precons/Dimir Dementia.dck | 41 ++++++++++++++++++++++++ res/quest/precons/Gruul Goliaths.dck | 42 +++++++++++++++++++++++++ res/quest/precons/Orzhov Oppression.dck | 41 ++++++++++++++++++++++++ res/quest/precons/Simic Synthesis.dck | 41 ++++++++++++++++++++++++ 6 files changed, 211 insertions(+) create mode 100644 res/quest/precons/Boros Battalion.dck create mode 100644 res/quest/precons/Dimir Dementia.dck create mode 100644 res/quest/precons/Gruul Goliaths.dck create mode 100644 res/quest/precons/Orzhov Oppression.dck create mode 100644 res/quest/precons/Simic Synthesis.dck diff --git a/.gitattributes b/.gitattributes index 22f0ba289df..54fd9d0805e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13212,6 +13212,7 @@ res/quest/precons/Battle[!!-~]Surge.dck -text res/quest/precons/Blood[!!-~]And[!!-~]Fire.dck -text res/quest/precons/Boggart[!!-~]Feast.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/Breath[!!-~]of[!!-~]Fire.dck -text res/quest/precons/Carnival[!!-~]of[!!-~]Blood.dck -text @@ -13231,6 +13232,7 @@ res/quest/precons/Decay.dck -text res/quest/precons/Deep[!!-~]Freeze.dck -text res/quest/precons/Depths[!!-~]of[!!-~]Power.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/Domain.dck -text res/quest/precons/Doom[!!-~]Inevitable.dck -text @@ -13258,6 +13260,7 @@ res/quest/precons/Grave[!!-~]Power.dck -text res/quest/precons/Grixis[!!-~]Shambling[!!-~]Army.dck -text res/quest/precons/Grixis[!!-~]Undead.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/Hold[!!-~]the[!!-~]Line.dck -text res/quest/precons/Infect[!!-~]and[!!-~]Defile.dck -text @@ -13290,6 +13293,7 @@ res/quest/precons/Mysterious[!!-~]Realms.dck -text res/quest/precons/Mystical[!!-~]Might.dck -text res/quest/precons/Naya[!!-~]Behemoths.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/Phyrexian[!!-~]Assault.dck -text res/quest/precons/Phyrexian[!!-~]Poison.dck -text @@ -13316,6 +13320,7 @@ res/quest/precons/Sacrificial[!!-~]Bam.dck -text res/quest/precons/Selesnya[!!-~]Surge.dck -text res/quest/precons/Selesnya[!!-~]United.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/Slaughterhouse.dck -text res/quest/precons/Sleeper.dck -text diff --git a/res/quest/precons/Boros Battalion.dck b/res/quest/precons/Boros Battalion.dck new file mode 100644 index 00000000000..66f96623c6f --- /dev/null +++ b/res/quest/precons/Boros Battalion.dck @@ -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] \ No newline at end of file diff --git a/res/quest/precons/Dimir Dementia.dck b/res/quest/precons/Dimir Dementia.dck new file mode 100644 index 00000000000..9ecea41c4ee --- /dev/null +++ b/res/quest/precons/Dimir Dementia.dck @@ -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] \ No newline at end of file diff --git a/res/quest/precons/Gruul Goliaths.dck b/res/quest/precons/Gruul Goliaths.dck new file mode 100644 index 00000000000..d4ba05a5137 --- /dev/null +++ b/res/quest/precons/Gruul Goliaths.dck @@ -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] \ No newline at end of file diff --git a/res/quest/precons/Orzhov Oppression.dck b/res/quest/precons/Orzhov Oppression.dck new file mode 100644 index 00000000000..648e3d2e7bc --- /dev/null +++ b/res/quest/precons/Orzhov Oppression.dck @@ -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] \ No newline at end of file diff --git a/res/quest/precons/Simic Synthesis.dck b/res/quest/precons/Simic Synthesis.dck new file mode 100644 index 00000000000..dfd3faccbed --- /dev/null +++ b/res/quest/precons/Simic Synthesis.dck @@ -0,0 +1,41 @@ +[shop] +WinsToUnlock=0 +Credits=1200 +MinDifficulty=0 +MaxDifficulty=5 +[metadata] +Name=Simic Synthesis +Description=The Simic Combine is always evolvingÑ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] \ No newline at end of file From e9b51dc21d108a31764e29c0ad9a0ab82c829924 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Sat, 16 Mar 2013 14:27:15 +0000 Subject: [PATCH 26/27] chains_of_mephistopheles.txt hardcode moved from specific player classes several instances of Chains of Mephistopheles are handled closer to the rules --- .../c/chains_of_mephistopheles.txt | 2 + src/main/java/forge/Card.java | 2 +- .../java/forge/game/phase/PhaseHandler.java | 2 +- src/main/java/forge/game/player/AIPlayer.java | 11 -- .../java/forge/game/player/HumanPlayer.java | 13 -- src/main/java/forge/game/player/Player.java | 152 ++++++++++-------- .../java/forge/game/player/PlayerUtil.java | 39 ----- 7 files changed, 86 insertions(+), 135 deletions(-) diff --git a/res/cardsfolder/c/chains_of_mephistopheles.txt b/res/cardsfolder/c/chains_of_mephistopheles.txt index f39312269d1..23dfe643e39 100644 --- a/res/cardsfolder/c/chains_of_mephistopheles.txt +++ b/res/cardsfolder/c/chains_of_mephistopheles.txt @@ -2,6 +2,8 @@ Name:Chains of Mephistopheles ManaCost:1 B 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. +SVar:MillOne:DB$ Mill | NumCards$ 1 +SVar:DiscardOne:DB$ Discard | Mandatory$ True | NumCards$ 1 | Mode$ TgtChoose SVar:RemRandomDeck:True 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. diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index a25749a8a34..ba81d6d9644 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -9052,7 +9052,7 @@ public class Card extends GameEntity implements Comparable { * the rE */ 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); this.getCharacteristics().getReplacementEffects().add(replacementEffectCopy); } diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 2c9fb6545de..9893b613a98 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -265,7 +265,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) { this.setPlayersPriorityPermission(false); } else { - this.getPlayerTurn().drawCards(1, true); + this.getPlayerTurn().drawCard(); } break; diff --git a/src/main/java/forge/game/player/AIPlayer.java b/src/main/java/forge/game/player/AIPlayer.java index 41271b4b875..fa3ede2880a 100644 --- a/src/main/java/forge/game/player/AIPlayer.java +++ b/src/main/java/forge/game/player/AIPlayer.java @@ -144,17 +144,6 @@ public class AIPlayer extends Player { } } - /* - * (non-Javadoc) - * - * @see forge.Player#discard_Chains_of_Mephistopheles() - */ - @Override - protected final void discardChainsOfMephistopheles() { - this.discard(1, null); - this.drawCard(); - } - /* (non-Javadoc) * @see forge.game.player.Player#getType() */ diff --git a/src/main/java/forge/game/player/HumanPlayer.java b/src/main/java/forge/game/player/HumanPlayer.java index 49633907fd5..a752e6ceebb 100644 --- a/src/main/java/forge/game/player/HumanPlayer.java +++ b/src/main/java/forge/game/player/HumanPlayer.java @@ -100,19 +100,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 public final void sacrificePermanent(final String prompt, final List choices) { diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 695393e853e..9ee074e8d5d 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -42,6 +42,8 @@ import forge.Constant.Preferences; import forge.CounterType; import forge.GameEntity; import forge.Singletons; +import forge.card.ability.AbilityFactory; +import forge.card.ability.AbilityUtils; import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.Cost; import forge.card.mana.ManaPool; @@ -50,6 +52,7 @@ import forge.card.replacement.ReplacementResult; import forge.card.spellability.Ability; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; +import forge.card.spellability.Target; import forge.card.staticability.StaticAbility; import forge.card.trigger.TriggerType; import forge.game.GameActionUtil; @@ -65,6 +68,7 @@ import forge.game.event.MulliganEvent; import forge.game.event.PoisonCounterEvent; import forge.game.event.ShuffleEvent; import forge.game.phase.PhaseHandler; +import forge.game.phase.PhaseType; import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.Zone; @@ -125,6 +129,7 @@ public abstract class Player extends GameEntity implements Comparable { /** The num drawn this turn. */ private int numDrawnThisTurn = 0; + private int numDrawnThisDrawStep = 0; /** The slowtrip list. */ private List slowtripList = new ArrayList(); @@ -1239,70 +1244,6 @@ public abstract class Player extends GameEntity implements Comparable { */ public abstract boolean dredge(); - /** - *

- * drawCards. - *

- * - * @param n - * a int. - * @return a List of cards actually drawn - */ - public final List drawCards(final int n) { - return this.drawCards(n, false); - } - - /** - *

- * drawCards. - *

- * - * @param n - * a int. - * @param firstFromDraw - * true if this is the card drawn from that player's draw step - * each turn - * @return a List of cards actually drawn - */ - public final List drawCards(final int n, final boolean firstFromDraw) { - final List drawn = new ArrayList(); - - 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 - game.getEvents().post(new DrawCardEvent()); - - return drawn; - } - /** * * TODO Write javadoc for this method. @@ -1328,6 +1269,40 @@ public abstract class Player extends GameEntity implements Comparable { stats.notifyOpeningHandSize(newHand); } + /** + *

+ * drawCards. + *

+ * + * @param n + * a int. + * @return a List of cards actually drawn + */ + public final List drawCards(final int n) { + final List drawn = new ArrayList(); + + 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; + } + /** *

* doDraw. @@ -1348,7 +1323,46 @@ public abstract class Player extends GameEntity implements Comparable { 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 chainsList = null; + for(Card c: game.getCardsInGame()) { + if ( c.getName().equals("Chains of Mephistopheles") ) { + if ( null == chainsList ) + chainsList = new ArrayList(); + 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); c = game.getAction().moveToHand(c); @@ -1371,6 +1385,8 @@ public abstract class Player extends GameEntity implements Comparable { this.setLastDrawnCard(c); c.setDrawnThisTurn(true); this.numDrawnThisTurn++; + if ( game.getPhaseHandler().is(PhaseType.DRAW)) + this.numDrawnThisDrawStep++; // Miracle draws if (this.numDrawnThisTurn == 1 && game.getPhaseHandler().getTurn() != 0) { @@ -1550,6 +1566,7 @@ public abstract class Player extends GameEntity implements Comparable { */ public final void resetNumDrawnThisTurn() { this.numDrawnThisTurn = 0; + this.numDrawnThisDrawStep = 0; } /** @@ -1569,11 +1586,6 @@ public abstract class Player extends GameEntity implements Comparable { // / // ////////////////////////////// - /** - * Discard_ chains_of_ mephistopheles. - */ - protected abstract void discardChainsOfMephistopheles(); - /** *

* discard. diff --git a/src/main/java/forge/game/player/PlayerUtil.java b/src/main/java/forge/game/player/PlayerUtil.java index 743822cc362..39d852e5785 100644 --- a/src/main/java/forge/game/player/PlayerUtil.java +++ b/src/main/java/forge/game/player/PlayerUtil.java @@ -154,45 +154,6 @@ public final class PlayerUtil { return target; } // input_discard() - /** - *

- * input_chainsDiscard. - *

- * - * @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() - /** *

* input_sacrificePermanent. From 561b8f95bf03b0508792df124862edaa7e5f9f40 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sat, 16 Mar 2013 14:59:46 +0000 Subject: [PATCH 27/27] - Removed some unnecessary addController calls. --- src/main/java/forge/card/ability/effects/EffectEffect.java | 4 +--- src/main/java/forge/card/ability/effects/PlayEffect.java | 2 -- src/main/java/forge/card/cardfactory/CardFactoryUtil.java | 1 - src/main/java/forge/game/GameNew.java | 1 - src/main/java/forge/gui/GuiDisplayUtil.java | 1 - 5 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/forge/card/ability/effects/EffectEffect.java b/src/main/java/forge/card/ability/effects/EffectEffect.java index d2608001f07..f1c0896bfeb 100644 --- a/src/main/java/forge/card/ability/effects/EffectEffect.java +++ b/src/main/java/forge/card/ability/effects/EffectEffect.java @@ -100,9 +100,7 @@ public class EffectEffect extends SpellAbilityEffect { final Card eff = new Card(); eff.setName(name); eff.addType("Effect"); // Or Emblem - eff.setToken(true); // Set token to true, so when leaving play it gets - // nuked - eff.addController(controller); + eff.setToken(true); // Set token to true, so when leaving play it gets nuked eff.setOwner(controller); eff.setImageFilename(sa.hasParam("Image") ? sa.getParam("Image") : hostCard.getImageFilename()); eff.setColor(hostCard.getColor()); diff --git a/src/main/java/forge/card/ability/effects/PlayEffect.java b/src/main/java/forge/card/ability/effects/PlayEffect.java index 95e2e947c9a..40d995d07c7 100644 --- a/src/main/java/forge/card/ability/effects/PlayEffect.java +++ b/src/main/java/forge/card/ability/effects/PlayEffect.java @@ -151,8 +151,6 @@ public class PlayEffect extends SpellAbilityEffect { } if (sa.hasParam("CopyCard")) { tgtCard = CardDb.getCard(tgtCard).toForgeCard(sa.getActivatingPlayer()); - // when copying something stolen: - tgtCard.addController(sa.getActivatingPlayer()); tgtCard.setToken(true); tgtCard.setCopiedSpell(true); diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index 457dc1e448c..58de6f1bc06 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -3171,7 +3171,6 @@ public class CardFactoryUtil { eff.addType("Effect"); // Or Emblem eff.setToken(true); // Set token to true, so when leaving // play it gets nuked - eff.addController(card.getController()); eff.setOwner(card.getController()); eff.setColor(card.getColor()); eff.setImmutable(true); diff --git a/src/main/java/forge/game/GameNew.java b/src/main/java/forge/game/GameNew.java index 9237478b61b..28f4029ee9a 100644 --- a/src/main/java/forge/game/GameNew.java +++ b/src/main/java/forge/game/GameNew.java @@ -334,7 +334,6 @@ public class GameNew { for (final IPaperCard cp : cards) { Card c = cp.toForgeCard(player); c.setOwner(player); - c.addController(player); bf.add(c, false); c.setSickness(true); c.setStartsGameInPlay(true); diff --git a/src/main/java/forge/gui/GuiDisplayUtil.java b/src/main/java/forge/gui/GuiDisplayUtil.java index fb99dc6afc9..79abe02e817 100644 --- a/src/main/java/forge/gui/GuiDisplayUtil.java +++ b/src/main/java/forge/gui/GuiDisplayUtil.java @@ -146,7 +146,6 @@ public final class GuiDisplayUtil { final Card dummy = new Card(); final Player human = Singletons.getControl().getPlayer(); dummy.setOwner(human); - dummy.addController(human); Map produced = new HashMap(); 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);