diff --git a/.gitattributes b/.gitattributes index 5194c947597..f9f1cc79c89 100644 --- a/.gitattributes +++ b/.gitattributes @@ -553,7 +553,7 @@ res/cardsfolder/a/aspect_of_wolf.txt svneol=native#text/plain res/cardsfolder/a/assassinate.txt svneol=native#text/plain res/cardsfolder/a/assassins_blade.txt svneol=native#text/plain res/cardsfolder/a/assassins_strike.txt -text -res/cardsfolder/a/assault__battery.txt -text +res/cardsfolder/a/assault_battery.txt -text res/cardsfolder/a/assault_griffin.txt svneol=native#text/plain res/cardsfolder/a/assault_strobe.txt svneol=native#text/plain res/cardsfolder/a/assault_zeppelid.txt svneol=native#text/plain @@ -1181,6 +1181,7 @@ res/cardsfolder/b/bonfire_of_the_damned.txt -text res/cardsfolder/b/booby_trap.txt -text res/cardsfolder/b/book_burning.txt -text res/cardsfolder/b/book_of_rass.txt svneol=native#text/plain +res/cardsfolder/b/boom_bust.txt -text res/cardsfolder/b/boomerang.txt svneol=native#text/plain res/cardsfolder/b/boon_reflection.txt svneol=native#text/plain res/cardsfolder/b/borborygmos.txt svneol=native#text/plain @@ -2098,6 +2099,7 @@ res/cardsfolder/c/crescendo_of_war.txt -text res/cardsfolder/c/crested_craghorn.txt -text res/cardsfolder/c/crevasse.txt svneol=native#text/plain res/cardsfolder/c/crib_swap.txt svneol=native#text/plain +res/cardsfolder/c/crime_punishment.txt -text res/cardsfolder/c/crimson_acolyte.txt svneol=native#text/plain res/cardsfolder/c/crimson_hellkite.txt -text res/cardsfolder/c/crimson_kobolds.txt svneol=native#text/plain @@ -2372,6 +2374,7 @@ res/cardsfolder/d/day_of_the_dragons.txt svneol=native#text/plain res/cardsfolder/d/daybreak_coronet.txt -text res/cardsfolder/d/daybreak_ranger_nightfall_predator.txt -text res/cardsfolder/d/daze.txt svneol=native#text/plain +res/cardsfolder/d/dead_gone.txt -text res/cardsfolder/d/dead_iron_sledge.txt svneol=native#text/plain res/cardsfolder/d/dead_reckoning.txt -text svneol=unset#text/plain res/cardsfolder/d/dead_reveler.txt -text @@ -4870,6 +4873,7 @@ res/cardsfolder/h/hidden_path.txt svneol=native#text/plain res/cardsfolder/h/hidden_predators.txt -text res/cardsfolder/h/hidden_spider.txt svneol=native#text/plain res/cardsfolder/h/hidden_stag.txt -text svneol=unset#text/plain +res/cardsfolder/h/hide_seek.txt -text res/cardsfolder/h/hideous_end.txt svneol=native#text/plain res/cardsfolder/h/hideous_laughter.txt -text res/cardsfolder/h/hideous_visage.txt svneol=native#text/plain @@ -5117,6 +5121,7 @@ res/cardsfolder/i/ill_gotten_gains.txt svneol=native#text/plain res/cardsfolder/i/illness_in_the_ranks.txt -text res/cardsfolder/i/illuminated_wings.txt svneol=native#text/plain res/cardsfolder/i/illumination.txt svneol=native#text/plain +res/cardsfolder/i/illusion_reality.txt -text res/cardsfolder/i/illusionary_forces.txt svneol=native#text/plain res/cardsfolder/i/illusionary_servant.txt svneol=native#text/plain res/cardsfolder/i/illusionary_wall.txt svneol=native#text/plain @@ -5995,6 +6000,7 @@ res/cardsfolder/l/lieutenant_kirtar.txt svneol=native#text/plain res/cardsfolder/l/life_and_limb.txt svneol=native#text/plain res/cardsfolder/l/life_burst.txt svneol=native#text/plain res/cardsfolder/l/life_chisel.txt svneol=native#text/plain +res/cardsfolder/l/life_death.txt -text res/cardsfolder/l/life_from_the_loam.txt svneol=native#text/plain res/cardsfolder/l/life_matrix.txt -text svneol=unset#text/plain res/cardsfolder/l/lifeblood.txt svneol=native#text/plain @@ -7151,6 +7157,7 @@ res/cardsfolder/n/niblis_of_the_mist.txt -text res/cardsfolder/n/niblis_of_the_urn.txt -text res/cardsfolder/n/nicol_bolas.txt svneol=native#text/plain res/cardsfolder/n/nicol_bolas_planeswalker.txt svneol=native#text/plain +res/cardsfolder/n/night_day.txt -text res/cardsfolder/n/night_dealings.txt -text svneol=unset#text/plain res/cardsfolder/n/night_of_souls_betrayal.txt svneol=native#text/plain res/cardsfolder/n/night_revelers.txt -text @@ -7421,6 +7428,7 @@ res/cardsfolder/o/orcish_spy.txt svneol=native#text/plain res/cardsfolder/o/orcish_squatters.txt -text svneol=unset#text/plain res/cardsfolder/o/orcish_squatters_avatar.txt -text res/cardsfolder/o/orcish_veteran.txt svneol=native#text/plain +res/cardsfolder/o/order_chaos.txt -text res/cardsfolder/o/order_of_leitbur.txt svneol=native#text/plain res/cardsfolder/o/order_of_the_ebon_hand.txt svneol=native#text/plain res/cardsfolder/o/order_of_the_golden_cricket.txt svneol=native#text/plain @@ -7515,6 +7523,7 @@ res/cardsfolder/p/pact_of_negation.txt svneol=native#text/plain res/cardsfolder/p/pact_of_the_titan.txt svneol=native#text/plain res/cardsfolder/p/pain_kami.txt svneol=native#text/plain res/cardsfolder/p/pain_magnification.txt svneol=native#text/plain +res/cardsfolder/p/pain_suffering.txt -text res/cardsfolder/p/painbringer.txt -text svneol=unset#text/plain res/cardsfolder/p/painful_memories.txt svneol=native#text/plain res/cardsfolder/p/painful_quandary.txt -text @@ -8063,6 +8072,7 @@ res/cardsfolder/p/puppeteer_clique.txt svneol=native#text/plain res/cardsfolder/p/puppets_verdict.txt -text res/cardsfolder/p/pure_intentions.txt -text svneol=unset#text/plain res/cardsfolder/p/pure_reflection.txt -text +res/cardsfolder/p/pure_simple.txt -text res/cardsfolder/p/purelace.txt -text res/cardsfolder/p/puresight_merrow.txt svneol=native#text/plain res/cardsfolder/p/puresteel_paladin.txt -text @@ -8751,6 +8761,7 @@ res/cardsfolder/r/rotting_fensnake.txt -text res/cardsfolder/r/rotting_giant.txt -text res/cardsfolder/r/rotting_legion.txt svneol=native#text/plain res/cardsfolder/r/rotting_rats.txt svneol=native#text/plain +res/cardsfolder/r/rough_tumble.txt -text res/cardsfolder/r/roughshod_mentor.txt svneol=native#text/plain res/cardsfolder/r/rouse.txt svneol=native#text/plain res/cardsfolder/r/rowan_treefolk.txt svneol=native#text/plain @@ -10065,6 +10076,7 @@ res/cardsfolder/s/spiritual_guardian.txt svneol=native#text/plain res/cardsfolder/s/spiritual_sanctuary.txt svneol=native#text/plain res/cardsfolder/s/spiritual_visit.txt -text res/cardsfolder/s/spiritualize.txt -text svneol=unset#text/plain +res/cardsfolder/s/spite_malice.txt -text res/cardsfolder/s/spitebellows.txt svneol=native#text/plain res/cardsfolder/s/spiteflame_witch.txt svneol=native#text/plain res/cardsfolder/s/spiteful_bully.txt svneol=native#text/plain @@ -10167,6 +10179,7 @@ res/cardsfolder/s/stampede_driver.txt svneol=native#text/plain res/cardsfolder/s/stampeding_rhino.txt svneol=native#text/plain res/cardsfolder/s/stampeding_serow.txt svneol=native#text/plain res/cardsfolder/s/stampeding_wildebeests.txt svneol=native#text/plain +res/cardsfolder/s/stand_deliver.txt -text res/cardsfolder/s/stand_firm.txt svneol=native#text/plain res/cardsfolder/s/stand_together.txt -text res/cardsfolder/s/standardize.txt -text svneol=unset#text/plain @@ -10449,6 +10462,7 @@ res/cardsfolder/s/suntail_hawk.txt svneol=native#text/plain res/cardsfolder/s/suntouched_myr.txt svneol=native#text/plain res/cardsfolder/s/sunweb.txt svneol=native#text/plain res/cardsfolder/s/superior_numbers.txt -text svneol=unset#text/plain +res/cardsfolder/s/supply_demand.txt -text res/cardsfolder/s/suppress.txt -text svneol=unset#text/plain res/cardsfolder/s/suppression_field.txt -text res/cardsfolder/s/supreme_exemplar.txt svneol=native#text/plain @@ -11175,6 +11189,7 @@ res/cardsfolder/t/treva_the_renewer.txt svneol=native#text/plain res/cardsfolder/t/trevas_attendant.txt svneol=native#text/plain res/cardsfolder/t/trevas_charm.txt svneol=native#text/plain res/cardsfolder/t/trevas_ruins.txt svneol=native#text/plain +res/cardsfolder/t/trial_error.txt -text res/cardsfolder/t/triangle_of_war.txt svneol=native#text/plain res/cardsfolder/t/triassic_egg.txt svneol=native#text/plain res/cardsfolder/t/tribal_flames.txt svneol=native#text/plain @@ -11931,6 +11946,7 @@ res/cardsfolder/w/wave_of_reckoning.txt -text res/cardsfolder/w/wave_of_terror.txt svneol=native#text/plain res/cardsfolder/w/waves_of_aggression.txt -text res/cardsfolder/w/waveskimmer_aven.txt svneol=native#text/plain +res/cardsfolder/w/wax_wane.txt -text res/cardsfolder/w/waxmane_baku.txt -text res/cardsfolder/w/way_of_the_thief.txt -text res/cardsfolder/w/wayfarers_bauble.txt svneol=native#text/plain @@ -13053,6 +13069,7 @@ res/quest/duels/Pointy[!!-~]Haired[!!-~]Boss[!!-~]3.dck -text res/quest/duels/Princess[!!-~]Selenia[!!-~]1.dck -text res/quest/duels/Professor[!!-~]X[!!-~]2.dck -text res/quest/duels/Professor[!!-~]X[!!-~]3.dck -text +res/quest/duels/Quicksilver[!!-~]3.dck -text res/quest/duels/R2-D2[!!-~]3.dck -text res/quest/duels/Radagast[!!-~]2.dck -text res/quest/duels/Radiant[!!-~]2.dck -text @@ -14102,6 +14119,7 @@ src/main/java/forge/deck/generate/Generate3ColorDeck.java svneol=native#text/pla src/main/java/forge/deck/generate/Generate5ColorDeck.java svneol=native#text/plain src/main/java/forge/deck/generate/GenerateColoredDeckBase.java -text src/main/java/forge/deck/generate/GenerateDeckUtil.java -text +src/main/java/forge/deck/generate/GenerateMonoColorDeck.java -text src/main/java/forge/deck/generate/GenerateThemeDeck.java svneol=native#text/plain src/main/java/forge/deck/generate/package-info.java svneol=native#text/plain src/main/java/forge/deck/io/DeckFileHeader.java -text diff --git a/CHANGES.txt b/CHANGES.txt index 5a7156417af..97248ead715 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,12 +8,18 @@ Release Notes: A new quest world by Serrasmurf based on Ravinca has been added. -An effort is being made to implement the split cards (e.g. Fire/Ice) in Forge. +An effort is being made to implement the split cards (e.g. Fire/Ice) in Forge. The initial effort to add support for split cards may be considered complete and the relevant branch has reen reintegrated to trunk. Expect some rough edges and incompatibilities along the way. The "Scale Image Larger" option in the preferences should be fixed. It should no longer continue to scale images larger when it is disabled. The fat packs should now be available for purchase in the quest mode card shop. +Information for non-card items in spell shop are now shown in the card details panel. + +Multiple problems with Convoke have been fixed. + +Forge should now be the Magic rules enforcing program with the most supported cards! That means we finally beat MtGO! MtGO is missing 828 cards. Even subtracting the Power Nine, which were available in the MtGO cube for some time, that's more than we are missing (805) at the time this was written. + New Cards: @@ -28,6 +34,22 @@ Delaying Shield Fatal Lore Library of Lat-Nam Misfortune +Assault // Battery +Fire // Ice +Night // Day +Trial // Error +Wax // Wane +Dead // Gone +Illusion // Reality +Life // Death +Pure // Simple +Rough // Tumble +Supply // Demand +Hide // Seek +Order // Chaos +Pain // Suffering +Spite // Malice +Stand // Deliver New Phenomenons: diff --git a/res/cardsfolder/a/assault__battery.txt b/res/cardsfolder/a/assault_battery.txt similarity index 85% rename from res/cardsfolder/a/assault__battery.txt rename to res/cardsfolder/a/assault_battery.txt index fae1d1f4b26..23e0732f152 100644 --- a/res/cardsfolder/a/assault__battery.txt +++ b/res/cardsfolder/a/assault_battery.txt @@ -3,9 +3,7 @@ ManaCost:R AlternateMode: Split Types:Sorcery A:SP$ DealDamage | Cost$ R | NumDmg$ 2 | ValidTgts$ Creature,Player | TgtPrompt$ Select target creature or player | SpellDescription$ Assault deals 2 damage to target creature or player. -SetInfo:INV Uncommon -SetInfo:TSB Uncommon -SetInfo:HOP Uncommon +SVar:Picture:http://www.wizards.com/global/images/magic/general/assault_battery.jpg Oracle:Assault deals 2 damage to target creature or player. ALTERNATE @@ -15,4 +13,6 @@ ManaCost:3 G Types:Sorcery A:SP$ Token | Cost$ 3 G | TokenAmount$ 1 | TokenName$ Elephant| TokenTypes$ Creature,Elephant| TokenOwner$ You | TokenColors$ Green | TokenPower$ 3 | TokenToughness$ 3 | TokenImage$ g 3 3 elephant | SpellDescription$ Put a 3/3 green Elephant creature token onto the battlefield. Oracle:Put a 3/3 green Elephant creature token onto the battlefield. -End +SetInfo:TSB Special +SetInfo:HOP Uncommon +SetInfo:INV Uncommon diff --git a/res/cardsfolder/b/boom_bust.txt b/res/cardsfolder/b/boom_bust.txt new file mode 100644 index 00000000000..1f32915f3bd --- /dev/null +++ b/res/cardsfolder/b/boom_bust.txt @@ -0,0 +1,17 @@ +Name:Boom +ManaCost:1 R +AlternateMode: Split +Types:Sorcery +A:SP$ Destroy | Cost$ 1 R | TgtPrompt$ Choose target land you control to destroy | ValidTgts$ Land.YouCtrl | SubAbility$ DestroyOpp | SpellDescription$ Destroy target land you control and target land you don't control. +SVar:DestroyOpp:DB$ Destroy | TgtPrompt$ Choose target land you don't control to destroy | ValidTgts$ Land.YouDontCtrl +SVar:Picture:http://www.wizards.com/global/images/magic/general/boombust.jpg +Oracle:Destroy target land you control and target land you don't control. + +ALTERNATE + +Name:Bust +ManaCost:5 R +Types:Sorcery +A:SP$ DestroyAll | Cost$ 5 R | ValidCards$ Land | SpellDescription$ Destroy all lands. +Oracle:Destroy all lands. +SetInfo:PLC Rare \ No newline at end of file diff --git a/res/cardsfolder/c/crime_punishment.txt b/res/cardsfolder/c/crime_punishment.txt new file mode 100644 index 00000000000..f2f3aab1ca3 --- /dev/null +++ b/res/cardsfolder/c/crime_punishment.txt @@ -0,0 +1,17 @@ +Name:Crime +ManaCost:3 W B +AlternateMode: Split +Types:Sorcery +A:SP$ ChangeZone | Cost$ 3 W B | Origin$ Graveyard | Destination$ Battlefield | GainControl$ True | TgtPrompt$ Choose target creature or enchantment in an opponent's graveyard | ValidTgts$ Creature.OppCtrl,Enchantment.OppCtrl | SpellDescription$ Put target creature or enchantment card from an opponent's graveyard onto the battlefield under your control. +SVar:Picture:http://www.wizards.com/global/images/magic/general/crimepunishment.jpg +Oracle:Put target creature or enchantment card from an opponent's graveyard onto the battlefield under your control. + +ALTERNATE + +Name:Punishment +ManaCost:X B G +Types:Sorcery +A:SP$ DestroyAll | Cost$ X B G | ValidCards$ Artifact.cmcEQX,Creature.cmcEQX,Enchantment.cmcEQX | References$ X | SpellDescription$ Destroy each artifact, creature, and enchantment with converted mana cost X. +SVar:X:Count$xPaid +Oracle:Destroy each artifact, creature, and enchantment with converted mana cost X. +SetInfo:DIS Rare \ No newline at end of file diff --git a/res/cardsfolder/d/dead_gone.txt b/res/cardsfolder/d/dead_gone.txt new file mode 100644 index 00000000000..31bff11b8aa --- /dev/null +++ b/res/cardsfolder/d/dead_gone.txt @@ -0,0 +1,16 @@ +Name:Dead +ManaCost:R +AlternateMode: Split +Types:Instant +A:SP$ DealDamage | Cost$ R | ValidTgts$ Creature | NumDmg$ 2 | SpellDescription$ Dead deals 2 damage to target creature. +SVar:Picture:http://www.wizards.com/global/images/magic/general/deadgone.jpg +Oracle:Dead deals 2 damage to target creature. + +ALTERNATE + +Name:Gone +ManaCost:2 R +Types:Instant +A:SP$ ChangeZone | Cost$ 2 R | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature you don't control | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target creature you don't control to its owner's hand. +Oracle:Return target creature you don't control to its owner's hand. +SetInfo:PLC Common \ No newline at end of file diff --git a/res/cardsfolder/e/eye_of_the_storm.txt b/res/cardsfolder/e/eye_of_the_storm.txt index 532053ac2e4..4b8705905df 100644 --- a/res/cardsfolder/e/eye_of_the_storm.txt +++ b/res/cardsfolder/e/eye_of_the_storm.txt @@ -1,7 +1,7 @@ Name:Eye of the Storm ManaCost:5 U U Types:Enchantment -T:Mode$ SpellCast | ValidCard$ Card.Instant,Card.Sorcery | Execute$ TrigExileSpell | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player casts an instant or sorcery card, exile it. Then that player copies each instant or sorcery card exiled with CARDNAME. For each copy, the player may cast the copy without paying its mana cost. +T:Mode$ SpellCast | ValidCard$ Instant.nonToken,Sorcery.nonToken | Execute$ TrigExileSpell | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player casts an instant or sorcery card, exile it. Then that player copies each instant or sorcery card exiled with CARDNAME. For each copy, the player may cast the copy without paying its mana cost. SVar:TrigExileSpell:AB$ ChangeZone | Cost$ 0 | Defined$ TriggeredCard | Origin$ Stack | Destination$ Exile | Fizzle$ True | RememberChanged$ True | SubAbility$ DBPlaySpell SVar:DBPlaySpell:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Card.IsRemembered | ChooseOrder$ True | Zone$ Exile | RepeatSubAbility$ DBPlay SVar:DBPlay:DB$ Play | Defined$ Imprinted | Controller$ TriggeredCardController | WithoutManaCost$ True | CopyCard$ True | Optional$ True diff --git a/res/cardsfolder/f/fire_ice.txt b/res/cardsfolder/f/fire_ice.txt index f3ef0303faf..a061e35adb8 100644 --- a/res/cardsfolder/f/fire_ice.txt +++ b/res/cardsfolder/f/fire_ice.txt @@ -3,9 +3,7 @@ ManaCost:1 R AlternateMode: Split Types:Instant A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player | TgtPrompt$ Select target creature or player to distribute damage to | NumDmg$ 2 | TargetMin$ 1 | TargetMax$ 2 | DividedAsYouChoose$ 2 | SpellDescription$ Fire deals 2 damage divided as you choose among one or two target creatures and/or players. -SetInfo:APC Uncommon -SetInfo:COM Uncommon -SetInfo:DDJ Uncommon +SVar:Picture:http://www.wizards.com/global/images/magic/general/fire_ice.jpg Oracle:Fire deals 2 damage divided as you choose among one or two target creatures and/or players. ALTERNATE @@ -16,4 +14,6 @@ Types:Instant A:SP$ Tap | Cost$ 1 U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | SubAbility$ DBDraw | SpellDescription$ Tap target permanent. Draw a card. SVar:DBDraw:DB$ Draw | NumCards$ 1 Oracle:Tap target permanent.\nDraw a card. -End +SetInfo:APC Uncommon +SetInfo:COM Uncommon +SetInfo:DDJ Uncommon \ No newline at end of file diff --git a/res/cardsfolder/h/hide_seek.txt b/res/cardsfolder/h/hide_seek.txt new file mode 100644 index 00000000000..c5a0d04cdb1 --- /dev/null +++ b/res/cardsfolder/h/hide_seek.txt @@ -0,0 +1,20 @@ +Name:Hide +ManaCost:W R +AlternateMode: Split +Types:Instant +A:SP$ ChangeZone | Cost$ W R | ValidTgts$ Artifact,Enchantment | TgtPrompt$ Select target artifact or enchantment | Origin$ Battlefield | Destination$ Library | LibraryPosition$ -1 | SpellDescription$ Put target artifact or enchantment on the bottom of its owner's library. +SVar:Picture:http://www.wizards.com/global/images/magic/general/hideseek.jpg + +Oracle:Put target artifact or enchantment on the bottom of its owner's library. + +ALTERNATE + +Name:Seek +ManaCost:W B +Types:Instant +A:SP$ ChangeZone | Cost$ W B | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Origin$ Library | DefinedPlayer$ Targeted | Chooser$ You | Destination$ Exile | Changetype$ Card | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBGainLife | SpellDescription$ Search target opponent's library for a card and exile it. You gain life equal to its converted mana cost. Then that player shuffles his or her library. +SVar:DBGainLife:DB$ GainLife | LifeAmount$ X | References$ X | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Remembered$CardManaCost +Oracle:Search target opponent's library for a card and exile it. You gain life equal to its converted mana cost. Then that player shuffles his or her library. +SetInfo:DIS Rare \ No newline at end of file diff --git a/res/cardsfolder/i/illusion_reality.txt b/res/cardsfolder/i/illusion_reality.txt new file mode 100644 index 00000000000..8a805731680 --- /dev/null +++ b/res/cardsfolder/i/illusion_reality.txt @@ -0,0 +1,17 @@ +Name:Illusion +ManaCost:U +AlternateMode: Split +Types:Instant +A:SP$ ChooseColor | Cost$ U | Defined$ You | SubAbility$ Animate | SpellDescription$ Target spell or permanent becomes the color of your choice until end of turn. +SVar:Animate:DB$ Animate | ValidTgts$ Card | TgtPrompt$ Select target spell or permanent to change the color of | TgtZone$ Stack,Battlefield | Colors$ ChosenColor | OverwriteColors$ True +SVar:Picture:http://www.wizards.com/global/images/magic/general/illusion_reality.jpg +Oracle:Target spell or permanent becomes the color of your choice until end of turn. + +ALTERNATE + +Name:Reality +ManaCost:2 G +Types:Instant +A:SP$ Destroy | Cost$ 2 G | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SpellDescription$ Destroy target artifact. +Oracle:Destroy target artifact. +SetInfo:APC Uncommon \ No newline at end of file diff --git a/res/cardsfolder/l/life_death.txt b/res/cardsfolder/l/life_death.txt new file mode 100644 index 00000000000..f2545760cb3 --- /dev/null +++ b/res/cardsfolder/l/life_death.txt @@ -0,0 +1,19 @@ +Name:Life +ManaCost:G +AlternateMode: Split +Types:Sorcery +A:SP$ AnimateAll | Cost$ G | Power$ 1 | Toughness$ 1 | Types$ Creature | ValidCards$ Land.YouCtrl | SpellDescription$ All lands you control become 1/1 creatures until end of turn. They're still lands. +SVar:Picture:http://www.wizards.com/global/images/magic/general/life_death.jpg +Oracle:All lands you control become 1/1 creatures until end of turn. They're still lands. + +ALTERNATE + +Name:Death +ManaCost:1 B +Types:Sorcery +A:SP$ ChangeZone | Cost$ 1 B | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | TgtPrompt$ Choose target creature card in your graveyard | GainControl$ True | SubAbility$ DBLoseLifeYou | SpellDescription$ Return target creature card from your graveyard to the battlefield. You lose life equal to its converted mana cost. +SVar:DBLoseLifeYou:DB$ LoseLife | Defined$ You | LifeAmount$ X | References$ X +SVar:X:Targeted$CardManaCost +Oracle:Return target creature card from your graveyard to the battlefield. You lose life equal to its converted mana cost. +SetInfo:APC Uncommon +SetInfo:DDJ Uncommon \ No newline at end of file diff --git a/res/cardsfolder/m/murderous_spoils.txt b/res/cardsfolder/m/murderous_spoils.txt index f646be0a1e9..cdf225db831 100644 --- a/res/cardsfolder/m/murderous_spoils.txt +++ b/res/cardsfolder/m/murderous_spoils.txt @@ -1,7 +1,7 @@ Name:Murderous Spoils ManaCost:5 B Types:Instant -A:SP$ Destroy | Cost$ 5 B | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ StealEquip | NoRegen$ True | SpellDescription$ Destroy target nonblack creature. It can't be regenerated. You gain control of all Equipment that was attached to it. (This effect lasts indefinitely.) +A:SP$ Destroy | Cost$ 5 B | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select target non-black creature | SubAbility$ StealEquip | NoRegen$ True | SpellDescription$ Destroy target nonblack creature. It can't be regenerated. You gain control of all Equipment that was attached to it. (This effect lasts indefinitely.) SVar:StealEquip:DB$ GainControl | AllValid$ Targeted.Equipment+Attached SVar:Picture:http://www.wizards.com/global/images/magic/general/murderous_spoils.jpg Oracle:Destroy target nonblack creature. It can't be regenerated. You gain control of all Equipment that was attached to it. (This effect lasts indefinitely.) diff --git a/res/cardsfolder/n/night_day.txt b/res/cardsfolder/n/night_day.txt new file mode 100644 index 00000000000..3493dff7369 --- /dev/null +++ b/res/cardsfolder/n/night_day.txt @@ -0,0 +1,16 @@ +Name:Night +ManaCost:B +AlternateMode: Split +Types:Instant +A:SP$ Pump | Cost$ B | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Target creature gets -1/-1 until end of turn. +SVar:Picture:http://www.wizards.com/global/images/magic/general/night_day.jpg +Oracle:Target creature gets -1/-1 until end of turn. + +ALTERNATE + +Name:Day +ManaCost:2 W +Types:Instant +A:SP$ PumpAll | Cost$ 1 W | ValidTgts$ Player | TgtPrompt$ Select target player | ValidCards$ Creature | NumAtt$ +1 | NumDef$ +1 | SpellDescription$ Creatures target player controls get +1/+1 until end of turn. +Oracle:Creatures target player controls get +1/+1 until end of turn. +SetInfo:APC Uncommon \ No newline at end of file diff --git a/res/cardsfolder/o/order_chaos.txt b/res/cardsfolder/o/order_chaos.txt new file mode 100644 index 00000000000..26a27592eaa --- /dev/null +++ b/res/cardsfolder/o/order_chaos.txt @@ -0,0 +1,20 @@ +Name:Order +ManaCost:3 W +AlternateMode: Split +Types:Instant +A:SP$ ChangeZone | Cost$ 3 W | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | Origin$ Battlefield | Destination$ Exile | SpellDescription$ Exile target attacking creature. +SVar:Picture:http://www.wizards.com/global/images/magic/general/order_chaos.jpg + +Oracle:Exile target attacking creature. + +ALTERNATE + +Name:Chaos +ManaCost:2 R +Types:Instant +A:SP$ Effect | Cost$ 2 R | Name$ Chaos Effect | StaticAbilities$ KWPump | AILogic$ Evasion | SpellDescription$ Creatures can't block this turn. +SVar:KWPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature | AddHiddenKeyword$ CARDNAME can't block. | Description$ Creatures can't block this turn. +SVar:RemAIDeck:True +Oracle:Creatures can't block this turn. +SetInfo:APC Uncommon +SetInfo:HOP Uncommon \ No newline at end of file diff --git a/res/cardsfolder/p/pain_suffering.txt b/res/cardsfolder/p/pain_suffering.txt new file mode 100644 index 00000000000..d254639f83b --- /dev/null +++ b/res/cardsfolder/p/pain_suffering.txt @@ -0,0 +1,17 @@ +Name:Pain +ManaCost:B +AlternateMode: Split +Types:Sorcery +A:SP$ Discard | Cost$ B | ValidTgts$ Player | NumCards$ 1 | Mode$ TgtChoose | SpellDescription$ Target player discards a card. +SVar:Picture:http://www.wizards.com/global/images/magic/general/pain_suffering.jpg +Oracle:Target player discards a card. + +ALTERNATE + +Name:Suffering +ManaCost:3 R +Types:Sorcery +A:SP$ Destroy | Cost$ 3 R | ValidTgts$ Land | TgtPrompt$ Select target land | SpellDescription$ Destroy target land. +Oracle:Destroy target land. +SetInfo:DDH Uncommon +SetInfo:INV Uncommon \ No newline at end of file diff --git a/res/cardsfolder/p/pure_simple.txt b/res/cardsfolder/p/pure_simple.txt new file mode 100644 index 00000000000..2e308369ef0 --- /dev/null +++ b/res/cardsfolder/p/pure_simple.txt @@ -0,0 +1,16 @@ +Name:Pure +ManaCost:1 R G +AlternateMode: Split +Types:Sorcery +A:SP$ Destroy | Cost$ 1 R G | ValidTgts$ Permanent.MultiColor | TgtPrompt$ Select target multicolored permanent | SpellDescription$ Destroy target multicolored permanent. +SVar:Picture:http://www.wizards.com/global/images/magic/general/puresimple.jpg +Oracle:Destroy target multicolored permanent. + +ALTERNATE + +Name:Simple +ManaCost:1 W G +Types:Sorcery +A:SP$ DestroyAll | Cost$ 1 W G | ValidCards$ Aura,Equipment | SpellDescription$ Destroy all Auras and Equipment. +Oracle:Destroy all Auras and Equipment. +SetInfo:DIS Uncommon \ No newline at end of file diff --git a/res/cardsfolder/r/rough_tumble.txt b/res/cardsfolder/r/rough_tumble.txt new file mode 100644 index 00000000000..55430c479c9 --- /dev/null +++ b/res/cardsfolder/r/rough_tumble.txt @@ -0,0 +1,16 @@ +Name:Rough +ManaCost:1 R +AlternateMode: Split +Types:Sorcery +A:SP$ DamageAll | Cost$ 1 R | ValidCards$ Creature.withoutFlying | NumDmg$ 2 | SpellDescription$ Rough deals 2 damage to each creature without flying. +SVar:Picture:http://www.wizards.com/global/images/magic/general/rough_tumble.jpg +Oracle:Rough deals 2 damage to each creature without flying. + +ALTERNATE + +Name:Tumble +ManaCost:5 R +Types:Sorcery +A:SP$ DamageAll | Cost$ 5 R | ValidCards$ Creature.withFlying | NumDmg$ 6 | SpellDescription$ Tumble deals 6 damage to each creature with flying. +Oracle:Tumble deals 6 damage to each creature with flying. +SetInfo:PLC Uncommon \ No newline at end of file diff --git a/res/cardsfolder/s/sakashima_the_impostor.txt b/res/cardsfolder/s/sakashima_the_impostor.txt index 0aa1dc5153c..4d7fbf49f37 100644 --- a/res/cardsfolder/s/sakashima_the_impostor.txt +++ b/res/cardsfolder/s/sakashima_the_impostor.txt @@ -4,7 +4,7 @@ Types:Legendary Creature Human Rogue PT:3/1 # Make Svars for granting abilities and triggers on clones distinct to avoid SVars getting overwritten when cloning a clone K:ETBReplacement:Copy:ChooseCreature:Optional -SVar:ChooseCreature:AB$ ChooseCard | Cost$ 0 | Defined$ You | Amount$ 1 | Choices$ Creature.Other | SubAbility$ DBCopy | RememberChosen$ True | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any creature on the battlefield, except its name is still CARDNAME, it's legendary in addition to its other types, and it gains "2 U U: Return CARDNAME to its owner's hand at the beginning of the next end step." +SVar:ChooseCreature:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Creature.Other | SubAbility$ DBCopy | RememberChosen$ True | SpellDescription$ You may have CARDNAME enter the battlefield as a copy of any creature on the battlefield, except its name is still CARDNAME, it's legendary in addition to its other types, and it gains "2 U U: Return CARDNAME to its owner's hand at the beginning of the next end step." SVar:DBCopy:DB$ Clone | Defined$ Remembered | KeepName$ True | AddTypes$ Legendary | AddAbilities$ ReturnSakashima | AddSVars$ TrigReturnSak SVar:ReturnSakashima:AB$ DelayedTrigger | Cost$ 2 U U | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturnSak | SpellDescription$ Return CARDNAME to it's owners hand at the beginning of the next end step. SVar:TrigReturnSak:AB$ChangeZone | Cost$ 0 | Defined$ Self | Origin$ Battlefield | Destination$ Hand diff --git a/res/cardsfolder/s/spite_malice.txt b/res/cardsfolder/s/spite_malice.txt new file mode 100644 index 00000000000..7a465049dc2 --- /dev/null +++ b/res/cardsfolder/s/spite_malice.txt @@ -0,0 +1,17 @@ +Name:Spite +ManaCost:3 U +AlternateMode: Split +Types:Instant +A:SP$ Counter | Cost$ 3 U | TargetType$ Spell | TgtPrompt$ Select target nonCreature spell | ValidTgts$ Card.nonCreature | SpellDescription$ Counter target noncreature spell. +SVar:Picture:http://www.wizards.com/global/images/magic/general/spite_malice.jpg +Oracle:Counter target noncreature spell. + +ALTERNATE + +Name:Malice +ManaCost:3 B +Types:Instant +A:SP$ Destroy | Cost$ 3 B | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select target nonblack creature | NoRegen$ True | SpellDescription$ Destroy target nonblack creature. It can't be regenerated. +Oracle:Destroy target nonblack creature. It can't be regenerated. +SetInfo:DDH Uncommon +SetInfo:INV Uncommon \ No newline at end of file diff --git a/res/cardsfolder/s/stand_deliver.txt b/res/cardsfolder/s/stand_deliver.txt new file mode 100644 index 00000000000..81e01e2c497 --- /dev/null +++ b/res/cardsfolder/s/stand_deliver.txt @@ -0,0 +1,17 @@ +Name:Stand +ManaCost:W +AlternateMode: Split +Types:Instant +A:SP$ PreventDamage | Cost$ W | ValidTgts$ Creature | Amount$ 2 | TgtPrompt$ Select target creature | SpellDescription$ Prevent the next 2 damage that would be dealt to target creature this turn. +SVar:Picture:http://www.wizards.com/global/images/magic/general/stand_deliver.jpg + +Oracle:Prevent the next 2 damage that would be dealt to target creature this turn. + +ALTERNATE + +Name:Deliver +ManaCost:2 U +Types:Instant +A:SP$ ChangeZone | Cost$ 2 U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target permanent to its owner's hand. +Oracle:Return target permanent to its owner's hand. +SetInfo:INV Uncommon \ No newline at end of file diff --git a/res/cardsfolder/s/supply_demand.txt b/res/cardsfolder/s/supply_demand.txt new file mode 100644 index 00000000000..b68706d9ad3 --- /dev/null +++ b/res/cardsfolder/s/supply_demand.txt @@ -0,0 +1,17 @@ +Name:Supply +ManaCost:X W G +AlternateMode: Split +Types:Sorcery +A:SP$ Token | Cost$ X W G | TokenAmount$ X | References$ X | TokenName$ Saproling | TokenTypes$ Creature,Saproling | TokenOwner$ You | TokenColors$ Green | TokenPower$ 1 | TokenToughness$ 1 | SpellDescription$ Put X 1/1 green Saproling creature tokens onto the battlefield. +SVar:X:Count$xPaid +SVar:Picture:http://www.wizards.com/global/images/magic/general/supplydemand.jpg +Oracle:Put X 1/1 green Saproling creature tokens onto the battlefield. + +ALTERNATE + +Name:Demand +ManaCost:1 W U +Types:Sorcery +A:SP$ ChangeZone | Cost$ 1 W U | Origin$ Library | Destination$ Hand | ChangeType$ Card.MultiColor | ChangeNum$ 1 | SpellDescription$ Search your library for a multicolored card, reveal it, and put it into your hand. Then shuffle your library. +Oracle:Search your library for a multicolored card, reveal it, and put it into your hand. Then shuffle your library. +SetInfo:DIS Uncommon \ No newline at end of file diff --git a/res/cardsfolder/t/trial_error.txt b/res/cardsfolder/t/trial_error.txt new file mode 100644 index 00000000000..8f09b21280d --- /dev/null +++ b/res/cardsfolder/t/trial_error.txt @@ -0,0 +1,18 @@ +Name:Trial +ManaCost:W U +AlternateMode: Split +Types:Instant +A:SP$ ChangeZoneAll | Cost$ W U | ValidTgts$ Creature | TgtPrompt$ Select target creature | RememberTargets$ True | ChangeType$ Creature.blockingRemembered,Creature.isBlockedByRemembered | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBCleanup | SpellDescription$ Return all creatures blocking or blocked by target creature to their owner's hand. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/trialerror.jpg +Oracle:Return all creatures blocking or blocked by target creature to their owner's hand. + +ALTERNATE + +Name:Error +ManaCost:U B +Types:Instant +A:SP$ Counter | Cost$ U B | TargetType$ Spell | TgtPrompt$ Select target multicolored spell | ValidTgts$ Card.MultiColor | SpellDescription$ Counter target multicolored spell. +Oracle:Counter target multicolored spell. +SetInfo:DIS Uncommon \ No newline at end of file diff --git a/res/cardsfolder/w/wax_wane.txt b/res/cardsfolder/w/wax_wane.txt new file mode 100644 index 00000000000..146a4ae4695 --- /dev/null +++ b/res/cardsfolder/w/wax_wane.txt @@ -0,0 +1,17 @@ +Name:Wax +ManaCost:G +AlternateMode: Split +Types:Instant +A:SP$ Pump | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ Target creature gets +2/+2 until end of turn. +SVar:Picture:http://www.wizards.com/global/images/magic/general/wax_wane.jpg +Oracle:Target creature gets +2/+2 until end of turn. + +ALTERNATE + +Name:Wane +ManaCost:W +Types:Instant +A:SP$ Destroy | Cost$ W | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment | SpellDescription$ Destroy target enchantment. +Oracle:Destroy target enchantment. +SetInfo:ARC Uncommon +SetInfo:INV Uncommon \ No newline at end of file diff --git a/res/quest/duels/Quicksilver 3.dck b/res/quest/duels/Quicksilver 3.dck new file mode 100644 index 00000000000..c26b7b3b04d --- /dev/null +++ b/res/quest/duels/Quicksilver 3.dck @@ -0,0 +1,42 @@ +[duel] +[metadata] +Name=Quicksilver 3 +Title=Quicksilver +Difficulty=very hard +Description=WUG Hypergenesis/Show and Tell deck with huge creatures +Icon=Quicksilver.jpg +Deck Type=constructed +[main] +2 Tropical Island +3 Tundra +4 Misty Rainforest +3 Polluted Delta +3 Scalding Tarn +2 Flooded Strand +1 Island +1 Plains +4 Elvish Spirit Guide +4 Simian Spirit Guide +2 Hypergenesis +4 Ardent Plea +4 Shardless Agent +4 Show and Tell +1 Angel of Serenity +1 Angel of Despair +1 Avacyn, Angel of Hope +1 Blazing Archon +1 Blightsteel Colossus +1 Bogardan Hellkite +1 Elesh Norn, Grand Cenobite +1 Emrakul, the Aeons Torn +1 Gisela, Blade of Goldnight +1 Iona, Shield of Emeria +1 Jin-Gitaxias, Core Augur +1 Karrthus, Tyrant of Jund +2 Maelstrom Wanderer +1 Molten Primordial +1 Pelakka Wurm +1 Platinum Emperion +1 Progenitus +1 Thraximundar +[sideboard] diff --git a/res/quest/quest-opponent-icons.txt b/res/quest/quest-opponent-icons.txt index 9932bb93404..a75ec8758ec 100644 --- a/res/quest/quest-opponent-icons.txt +++ b/res/quest/quest-opponent-icons.txt @@ -27,6 +27,7 @@ http://www.cardforge.org/fpics/questAvatars/Buffy.jpg http://www.cardforge.org/fpics/questAvatars/Bushwhacked.jpg http://www.cardforge.org/fpics/questAvatars/C3PO.jpg http://www.cardforge.org/fpics/questAvatars/Cable.jpg +http://www.cardforge.org/fpics/questAvatars/Carnage.jpg http://www.cardforge.org/fpics/questAvatars/Captain%20America.jpg http://www.cardforge.org/fpics/questAvatars/Catwoman.jpg http://www.cardforge.org/fpics/questAvatars/Chief_Wiggum.jpg @@ -145,6 +146,7 @@ http://www.cardforge.org/fpics/questAvatars/Princess%20Selenia.jpg http://www.cardforge.org/fpics/questAvatars/Private%20Domain.jpg http://www.cardforge.org/fpics/questAvatars/Professor%20X.jpg http://www.cardforge.org/fpics/questAvatars/Quest%20for%20Ulas%20Temple.jpg +http://www.cardforge.org/fpics/questAvatars/Quicksilver.jpg http://www.cardforge.org/fpics/questAvatars/R2-D2.jpg http://www.cardforge.org/fpics/questAvatars/Radagast.jpg http://www.cardforge.org/fpics/questAvatars/Radiant.jpg diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index bcac589e379..ce3edced2b7 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -1631,6 +1631,7 @@ public class Card extends GameEntity implements Comparable { public final ManaCost getManaCost() { return this.getCharacteristics().getManaCost(); } + /** *

@@ -9194,7 +9195,7 @@ public class Card extends GameEntity implements Comparable { System.out.println("Trying to sacrifice immutables: " + this); return false; } - if (source != null && !getController().isOpponentOf(source.getActivatingPlayer()) + if (source != null && getController().isOpponentOf(source.getActivatingPlayer()) && getController().hasKeyword("Spells and abilities your opponents control can't cause you to sacrifice permanents.")) { return false; } diff --git a/src/main/java/forge/card/ability/AbilityApiBased.java b/src/main/java/forge/card/ability/AbilityApiBased.java index 97c2b31e056..6c616e3bf7b 100644 --- a/src/main/java/forge/card/ability/AbilityApiBased.java +++ b/src/main/java/forge/card/ability/AbilityApiBased.java @@ -7,7 +7,7 @@ import forge.card.ability.effects.ChangeZoneAllEffect; import forge.card.ability.effects.ChangeZoneEffect; import forge.card.ability.effects.ManaEffect; import forge.card.ability.effects.ManaReflectedEffect; -import forge.card.cardfactory.CardFactoryUtil; +import forge.card.cardfactory.CardFactory; import forge.card.cost.Cost; import forge.card.spellability.AbilityActivated; import forge.card.spellability.AbilityManaPart; @@ -50,7 +50,7 @@ public class AbilityApiBased extends AbilityActivated { public AbilityActivated getCopy() { Target tgt = getTarget() == null ? null : new Target(getTarget()); AbilityActivated res = new AbilityApiBased(api, getSourceCard(), getPayCosts(), tgt, params); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); return res; } diff --git a/src/main/java/forge/card/ability/ai/SacrificeAi.java b/src/main/java/forge/card/ability/ai/SacrificeAi.java index cbd554a1bf7..52fa70a8273 100644 --- a/src/main/java/forge/card/ability/ai/SacrificeAi.java +++ b/src/main/java/forge/card/ability/ai/SacrificeAi.java @@ -4,6 +4,7 @@ import java.util.List; import forge.Card; import forge.CardLists; +import forge.CardPredicates; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; @@ -21,42 +22,8 @@ public class SacrificeAi extends SpellAbilityAi { @Override protected boolean canPlayAI(AIPlayer ai, SpellAbility sa) { - boolean chance = sacrificeTgtAI(ai, sa); - // Some additional checks based on what is being sacrificed, and who is - // sacrificing - final Target tgt = sa.getTarget(); - if (tgt != null) { - final String valid = sa.getParam("SacValid"); - String num = sa.getParam("Amount"); - num = (num == null) ? "1" : num; - final int amount = AbilityUtils.calculateAmount(sa.getSourceCard(), num, sa); - - List list = - CardLists.getValidCards(ai.getOpponent().getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - - if (list.size() == 0) { - return false; - } - - final Card source = sa.getSourceCard(); - if (num.equals("X") && source.getSVar(num).equals("Count$xPaid")) { - // Set PayX here to maximum value. - final int xPay = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount); - source.setSVar("PayX", Integer.toString(xPay)); - } - - final int half = (amount / 2) + (amount % 2); // Half of amount - // rounded up - - // If the Human has at least half rounded up of the amount to be - // sacrificed, cast the spell - if (list.size() < half) { - return false; - } - } - - return chance; + return sacrificeTgtAI(ai, sa); } @Override @@ -83,16 +50,49 @@ public class SacrificeAi extends SpellAbilityAi { private boolean sacrificeTgtAI(final Player ai, final SpellAbility sa) { - final Card card = sa.getSourceCard(); + final Card source = sa.getSourceCard(); final Target tgt = sa.getTarget(); + final boolean destroy = sa.hasParam("Destroy"); Player opp = ai.getOpponent(); if (tgt != null) { tgt.resetTargets(); - if (opp.canBeTargetedBy(sa)) { - tgt.addTarget(opp); - return true; + if (!opp.canBeTargetedBy(sa)) { + return false; + } + tgt.addTarget(opp); + final String valid = sa.getParam("SacValid"); + String num = sa.getParam("Amount"); + num = (num == null) ? "1" : num; + final int amount = AbilityUtils.calculateAmount(sa.getSourceCard(), num, sa); + + List list = + CardLists.getValidCards(ai.getOpponent().getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getSourceCard()); + if (!destroy) { + list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(sa)); } else { + if (!CardLists.getKeyword(list, "Indestructible").isEmpty()) { + // human can choose to destroy indestructibles + return false; + } + } + + if (list.size() == 0) { + return false; + } + + if (num.equals("X") && source.getSVar(num).equals("Count$xPaid")) { + // Set PayX here to maximum value. + final int xPay = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount); + source.setSVar("PayX", Integer.toString(xPay)); + } + + final int half = (amount / 2) + (amount % 2); // Half of amount + // rounded up + + // If the Human has at least half rounded up of the amount to be + // sacrificed, cast the spell + if (!sa.isTrigger() && list.size() < half) { return false; } } @@ -109,9 +109,8 @@ public class SacrificeAi extends SpellAbilityAi { // TODO: Cast if the type is favorable: my "worst" valid is // worse than his "worst" valid final String num = sa.hasParam("Amount") ? sa.getParam("Amount") : "1"; - int amount = AbilityUtils.calculateAmount(card, num, sa); + int amount = AbilityUtils.calculateAmount(source, num, sa); - final Card source = sa.getSourceCard(); if (num.equals("X") && source.getSVar(num).equals("Count$xPaid")) { // Set PayX here to maximum value. amount = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount); diff --git a/src/main/java/forge/card/ability/ai/UntapAllAi.java b/src/main/java/forge/card/ability/ai/UntapAllAi.java index c2b53dfa1b3..8cf2becbcd9 100644 --- a/src/main/java/forge/card/ability/ai/UntapAllAi.java +++ b/src/main/java/forge/card/ability/ai/UntapAllAi.java @@ -1,18 +1,31 @@ package forge.card.ability.ai; +import java.util.List; + +import forge.Card; +import forge.CardLists; +import forge.Singletons; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.game.player.AIPlayer; +import forge.game.zone.ZoneType; public class UntapAllAi extends SpellAbilityAi { @Override protected boolean canPlayAI(AIPlayer aiPlayer, SpellAbility sa) { - // check SubAbilities DoTrigger? + final Card source = sa.getSourceCard(); + final AbilitySub abSub = sa.getSubAbility(); if (abSub != null) { - return true; + String valid = ""; + List list = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield); + if (sa.hasParam("ValidCards")) { + valid = sa.getParam("ValidCards"); + } + list = CardLists.getValidCards(list, valid.split(","), source.getController(), source); + return !list.isEmpty(); } return false; } diff --git a/src/main/java/forge/card/ability/effects/CloneEffect.java b/src/main/java/forge/card/ability/effects/CloneEffect.java index 6df5dac3769..a46784ff9ba 100644 --- a/src/main/java/forge/card/ability/effects/CloneEffect.java +++ b/src/main/java/forge/card/ability/effects/CloneEffect.java @@ -12,6 +12,7 @@ import forge.Command; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; +import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactoryUtil; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; @@ -122,7 +123,7 @@ public class CloneEffect extends SpellAbilityEffect { stateToCopy = cardToCopy.getCurState(); } - CardFactoryUtil.copyState(cardToCopy, stateToCopy, tgtCard); + CardFactory.copyState(cardToCopy, stateToCopy, tgtCard); // must call this before addAbilityFactoryAbilities so cloned added abilities are handled correctly addExtraCharacteristics(tgtCard, sa, origSVars); CardFactoryUtil.addAbilityFactoryAbilities(tgtCard); @@ -140,7 +141,7 @@ public class CloneEffect extends SpellAbilityEffect { tgtCard.addAlternateState(CardCharacteristicName.Flipped); tgtCard.setState(CardCharacteristicName.Flipped); } - CardFactoryUtil.copyState(cardToCopy, CardCharacteristicName.Flipped, tgtCard); + CardFactory.copyState(cardToCopy, CardCharacteristicName.Flipped, tgtCard); addExtraCharacteristics(tgtCard, sa, origSVars); CardFactoryUtil.addAbilityFactoryAbilities(tgtCard); for (int i = 0; i < tgtCard.getStaticAbilityStrings().size(); i++) { diff --git a/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java b/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java index 521ee2cb8be..1bd5ff47928 100644 --- a/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java +++ b/src/main/java/forge/card/ability/effects/CopyPermanentEffect.java @@ -12,6 +12,7 @@ import forge.Command; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; +import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactoryUtil; import forge.card.mana.ManaCost; import forge.card.spellability.Ability; @@ -82,7 +83,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect { if (!c.isToken() || c.isCopiedToken()) { // copy creature and put it onto the battlefield - copy = Singletons.getModel().getCardFactory().getCard(CardDb.getCard(c), sa.getActivatingPlayer()); + copy = CardFactory.getCard(CardDb.getCard(c), sa.getActivatingPlayer()); // when copying something stolen: copy.addController(controller); @@ -90,7 +91,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect { copy.setToken(true); copy.setCopiedToken(true); } else { // isToken() - copy = CardFactoryUtil.copyStats(c); + copy = CardFactory.copyStats(c); copy.setName(c.getName()); copy.setImageFilename(c.getImageFilename()); diff --git a/src/main/java/forge/card/ability/effects/CopySpellAbilityEffect.java b/src/main/java/forge/card/ability/effects/CopySpellAbilityEffect.java index 2197bde1aaa..398073cef00 100644 --- a/src/main/java/forge/card/ability/effects/CopySpellAbilityEffect.java +++ b/src/main/java/forge/card/ability/effects/CopySpellAbilityEffect.java @@ -5,9 +5,9 @@ import java.util.Iterator; import java.util.List; import forge.Card; -import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; +import forge.card.cardfactory.CardFactory; import forge.card.spellability.SpellAbility; import forge.game.player.Player; import forge.gui.GuiChoose; @@ -99,7 +99,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect { for (final SpellAbility chosenSAcopy : chosenSAs) { chosenSAcopy.setActivatingPlayer(controller); for (int i = 0; i < amount; i++) { - Singletons.getModel().getCardFactory().copySpellontoStack(card, chosenSAcopy.getSourceCard(), chosenSAcopy, true); + CardFactory.copySpellontoStack(card, chosenSAcopy.getSourceCard(), chosenSAcopy, true); } } } @@ -115,7 +115,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect { chosenSA.setActivatingPlayer(controller); for (int i = 0; i < amount; i++) { - Singletons.getModel().getCardFactory().copySpellontoStack(card, chosenSA.getSourceCard(), chosenSA, true); + CardFactory.copySpellontoStack(card, chosenSA.getSourceCard(), chosenSA, true); } } } // end resolve diff --git a/src/main/java/forge/card/ability/effects/PlayEffect.java b/src/main/java/forge/card/ability/effects/PlayEffect.java index 2d37c52385b..5322149cfe7 100644 --- a/src/main/java/forge/card/ability/effects/PlayEffect.java +++ b/src/main/java/forge/card/ability/effects/PlayEffect.java @@ -150,7 +150,7 @@ public class PlayEffect extends SpellAbilityEffect { source.clearRemembered(); } if (sa.hasParam("CopyCard")) { - tgtCard = Singletons.getModel().getCardFactory().getCard(CardDb.getCard(tgtCard), sa.getActivatingPlayer()); + tgtCard = CardDb.getCard(tgtCard).toForgeCard(sa.getActivatingPlayer()); // when copying something stolen: tgtCard.addController(sa.getActivatingPlayer()); diff --git a/src/main/java/forge/card/ability/effects/SacrificeEffect.java b/src/main/java/forge/card/ability/effects/SacrificeEffect.java index b9cd9074dda..a875e5f701d 100644 --- a/src/main/java/forge/card/ability/effects/SacrificeEffect.java +++ b/src/main/java/forge/card/ability/effects/SacrificeEffect.java @@ -3,6 +3,8 @@ package forge.card.ability.effects; import java.util.List; import forge.Card; +import forge.CardLists; +import forge.CardPredicates; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.SpellAbilityEffect; @@ -51,6 +53,9 @@ public class SacrificeEffect extends SpellAbilityEffect { for (final Player p : tgts) { List battlefield = p.getCardsIn(ZoneType.Battlefield); List validTargets = AbilityUtils.filterListByType(battlefield, valid, sa); + if (!destroy) { + validTargets = CardLists.filter(validTargets, CardPredicates.canBeSacrificedBy(sa)); + } if (sa.hasParam("Random")) { choosenToSacrifice = Aggregates.random(validTargets, Math.min(amount, validTargets.size())); diff --git a/src/main/java/forge/card/cardfactory/CardFactory.java b/src/main/java/forge/card/cardfactory/CardFactory.java index 2639a575b82..c16cb4e339f 100644 --- a/src/main/java/forge/card/cardfactory/CardFactory.java +++ b/src/main/java/forge/card/cardfactory/CardFactory.java @@ -27,6 +27,7 @@ import forge.CardCharacteristicName; import forge.CardColor; import forge.CardUtil; import forge.Color; +import forge.card.CardCharacteristics; import forge.card.CardRules; import forge.card.CardSplitType; import forge.card.ICardFace; @@ -39,12 +40,9 @@ import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellPermanent; import forge.card.spellability.Target; import forge.card.trigger.TriggerHandler; -import forge.error.BugReporter; import forge.game.player.Player; -import forge.gui.GuiUtils; import forge.item.CardDb; import forge.item.IPaperCard; -import forge.properties.NewConstants; /** *

@@ -65,33 +63,6 @@ import forge.properties.NewConstants; * @version $Id$ */ public class CardFactory { - - /** - *

- * Constructor for CardFactory. - *

- * - * @param file - * a {@link java.io.File} object. - */ - private final CardStorageReader reader; - - public CardFactory() { - - GuiUtils.checkEDT("CardFactory$constructor", false); - reader = new CardStorageReader(NewConstants.CARD_DATA_DIR.defaultLoc, true); - try { - // this fills in our map of card names to Card instances. - final List listCardRules = reader.loadCards(); - CardDb.setup(listCardRules.iterator()); - - } catch (final Exception ex) { - BugReporter.reportException(ex); - } - - } // constructor - - /** *

* copyCard. @@ -101,15 +72,15 @@ public class CardFactory { * a {@link forge.Card} object. * @return a {@link forge.Card} object. */ - public final Card copyCard(final Card in) { + public final static Card copyCard(final Card in) { final CardCharacteristicName curState = in.getCurState(); if (in.isInAlternateState()) { in.setState(CardCharacteristicName.Original); } - final Card out = this.getCard(CardDb.getCard(in), in.getOwner()); + final Card out = getCard(CardDb.getCard(in), in.getOwner()); out.setUniqueNumber(in.getUniqueNumber()); - CardFactoryUtil.copyCharacteristics(in, out); + CardFactory.copyCharacteristics(in, out); if (in.hasAlternateState()) { for (final CardCharacteristicName state : in.getStates()) { in.setState(state); @@ -117,7 +88,7 @@ public class CardFactory { out.addAlternateState(state); } out.setState(state); - CardFactoryUtil.copyCharacteristics(in, out); + CardFactory.copyCharacteristics(in, out); } in.setState(curState); out.setState(curState); @@ -154,7 +125,7 @@ public class CardFactory { * @param bCopyDetails * a boolean. */ - public final void copySpellontoStack(final Card source, final Card original, final SpellAbility sa, + public final static void copySpellontoStack(final Card source, final Card original, final SpellAbility sa, final boolean bCopyDetails) { //Player originalController = original.getController(); Player controller = sa.getActivatingPlayer(); @@ -240,7 +211,7 @@ public class CardFactory { * @return a {@link forge.Card} instance, owned by owner; or the special * blankCard */ - public final Card getCard(final IPaperCard cp, final Player owner) { + public final static Card getCard(final IPaperCard cp, final Player owner) { //System.out.println(cardName); CardRules cardRules = cp.getRules(); @@ -435,4 +406,110 @@ public class CardFactory { } } + /** + *

+ * Copies stats like power, toughness, etc. + *

+ * + * @param sim + * a {@link java.lang.Object} object. + * @return a {@link forge.Card} object. + */ + public static Card copyStats(final Card sim) { + final Card c = new Card(); + + c.setFlipCard(sim.isFlipCard()); + c.setDoubleFaced(sim.isDoubleFaced()); + c.setCurSetCode(sim.getCurSetCode()); + + final CardCharacteristicName origState = sim.getCurState(); + for (final CardCharacteristicName state : sim.getStates()) { + c.addAlternateState(state); + c.setState(state); + sim.setState(state); + CardFactory.copyCharacteristics(sim, c); + } + + sim.setState(origState); + c.setState(origState); + c.setRules(sim.getRules()); + + return c; + } // copyStats() + + /** + * Copy characteristics. + * + * @param from + * the from + * @param to + * the to + */ + private static void copyCharacteristics(final Card from, final Card to) { + to.setBaseAttack(from.getBaseAttack()); + to.setBaseDefense(from.getBaseDefense()); + to.setBaseLoyalty(from.getBaseLoyalty()); + to.setBaseAttackString(from.getBaseAttackString()); + to.setBaseDefenseString(from.getBaseDefenseString()); + to.setIntrinsicKeyword(from.getIntrinsicKeyword()); + to.setName(from.getName()); + to.setType(from.getCharacteristics().getType()); + to.setText(from.getSpellText()); + to.setManaCost(from.getManaCost()); + to.setColor(from.getColor()); + to.setSVars(from.getSVars()); + to.setIntrinsicAbilities(from.getIntrinsicAbilities()); + + to.setImageFilename(from.getImageFilename()); + to.setTriggers(from.getTriggers()); + to.setReplacementEffects(from.getReplacementEffects()); + to.setStaticAbilityStrings(from.getStaticAbilityStrings()); + + } + + /** + * Copy characteristics. + * + * @param from + * the from + * @param stateToCopy + * the state to copy + * @param to + * the to + */ + public static void copyState(final Card from, final CardCharacteristicName stateToCopy, final Card to) { + + // copy characteristics not associated with a state + to.setBaseLoyalty(from.getBaseLoyalty()); + to.setBaseAttackString(from.getBaseAttackString()); + to.setBaseDefenseString(from.getBaseDefenseString()); + to.setText(from.getSpellText()); + + // get CardCharacteristics for desired state + CardCharacteristics characteristics = from.getState(stateToCopy); + to.getCharacteristics().copy(characteristics); + // handle triggers and replacement effect through Card class interface + to.setTriggers(characteristics.getTriggers()); + to.setReplacementEffects(characteristics.getReplacementEffects()); + } + + public static void copySpellAbility(SpellAbility from, SpellAbility to) { + to.setDescription(from.getDescription()); + to.setStackDescription(from.getDescription()); + + if (from.getSubAbility() != null) { + to.setSubAbility(from.getSubAbility().getCopy()); + } + if (from.getRestrictions() != null) { + to.setRestrictions(from.getRestrictions()); + } + if (from.getConditions() != null) { + to.setConditions(from.getConditions()); + } + + for (String sVar : from.getSVars()) { + to.setSVar(sVar, from.getSVar(sVar)); + } + } + } // end class AbstractCardFactory diff --git a/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java b/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java index ff6ec2fdbde..c933627fdfe 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java @@ -48,7 +48,7 @@ class CardFactoryArtifacts { public AbilityActivated getCopy() { AbilityActivated res = new AbilityGrindstone(getSourceCard(), getPayCosts(), getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); return res; } @@ -118,7 +118,7 @@ class CardFactoryArtifacts { public AbilityActivated getCopy() { AbilityActivated res = new AbilityScrollRack(getSourceCard(), getPayCosts(), getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); return res; } @@ -261,7 +261,7 @@ class CardFactoryArtifacts { public AbilityActivated getCopy() { AbilityActivated res = new AbilityTemporalAperture(getSourceCard(), getPayCosts(), getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); return res; } diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index 479cb118c7b..d132c9aae94 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -40,7 +40,6 @@ import forge.Constant; import forge.CounterType; import forge.GameEntity; import forge.Singletons; -import forge.card.CardCharacteristics; import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; @@ -146,7 +145,7 @@ public class CardFactoryUtil { public AbilityActivated getCopy() { AbilityActivated res = new AbilityUnearth(getSourceCard(), getPayCosts(), getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); final SpellAbilityRestriction restrict = new SpellAbilityRestriction(); restrict.setZone(ZoneType.Graveyard); restrict.setSorcerySpeed(true); @@ -359,7 +358,7 @@ public class CardFactoryUtil { public AbilityActivated getCopy() { AbilityActivated res = new AbilityTransmute(getSourceCard(), getPayCosts(), getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); res.getRestrictions().setZone(ZoneType.Hand); return res; } @@ -2488,7 +2487,7 @@ public class CardFactoryUtil { final int multiplier = controller.getTokenDoublersMagnitude(); for (int i = 0; i < multiplier; i++) { - Card temp = CardFactoryUtil.copyStats(c); + Card temp = CardFactory.copyStats(c); for (final String kw : intrinsicKeywords) { temp.addIntrinsicKeyword(kw); @@ -2669,112 +2668,6 @@ public class CardFactoryUtil { || (c == CounterType.TIME); } - /** - *

- * Copies stats like power, toughness, etc. - *

- * - * @param sim - * a {@link java.lang.Object} object. - * @return a {@link forge.Card} object. - */ - public static Card copyStats(final Card sim) { - final Card c = new Card(); - - c.setFlipCard(sim.isFlipCard()); - c.setDoubleFaced(sim.isDoubleFaced()); - c.setCurSetCode(sim.getCurSetCode()); - - final CardCharacteristicName origState = sim.getCurState(); - for (final CardCharacteristicName state : sim.getStates()) { - c.addAlternateState(state); - c.setState(state); - sim.setState(state); - CardFactoryUtil.copyCharacteristics(sim, c); - } - - sim.setState(origState); - c.setState(origState); - c.setRules(sim.getRules()); - - return c; - } // copyStats() - - /** - * Copy characteristics. - * - * @param from - * the from - * @param to - * the to - */ - public static void copyCharacteristics(final Card from, final Card to) { - to.setBaseAttack(from.getBaseAttack()); - to.setBaseDefense(from.getBaseDefense()); - to.setBaseLoyalty(from.getBaseLoyalty()); - to.setBaseAttackString(from.getBaseAttackString()); - to.setBaseDefenseString(from.getBaseDefenseString()); - to.setIntrinsicKeyword(from.getIntrinsicKeyword()); - to.setName(from.getName()); - to.setType(from.getCharacteristics().getType()); - to.setText(from.getSpellText()); - to.setManaCost(from.getManaCost()); - to.setColor(from.getColor()); - to.setSVars(from.getSVars()); - to.setIntrinsicAbilities(from.getIntrinsicAbilities()); - - to.setImageFilename(from.getImageFilename()); - to.setTriggers(from.getTriggers()); - to.setReplacementEffects(from.getReplacementEffects()); - to.setStaticAbilityStrings(from.getStaticAbilityStrings()); - - } - - /** - * Copy characteristics. - * - * @param from - * the from - * @param stateToCopy - * the state to copy - * @param to - * the to - */ - public static void copyState(final Card from, final CardCharacteristicName stateToCopy, final Card to) { - - // copy characteristics not associated with a state - to.setBaseLoyalty(from.getBaseLoyalty()); - to.setBaseAttackString(from.getBaseAttackString()); - to.setBaseDefenseString(from.getBaseDefenseString()); - to.setText(from.getSpellText()); - - // get CardCharacteristics for desired state - CardCharacteristics characteristics = from.getState(stateToCopy); - to.getCharacteristics().copy(characteristics); - // handle triggers and replacement effect through Card class interface - to.setTriggers(characteristics.getTriggers()); - to.setReplacementEffects(characteristics.getReplacementEffects()); - } - - public static void copySpellAbility(SpellAbility from, SpellAbility to) { - to.setDescription(from.getDescription()); - to.setStackDescription(from.getDescription()); - - if (from.getSubAbility() != null) { - to.setSubAbility(from.getSubAbility().getCopy()); - } - if (from.getRestrictions() != null) { - to.setRestrictions(from.getRestrictions()); - } - if (from.getConditions() != null) { - to.setConditions(from.getConditions()); - } - - for (String sVar : from.getSVars()) { - to.setSVar(sVar, from.getSVar(sVar)); - } - } - public static void correctAbilityChainSourceCard(final SpellAbility sa, final Card card) { sa.setSourceCard(card); diff --git a/src/main/java/forge/card/spellability/AbilitySub.java b/src/main/java/forge/card/spellability/AbilitySub.java index 1146b6cdc44..bf4f169432d 100644 --- a/src/main/java/forge/card/spellability/AbilitySub.java +++ b/src/main/java/forge/card/spellability/AbilitySub.java @@ -28,7 +28,7 @@ import forge.card.ability.effects.ChangeZoneAllEffect; import forge.card.ability.effects.ChangeZoneEffect; import forge.card.ability.effects.ManaEffect; import forge.card.ability.effects.ManaReflectedEffect; -import forge.card.cardfactory.CardFactoryUtil; +import forge.card.cardfactory.CardFactory; import forge.game.player.AIPlayer; /** @@ -109,7 +109,7 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab public AbilitySub getCopy() { Target t = getTarget() == null ? null : new Target(getTarget()); AbilitySub res = new AbilitySub(api, getSourceCard(), t, params); - CardFactoryUtil.copySpellAbility(this, res); + CardFactory.copySpellAbility(this, res); return res; } diff --git a/src/main/java/forge/control/input/InputControl.java b/src/main/java/forge/control/input/InputControl.java index 01cccb60cbf..2d45e142391 100644 --- a/src/main/java/forge/control/input/InputControl.java +++ b/src/main/java/forge/control/input/InputControl.java @@ -148,6 +148,9 @@ public class InputControl extends MyObservable implements java.io.Serializable { * @return a {@link forge.control.input.Input} object. */ public final Input getActualInput() { + if ( !game.hasMulliganned() ) + return new InputMulligan(); + final PhaseHandler handler = game.getPhaseHandler(); final PhaseType phase = handler.getPhase(); final Player playerTurn = handler.getPlayerTurn(); diff --git a/src/main/java/forge/control/input/InputMulligan.java b/src/main/java/forge/control/input/InputMulligan.java index 6939ff93c8c..6524268240d 100644 --- a/src/main/java/forge/control/input/InputMulligan.java +++ b/src/main/java/forge/control/input/InputMulligan.java @@ -174,7 +174,6 @@ public class InputMulligan extends Input { } ga.checkStateEffects(); - Singletons.getModel().getMatch().getInput().clearInput(); Player next = game.getPhaseHandler().getPlayerTurn(); @@ -187,7 +186,9 @@ public class InputMulligan extends Input { VField nextField = CMatchUI.SINGLETON_INSTANCE.getFieldViewFor(next); SDisplayUtil.showTab(nextField); - game.getPhaseHandler().nextPhase(); + game.setMulliganned(true); + Singletons.getModel().getMatch().getInput().clearInput(); + Singletons.getModel().getMatch().getInput().resetInput(); } @Override diff --git a/src/main/java/forge/deck/DeckgenUtil.java b/src/main/java/forge/deck/DeckgenUtil.java index 30ee91d5028..eebc05c4ca9 100644 --- a/src/main/java/forge/deck/DeckgenUtil.java +++ b/src/main/java/forge/deck/DeckgenUtil.java @@ -19,6 +19,8 @@ import forge.Singletons; import forge.deck.generate.Generate2ColorDeck; import forge.deck.generate.Generate3ColorDeck; import forge.deck.generate.Generate5ColorDeck; +import forge.deck.generate.GenerateColoredDeckBase; +import forge.deck.generate.GenerateMonoColorDeck; import forge.deck.generate.GenerateThemeDeck; import forge.game.player.PlayerType; import forge.item.CardDb; @@ -67,7 +69,7 @@ public class DeckgenUtil { * @return {@link forge.deck.Deck} */ public static Deck buildColorDeck(final String[] selection, PlayerType pt) { - ItemPoolView cards = null; + final Deck deck; // Replace "random" with "AI" for deck generation code @@ -75,22 +77,20 @@ public class DeckgenUtil { selection[i] = COLOR_VALS.get(selection[i]); } - // 2, 3, and 5 colors. - if (selection.length == 2) { - final Generate2ColorDeck gen = new Generate2ColorDeck( - selection[0], selection[1]); - cards = gen.get2ColorDeck(60, pt); + GenerateColoredDeckBase gen = null; + + if (selection.length == 1) { + gen = new GenerateMonoColorDeck(selection[0]); + } else if (selection.length == 2) { + gen = new Generate2ColorDeck(selection[0], selection[1]); + } else if (selection.length == 3) { + gen = new Generate3ColorDeck(selection[0], selection[1], selection[2]); + } else { + gen = new Generate5ColorDeck(); } - else if (selection.length == 3) { - final Generate3ColorDeck gen = new Generate3ColorDeck( - selection[0], selection[1], selection[2]); - cards = gen.get3ColorDeck(60, pt); - } - else { - final Generate5ColorDeck gen = new Generate5ColorDeck(); - cards = gen.get5ColorDeck(60, pt); - } - + + ItemPoolView cards = gen == null ? null : gen.getDeck(60, pt); + // After generating card lists, build deck. deck = new Deck(); deck.getMain().addAll(cards); @@ -132,8 +132,8 @@ public class DeckgenUtil { /** @return {@link forge.deck.Deck} */ public static Deck getRandomColorDeck(PlayerType pt) { - final int[] colorCount = new int[] {2, 3, 5}; - final int count = colorCount[(int) (Math.round(Math.random() * 2))]; + final int[] colorCount = new int[] {1, 2, 3, 5}; + final int count = colorCount[MyRandom.getRandom().nextInt(colorCount.length)]; final String[] selection = new String[count]; // A simulated selection of "random 1" will trigger the AI selection process. @@ -288,14 +288,7 @@ public class DeckgenUtil { public static boolean colorCheck(final String[] colors0) { boolean result = true; - if (colors0.length == 1) { - JOptionPane.showMessageDialog(null, - "Sorry, single color generated decks aren't supported yet." - + "\n\rPlease choose at least one more color for this deck.", - "Generate deck: 1 color", JOptionPane.ERROR_MESSAGE); - result = false; - } - else if (colors0.length == 4) { + if (colors0.length == 4) { JOptionPane.showMessageDialog(null, "Sorry, four color generated decks aren't supported yet." + "\n\rPlease use 2, 3, or 5 colors for this deck.", diff --git a/src/main/java/forge/deck/generate/Generate2ColorDeck.java b/src/main/java/forge/deck/generate/Generate2ColorDeck.java index b70dcae7386..1e71bb96eea 100644 --- a/src/main/java/forge/deck/generate/Generate2ColorDeck.java +++ b/src/main/java/forge/deck/generate/Generate2ColorDeck.java @@ -76,7 +76,7 @@ public class Generate2ColorDeck extends GenerateColoredDeckBase { } - public final ItemPoolView get2ColorDeck(final int size, final PlayerType pt) { + public final ItemPoolView getDeck(final int size, final PlayerType pt) { addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); // Add lands diff --git a/src/main/java/forge/deck/generate/Generate3ColorDeck.java b/src/main/java/forge/deck/generate/Generate3ColorDeck.java index a46684b9afa..bc1e7c7aac6 100644 --- a/src/main/java/forge/deck/generate/Generate3ColorDeck.java +++ b/src/main/java/forge/deck/generate/Generate3ColorDeck.java @@ -75,7 +75,7 @@ public class Generate3ColorDeck extends GenerateColoredDeckBase { * the pt * @return a {@link forge.CardList} object. */ - public final ItemPoolView get3ColorDeck(final int size, final PlayerType pt) { + public final ItemPoolView getDeck(final int size, final PlayerType pt) { addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); // Add lands diff --git a/src/main/java/forge/deck/generate/Generate5ColorDeck.java b/src/main/java/forge/deck/generate/Generate5ColorDeck.java index 522340213fe..10f9475517e 100644 --- a/src/main/java/forge/deck/generate/Generate5ColorDeck.java +++ b/src/main/java/forge/deck/generate/Generate5ColorDeck.java @@ -65,7 +65,7 @@ public class Generate5ColorDeck extends GenerateColoredDeckBase { * a PlayerType * @return a {@link forge.CardList} object. */ - public final ItemPoolView get5ColorDeck(final int size, final PlayerType pt) { + public final ItemPoolView getDeck(final int size, final PlayerType pt) { addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); // Add lands diff --git a/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java b/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java index db6c7afb024..138366f0b18 100644 --- a/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java +++ b/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java @@ -40,6 +40,7 @@ import forge.game.player.PlayerType; import forge.item.CardDb; import forge.item.CardPrinted; import forge.item.ItemPool; +import forge.item.ItemPoolView; import forge.properties.ForgePreferences.FPref; import forge.util.Aggregates; import forge.util.MyRandom; @@ -88,6 +89,9 @@ public abstract class GenerateColoredDeckBase { addCmcAdjusted(spells, spellCnt, cmcLevels, cmcAmounts); } + public ItemPoolView getDeck(final int size, final PlayerType pt) { + return null; // all but theme deck do override this method + } protected void addSome(int cnt, List source) { for (int i = 0; i < cnt; i++) { diff --git a/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java b/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java new file mode 100644 index 00000000000..6de68421a63 --- /dev/null +++ b/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java @@ -0,0 +1,94 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.deck.generate; + +import java.util.Arrays; +import java.util.List; + +import forge.card.ColorSet; +import forge.card.MagicColor; +import forge.deck.generate.GenerateDeckUtil.FilterCMC; +import forge.game.player.PlayerType; +import forge.item.CardPrinted; +import forge.item.ItemPoolView; + +/** + *

+ * Generate2ColorDeck class. + *

+ * + * @author Forge + * @version $Id: Generate2ColorDeck.java 19765 2013-02-20 03:01:37Z myk $ + */ +public class GenerateMonoColorDeck extends GenerateColoredDeckBase { + @Override protected final float getLandsPercentage() { return 0.39f; } + @Override protected final float getCreatPercentage() { return 0.36f; } + @Override protected final float getSpellPercentage() { return 0.25f; } + + final List cmcLevels = Arrays.asList( + new GenerateDeckUtil.FilterCMC(0, 2), + new GenerateDeckUtil.FilterCMC(3, 4), + new GenerateDeckUtil.FilterCMC(5, 6), + new GenerateDeckUtil.FilterCMC(7, 20)); + final int[] cmcAmounts = {10, 8, 5, 3}; + + // mana curve of the card pool + // 20x 0 - 2 + // 16x 3 - 4 + // 12x 5 - 6 + // 4x 7 - 20 + // = 52x - card pool (before further random filtering) + + /** + *

+ * Constructor for Generate2ColorDeck. + *

+ * + * @param clr1 + * a {@link java.lang.String} object. + * @param clr2 + * a {@link java.lang.String} object. + */ + public GenerateMonoColorDeck(final String clr1) { + + if (clr1.equals("AI")) { + int color1 = r.nextInt(5); + colors = ColorSet.fromMask(MagicColor.WHITE << color1); + } else { + colors = ColorSet.fromNames(clr1); + } + } + + + public final ItemPoolView getDeck(final int size, final PlayerType pt) { + addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); + + // Add lands + int numLands = (int) (getLandsPercentage() * size); + + tmpDeck.append("numLands:").append(numLands).append("\n"); + + addBasicLand(numLands); + tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); + + adjustDeckSize(size); + tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); + + return tDeck; + } +} diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index 134f1748904..1306e977474 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -38,6 +38,7 @@ import forge.GameEntity; import forge.card.CardSplitType; import forge.card.CardType; import forge.card.ability.effects.AttachEffect; +import forge.card.cardfactory.CardFactory; import forge.card.cost.Cost; import forge.card.mana.ManaCost; import forge.card.replacement.ReplacementEffect; @@ -159,7 +160,7 @@ public class GameAction { if (zoneFrom.is(ZoneType.Battlefield)) { c.setFlipStaus(false); } - copied = forge.Singletons.getModel().getCardFactory().copyCard(c); + copied = CardFactory.copyCard(c); copied.setUnearthed(c.isUnearthed()); copied.setTapped(false); for (final Trigger trigger : copied.getTriggers()) { @@ -671,7 +672,7 @@ public class GameAction { if (p != null && p.is(ZoneType.Battlefield)) { lastKnownInfo = CardUtil.getLKICopy(c); c.clearCounters(); // remove all counters - library.add(forge.Singletons.getModel().getCardFactory().copyCard(c), libPosition); + library.add(CardFactory.copyCard(c), libPosition); } else { c.clearCounters(); // remove all counters library.add(c, libPosition); diff --git a/src/main/java/forge/game/GameActionPlay.java b/src/main/java/forge/game/GameActionPlay.java index d8e72cb8f23..391259925ed 100644 --- a/src/main/java/forge/game/GameActionPlay.java +++ b/src/main/java/forge/game/GameActionPlay.java @@ -73,6 +73,9 @@ public class GameActionPlay { * a {@link forge.card.spellability.SpellAbility} object. */ public final void playSpellAbilityForFree(final SpellAbility sa) { + final Card source = sa.getSourceCard(); + setSplitCardState(source, sa); // Split card support + if (sa.getPayCosts() != null) { if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) { CharmEffect.makeChoices(sa); @@ -180,23 +183,20 @@ public class GameActionPlay { List untappedCreats = CardLists.filter(spell.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES); untappedCreats = CardLists.filter(untappedCreats, CardPredicates.Presets.UNTAPPED); - if (untappedCreats.size() != 0) { + if (!untappedCreats.isEmpty()) { final List choices = new ArrayList(); - for (final Card c : untappedCreats) { - choices.add(c); - } + choices.addAll(untappedCreats); ArrayList usableColors = new ArrayList(); ManaCostBeingPaid newCost = new ManaCostBeingPaid(originalCost.toString()); Card tapForConvoke = null; if (sa.getActivatingPlayer().isHuman()) { - tapForConvoke = GuiChoose.oneOrNone("Tap for Convoke? " + newCost.toString(), - choices); + tapForConvoke = GuiChoose.oneOrNone("Tap for Convoke? " + newCost.toString(), choices); } else { // TODO: AI to choose a creature to tap would go here // Probably along with deciding how many creatures to // tap } - while (tapForConvoke != null && untappedCreats.size() != 0) { + while (tapForConvoke != null && !untappedCreats.isEmpty()) { final Card workingCard = (Card) tapForConvoke; usableColors = GameActionPlay.getConvokableColors(workingCard, newCost); @@ -223,7 +223,7 @@ public class GameActionPlay { sa.addTappedForConvoke(workingCard); choices.remove(workingCard); untappedCreats.remove(workingCard); - if ((choices.size() < 2) || (newCost.getConvertedManaCost() == 0)) { + if (choices.isEmpty() || (newCost.getConvertedManaCost() == 0)) { break; } } else { @@ -239,7 +239,7 @@ public class GameActionPlay { } // will only be null if user cancelled. - if (tapForConvoke != null) { + if (!sa.getTappedForConvoke().isEmpty()) { // Convoked creats are tapped here with triggers // suppressed, // Then again when payment is done(In diff --git a/src/main/java/forge/game/GameNew.java b/src/main/java/forge/game/GameNew.java index eb765f0b3e9..cab89f286af 100644 --- a/src/main/java/forge/game/GameNew.java +++ b/src/main/java/forge/game/GameNew.java @@ -23,16 +23,12 @@ import forge.CardUtil; import forge.Singletons; import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerType; -import forge.control.input.Input; -import forge.control.input.InputControl; -import forge.control.input.InputMulligan; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; import forge.error.BugReporter; import forge.game.event.FlipCoinEvent; import forge.game.phase.PhaseHandler; -import forge.game.phase.PhaseType; import forge.game.player.AIPlayer; import forge.game.player.LobbyPlayer; import forge.game.player.Player; @@ -195,8 +191,6 @@ public class GameNew { removedAnteCards.put(player, myRemovedAnteCards); } - - if (rAICards.size() > 0) { String message = buildFourColumnList("AI deck contains the following cards that it can't play or may be buggy:", rAICards); if (GameType.Quest == game.getType()) { @@ -215,7 +209,116 @@ public class GameNew { JOptionPane.showMessageDialog(null, ante.toString(), "", JOptionPane.INFORMATION_MESSAGE); } - GameNew.actuateGame(match, game, false); + // Deciding which cards go to ante + if (preferences.getPrefBoolean(FPref.UI_ANTE)) { + final String nl = System.getProperty("line.separator"); + final StringBuilder msg = new StringBuilder(); + for (final Player p : game.getPlayers()) { + + final List lib = p.getCardsIn(ZoneType.Library); + Predicate goodForAnte = Predicates.not(CardPredicates.Presets.BASIC_LANDS); + Card ante = Aggregates.random(Iterables.filter(lib, goodForAnte)); + if (ante == null) { + throw new RuntimeException(p + " library is empty."); + } + game.getGameLog().add("Ante", p + " anted " + ante, 0); + VAntes.SINGLETON_INSTANCE.addAnteCard(p, ante); + game.getAction().moveTo(ZoneType.Ante, ante); + msg.append(p.getName()).append(" ante: ").append(ante).append(nl); + } + JOptionPane.showMessageDialog(null, msg, "Ante", JOptionPane.INFORMATION_MESSAGE); + } + + determineFirstTurnPlayer(match.getLastGameOutcome(), game); + + // Draw cards + for (final Player p1 : game.getPlayers()) { + p1.drawCards(p1.getMaxHandSize()); + } + + Thread thGame = new GameInputUpdatesThread(match, game); + thGame.setName("Game input updater"); + thGame.start(); + } + + // ultimate of Karn the Liberated + public static void restartGame(final MatchController match, final GameState game, final Player startingTurn, Map> playerLibraries) { + + Map players = match.getPlayers(); + Map playersConditions = new HashMap(); + + for (Player p : game.getPlayers()) { + playersConditions.put(p, players.get(p.getLobbyPlayer())); + } + + game.setMulliganned(false); + match.getInput().clearInput(); + + //Card.resetUniqueNumber(); + // need this code here, otherwise observables fail + forge.card.trigger.Trigger.resetIDs(); + TriggerHandler trigHandler = game.getTriggerHandler(); + trigHandler.clearTriggerSettings(); + trigHandler.clearDelayedTrigger(); + trigHandler.cleanUpTemporaryTriggers(); + trigHandler.suppressMode(TriggerType.ChangesZone); + + game.getStack().reset(); + GameAction action = game.getAction(); + + + for (Entry p : playersConditions.entrySet()) { + final Player player = p.getKey(); + player.setStartingLife(p.getValue().getStartingLife()); + player.setNumLandsPlayed(0); + putCardsOnBattlefield(player, p.getValue().getCardsOnBattlefield(player)); + + PlayerZone library = player.getZone(ZoneType.Library); + List newLibrary = playerLibraries.get(player); + for (Card c : newLibrary) { + action.moveTo(library, c); + } + + player.shuffle(); + player.getZone(ZoneType.Battlefield).updateObservers(); + player.updateObservers(); + player.getZone(ZoneType.Hand).updateObservers(); + } + + trigHandler.clearSuppression(TriggerType.ChangesZone); + + PhaseHandler phaseHandler = game.getPhaseHandler(); + phaseHandler.setPlayerTurn(startingTurn); + + // Draw cards + for (final Player p : game.getPlayers()) { + p.drawCards(p.getMaxHandSize()); + } + } + + /** + * TODO: Write javadoc for this method. + * @param match + * @param game + */ + private static void determineFirstTurnPlayer(final GameOutcome lastGameOutcome, final GameState game) { + // Only cut/coin toss if it's the first game of the match + Player goesFirst; + Player humanPlayer = Singletons.getControl().getPlayer(); + boolean isFirstGame = lastGameOutcome == null; + if (isFirstGame) { + goesFirst = GameNew.seeWhoPlaysFirstDice(game); + } else { + + goesFirst = lastGameOutcome.isWinner(humanPlayer.getLobbyPlayer()) ? humanPlayer.getOpponent() : humanPlayer; + } + String message = goesFirst + ( isFirstGame ? " has won the coin toss." : " lost the last game."); + boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(message); + if ( goesFirst != humanPlayer ) { + JOptionPane.showMessageDialog(null, message + "\nComputer Going First", "You are drawing", JOptionPane.INFORMATION_MESSAGE); + } + goesFirst = willPlay ? goesFirst : goesFirst.getOpponent(); + game.getPhaseHandler().setPlayerTurn(goesFirst); } private static void initVariantsZones(final Player player, final PlayerStartConditions psc) { @@ -283,128 +386,6 @@ public class GameNew { } - // ultimate of Karn the Liberated - public static void restartGame(final MatchController match, final GameState game, final Player startingTurn, Map> playerLibraries) { - - Map players = match.getPlayers(); - Map playersConditions = new HashMap(); - - for (Player p : game.getPlayers()) { - playersConditions.put(p, players.get(p.getLobbyPlayer())); - } - - match.getInput().clearInput(); - - //Card.resetUniqueNumber(); - // need this code here, otherwise observables fail - forge.card.trigger.Trigger.resetIDs(); - TriggerHandler trigHandler = game.getTriggerHandler(); - trigHandler.clearTriggerSettings(); - trigHandler.clearDelayedTrigger(); - trigHandler.cleanUpTemporaryTriggers(); - trigHandler.suppressMode(TriggerType.ChangesZone); - - game.getStack().reset(); - GameAction action = game.getAction(); - - - for (Entry p : playersConditions.entrySet()) { - final Player player = p.getKey(); - player.setStartingLife(p.getValue().getStartingLife()); - player.setNumLandsPlayed(0); - putCardsOnBattlefield(player, p.getValue().getCardsOnBattlefield(player)); - - PlayerZone library = player.getZone(ZoneType.Library); - List newLibrary = playerLibraries.get(player); - for (Card c : newLibrary) { - action.moveTo(library, c); - } - - player.shuffle(); - player.getZone(ZoneType.Battlefield).updateObservers(); - player.updateObservers(); - player.getZone(ZoneType.Hand).updateObservers(); - } - - trigHandler.clearSuppression(TriggerType.ChangesZone); - - PhaseHandler phaseHandler = game.getPhaseHandler(); - phaseHandler.setPlayerTurn(startingTurn); - - GameNew.actuateGame(match, game, true); - } - - /** - * This must be separated from the newGame method since life totals and - * player details could be adjusted before the game is started. - * - * That process (also cleanup and observer updates) should be done in - * newGame, then when all is ready, call this function. - * @param isRestartedGame Whether the actuated game is the first start or a restart - */ - private static void actuateGame(final MatchController match, final GameState game, boolean isRestartedGame) { - if (!isRestartedGame) { - // Deciding which cards go to ante - if (preferences.getPrefBoolean(FPref.UI_ANTE)) { - final String nl = System.getProperty("line.separator"); - final StringBuilder msg = new StringBuilder(); - for (final Player p : game.getPlayers()) { - - final List lib = p.getCardsIn(ZoneType.Library); - Predicate goodForAnte = Predicates.not(CardPredicates.Presets.BASIC_LANDS); - Card ante = Aggregates.random(Iterables.filter(lib, goodForAnte)); - if (ante == null) { - throw new RuntimeException(p + " library is empty."); - } - game.getGameLog().add("Ante", p + " anted " + ante, 0); - VAntes.SINGLETON_INSTANCE.addAnteCard(p, ante); - game.getAction().moveTo(ZoneType.Ante, ante); - msg.append(p.getName()).append(" ante: ").append(ante).append(nl); - } - JOptionPane.showMessageDialog(null, msg, "Ante", JOptionPane.INFORMATION_MESSAGE); - } - - GameOutcome lastGameOutcome = match.getLastGameOutcome(); - // Only cut/coin toss if it's the first game of the match - Player goesFirst; - Player humanPlayer = Singletons.getControl().getPlayer(); - boolean isFirstGame = lastGameOutcome == null; - if (isFirstGame) { - goesFirst = GameNew.seeWhoPlaysFirstDice(game); - } else { - - goesFirst = lastGameOutcome.isWinner(humanPlayer.getLobbyPlayer()) ? humanPlayer.getOpponent() : humanPlayer; - } - String message = goesFirst + ( isFirstGame ? " has won the coin toss." : " lost the last game."); - boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(message); - if ( goesFirst != humanPlayer ) { - JOptionPane.showMessageDialog(null, message + "\nComputer Going First", "You are drawing", JOptionPane.INFORMATION_MESSAGE); - } - goesFirst = willPlay ? goesFirst : goesFirst.getOpponent(); - game.getPhaseHandler().setPlayerTurn(goesFirst); - } - - // Draw cards - for (final Player p : game.getPlayers()) { - p.drawCards(p.getMaxHandSize()); - } - - - - game.getPhaseHandler().setPhaseState(PhaseType.MULLIGAN); - - InputControl control = match.getInput(); - Input tmp = new InputMulligan(); - control.setInput(tmp); - - - Thread thGame = new GameInputUpdatesThread(match, game); - - match.getInput().getInput().showMessage(); - thGame.setName("Game input updater"); - thGame.start(); - } // newGame() - private static String buildFourColumnList(String firstLine, Iterable cAnteRemoved) { StringBuilder sb = new StringBuilder(firstLine); int i = 0; diff --git a/src/main/java/forge/game/GameState.java b/src/main/java/forge/game/GameState.java index ebe30d10398..88a1c9903f5 100644 --- a/src/main/java/forge/game/GameState.java +++ b/src/main/java/forge/game/GameState.java @@ -666,4 +666,8 @@ public class GameState { // TODO Auto-generated method stub return actionPlay; } + + public boolean mulliganned = false; + public boolean hasMulliganned(){ return mulliganned; } + public void setMulliganned(boolean value) { mulliganned = value; } } diff --git a/src/main/java/forge/game/phase/CombatUtil.java b/src/main/java/forge/game/phase/CombatUtil.java index 4943781681c..7e72f8ffdd7 100644 --- a/src/main/java/forge/game/phase/CombatUtil.java +++ b/src/main/java/forge/game/phase/CombatUtil.java @@ -48,7 +48,6 @@ import forge.game.GameActionUtil; import forge.game.GameState; import forge.game.GlobalRuleChange; import forge.game.ai.ComputerUtil; -import forge.game.ai.ComputerUtilBlock; import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCost; import forge.game.player.AIPlayer; @@ -57,7 +56,6 @@ import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; -import forge.gui.GuiUtils; import forge.gui.framework.EDocID; import forge.gui.framework.SDisplayUtil; import forge.gui.match.views.VCombat; @@ -431,21 +429,8 @@ public class CombatUtil { if (blockers.size() <= 1) { continue; } - - List orderedBlockers = null; - if (player.isHuman()) { - GuiUtils.setPanelSelection(attacker); - List ordered = GuiChoose.order("Choose Blocking Order", "Damaged First", 0, blockers, null, attacker); - - orderedBlockers = new ArrayList(); - for (Object o : ordered) { - orderedBlockers.add((Card) o); - } - } - else { - orderedBlockers = ComputerUtilBlock.orderBlockers(attacker, blockers); - } - combat.setBlockerList(attacker, orderedBlockers); + List orderedBlockers = player.getController().orderBlockers(attacker, blockers); + combat.setBlockerList(attacker, orderedBlockers); } CombatUtil.showCombat(); // Refresh Combat Panel @@ -459,19 +444,7 @@ public class CombatUtil { continue; } - List orderedAttacker = null; - if (blocker.getController().isHuman()) { - GuiUtils.setPanelSelection(blocker); - List ordered = GuiChoose.order("Choose Blocking Order", "Damaged First", 0, attackers, null, blocker); - - orderedAttacker = new ArrayList(); - for (Object o : ordered) { - orderedAttacker.add((Card) o); - } - } - else { - orderedAttacker = ComputerUtilBlock.orderAttackers(blocker, attackers); - } + List orderedAttacker = blocker.getController().getController().orderAttackers(blocker, attackers); combat.setAttackersBlockedByList(blocker, orderedAttacker); } CombatUtil.showCombat(); diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 97281b57b7a..966777b33be 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -52,9 +52,9 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { /** Constant serialVersionUID=5207222278370963197L. */ private static final long serialVersionUID = 5207222278370963197L; - private PhaseType phase = PhaseType.MULLIGAN; - private int turn = 0; - // Start turn at 0, so first untap step will turn it to 1 + private PhaseType phase = PhaseType.UNTAP; + private int turn = 1; + // Start turn at 1, since first untap is where we start private final transient Stack extraTurns = new Stack(); private final transient Map> extraPhases = new HashMap>(); @@ -262,7 +262,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { break; case DRAW: - if (getTurn() == 1 || PhaseUtil.skipDraw(this.getPlayerTurn())) { + if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) { this.setPlayersPriorityPermission(false); } else { this.getPlayerTurn().drawCards(1, true); diff --git a/src/main/java/forge/game/phase/PhaseType.java b/src/main/java/forge/game/phase/PhaseType.java index 75616ee2b62..a75cc138b06 100644 --- a/src/main/java/forge/game/phase/PhaseType.java +++ b/src/main/java/forge/game/phase/PhaseType.java @@ -9,8 +9,6 @@ import org.apache.commons.lang3.StringUtils; public enum PhaseType { - MULLIGAN("Mulligan"), - // Note: Mulligan is not part of "All Phases" which are strictly ingame phases UNTAP("Untap"), UPKEEP("Upkeep"), DRAW("Draw"), diff --git a/src/main/java/forge/game/phase/PhaseUtil.java b/src/main/java/forge/game/phase/PhaseUtil.java index ee1e14b0668..ea4cdaa8e65 100644 --- a/src/main/java/forge/game/phase/PhaseUtil.java +++ b/src/main/java/forge/game/phase/PhaseUtil.java @@ -114,28 +114,7 @@ public class PhaseUtil { game.getPhaseHandler().setPlayersPriorityPermission(false); } - /** - *

- * skipDraw. - *

- * - * @param player - * a {@link forge.game.player.Player} object. - * @return a boolean. - */ - public static boolean skipDraw(final Player player) { - - if (player.hasKeyword("Skip your next draw step.")) { - player.removeKeyword("Skip your next draw step."); - return true; - } - - if (player.hasKeyword("Skip your draw step.")) { - return true; - } - - return false; - } + // ********* Declare Attackers *********** diff --git a/src/main/java/forge/game/phase/Upkeep.java b/src/main/java/forge/game/phase/Upkeep.java index 0279b65f9ae..ed5623d075d 100644 --- a/src/main/java/forge/game/phase/Upkeep.java +++ b/src/main/java/forge/game/phase/Upkeep.java @@ -354,13 +354,14 @@ public class Upkeep extends Phase { final Command paidCommand = Command.BLANK; final Ability blankAbility = Upkeep.BlankAbility(c, upkeepCost); + blankAbility.setActivatingPlayer(controller); final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { @Override public void resolve() { if (controller.isHuman()) { GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, blankAbility.getPayCosts(), - paidCommand, unpaidCommand, null, game); + paidCommand, unpaidCommand, this, game); } else { // computer if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) { ComputerUtil.playNoStack((AIPlayer)controller, blankAbility, game); diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index ec723d369dc..5ed949607ad 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -3241,6 +3241,20 @@ public abstract class Player extends GameEntity implements Comparable { game.getStack().add(miracleTrigger); } + public boolean isSkippingDraw() { + + if (hasKeyword("Skip your next draw step.")) { + removeKeyword("Skip your next draw step."); + return true; + } + + if (hasKeyword("Skip your draw step.")) { + return true; + } + + return false; + } + /** * TODO: Write javadoc for this type. * diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 60fc5ab4808..91092f71e57 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -97,4 +97,7 @@ public abstract class PlayerController { public abstract boolean confirmAction(SpellAbility sa, String mode, String message); public abstract boolean getWillPlayOnFirstTurn(String message); public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message); + + public abstract List orderBlockers(Card attacker, List blockers); + public abstract List orderAttackers(Card blocker, List attackers); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 89b9daaf663..de9e1477167 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -15,6 +15,7 @@ import forge.game.ai.AiController; import forge.game.ai.AiInputBlock; import forge.game.ai.AiInputCommon; import forge.game.ai.ComputerUtil; +import forge.game.ai.ComputerUtilBlock; import forge.game.ai.ComputerUtilCombat; import forge.gui.GuiChoose; @@ -208,4 +209,14 @@ public class PlayerControllerAi extends PlayerController { return brains.confirmStaticApplication(hostCard, affected, logic, message); } + @Override + public List orderBlockers(Card attacker, List blockers) { + return ComputerUtilBlock.orderBlockers(attacker, blockers); + } + + @Override + public List orderAttackers(Card blocker, List attackers) { + return ComputerUtilBlock.orderAttackers(blocker, attackers); + } + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 3f2108b193d..eda9cb5b9c9 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -22,6 +22,7 @@ import forge.game.GameType; import forge.game.phase.PhaseType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; +import forge.gui.GuiUtils; import forge.gui.match.CMatchUI; import forge.item.CardPrinted; @@ -260,4 +261,16 @@ public class PlayerControllerHuman extends PlayerController { return !playDraw.equals(1); } + + @Override + public List orderBlockers(Card attacker, List blockers) { + GuiUtils.setPanelSelection(attacker); + return GuiChoose.order("Choose Blocking Order", "Damaged First", 0, blockers, null, attacker); + } + + @Override + public List orderAttackers(Card blocker, List attackers) { + GuiUtils.setPanelSelection(blocker); + return GuiChoose.order("Choose Blocking Order", "Damaged First", 0, attackers, null, blocker); + } } diff --git a/src/main/java/forge/game/zone/MagicStack.java b/src/main/java/forge/game/zone/MagicStack.java index 82858987c60..dac8c592511 100644 --- a/src/main/java/forge/game/zone/MagicStack.java +++ b/src/main/java/forge/game/zone/MagicStack.java @@ -32,6 +32,7 @@ import forge.CardPredicates.Presets; import forge.Command; import forge.Singletons; import forge.card.ability.AbilityUtils; +import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactoryUtil; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostBeingPaid; @@ -608,7 +609,7 @@ public class MagicStack extends MyObservable { @Override public void execute() { for (int i = 0; i < sp.getSourceCard().getReplicateMagnitude(); i++) { - Singletons.getModel().getCardFactory().copySpellontoStack(sp.getSourceCard(), sp.getSourceCard(), sp, false); + CardFactory.copySpellontoStack(sp.getSourceCard(), sp.getSourceCard(), sp, false); } } }; diff --git a/src/main/java/forge/gui/CardDetailPanel.java b/src/main/java/forge/gui/CardDetailPanel.java index 88bc82ebe13..fa6898ff26c 100644 --- a/src/main/java/forge/gui/CardDetailPanel.java +++ b/src/main/java/forge/gui/CardDetailPanel.java @@ -36,10 +36,12 @@ import javax.swing.border.EtchedBorder; import org.apache.commons.lang3.StringUtils; import forge.Card; +import forge.CardCharacteristicName; import forge.CounterType; import forge.GameEntity; import forge.Singletons; import forge.card.CardEdition; +import forge.card.CardSplitType; import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.toolbox.FLabel; @@ -202,10 +204,14 @@ public class CardDetailPanel extends FPanel { final boolean canShowThis = card.canBeShownTo(Singletons.getControl().getPlayer()); if (canShowThis) { - if (card.getManaCost().toString().equals("") || card.isLand()) { + if (card.getManaCost().isNoCost()) { this.nameCostLabel.setText(card.getName()); } else { - this.nameCostLabel.setText(card.getName() + " - " + card.getManaCost()); + String manaCost = card.getManaCost().toString(); + if ( card.getRules() != null && card.getRules().getSplitType() == CardSplitType.Split && card.getCurState() == CardCharacteristicName.Original) { + manaCost = card.getRules().getMainPart().getManaCost().toString() + " // " + card.getRules().getOtherPart().getManaCost().toString(); + } + this.nameCostLabel.setText(card.getName() + " - " + manaCost); } this.typeLabel.setText(GuiDisplayUtil.formatCardType(card)); diff --git a/src/main/java/forge/gui/GuiDisplayUtil.java b/src/main/java/forge/gui/GuiDisplayUtil.java index ad24bb63a2c..bfc96e052a1 100644 --- a/src/main/java/forge/gui/GuiDisplayUtil.java +++ b/src/main/java/forge/gui/GuiDisplayUtil.java @@ -431,7 +431,7 @@ public final class GuiDisplayUtil { for (final String element : data) { final String[] cardinfo = element.trim().split("\\|"); - final Card c = Singletons.getModel().getCardFactory().getCard(CardDb.instance().getCard(cardinfo[0]), player); + final Card c = CardDb.instance().getCard(cardinfo[0]).toForgeCard(player); boolean hasSetCurSet = false; for (final String info : cardinfo) { @@ -596,7 +596,7 @@ public final class GuiDisplayUtil { */ public static void devModeCardToHand() { final List players = Singletons.getModel().getGame().getPlayers(); - final Player p = GuiChoose.oneOrNone("Put card in play for which player?", players); + final Player p = GuiChoose.oneOrNone("Put card in hand for which player?", players); if (null == p) { return; } diff --git a/src/main/java/forge/gui/InputProxy.java b/src/main/java/forge/gui/InputProxy.java index d767040375b..8219cd50199 100644 --- a/src/main/java/forge/gui/InputProxy.java +++ b/src/main/java/forge/gui/InputProxy.java @@ -52,7 +52,7 @@ public class InputProxy extends MyObservable implements Observer { * @param in * a {@link forge.control.input.Input} object. */ - public void setInput(final Input in) { + public final void setInput(final Input in) { valid = true; this.input = in; this.input.showMessage(); // this call may invalidate the input by the time it returns diff --git a/src/main/java/forge/gui/deckeditor/controllers/CDeckgen.java b/src/main/java/forge/gui/deckeditor/controllers/CDeckgen.java index 9ee2a0026de..aa7d0d5228c 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CDeckgen.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CDeckgen.java @@ -106,15 +106,15 @@ public enum CDeckgen implements ICDoc { switch (colorCount0) { case 2: genConstructed.getMain().addAll( - (new Generate2ColorDeck("AI", "AI")).get2ColorDeck(60, PlayerType.HUMAN)); + (new Generate2ColorDeck("AI", "AI")).getDeck(60, PlayerType.HUMAN)); break; case 3: genConstructed.getMain().addAll( - (new Generate3ColorDeck("AI", "AI", "AI")).get3ColorDeck(60, PlayerType.HUMAN)); + (new Generate3ColorDeck("AI", "AI", "AI")).getDeck(60, PlayerType.HUMAN)); break; case 5: genConstructed.getMain().addAll( - (new Generate5ColorDeck()).get5ColorDeck(60, PlayerType.HUMAN)); + (new Generate5ColorDeck()).getDeck(60, PlayerType.HUMAN)); break; default: } diff --git a/src/main/java/forge/gui/deckeditor/tables/ManaCostRenderer.java b/src/main/java/forge/gui/deckeditor/tables/ManaCostRenderer.java index 97260b48fb3..eab283e18bf 100644 --- a/src/main/java/forge/gui/deckeditor/tables/ManaCostRenderer.java +++ b/src/main/java/forge/gui/deckeditor/tables/ManaCostRenderer.java @@ -24,6 +24,8 @@ import java.util.List; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; +import forge.card.CardRules; +import forge.card.CardSplitType; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.gui.toolbox.CardFaceSymbols; @@ -34,7 +36,13 @@ import forge.gui.toolbox.CardFaceSymbols; public class ManaCostRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1770527102334163549L; - private ManaCost value; + static final int elemtWidth = 13; + static final int elemtGap = 0; + static final int padding0 = 1; + static final int spaceBetweenSplitCosts = 3; + + private ManaCost v1; + private ManaCost v2; /* * (non-Javadoc) @@ -46,8 +54,10 @@ public class ManaCostRenderer extends DefaultTableCellRenderer { @Override public final Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { - this.value = (ManaCost) value; - this.setToolTipText(this.value.toString()); + CardRules v = value instanceof CardRules ? (CardRules) value : null; + this.v1 = v == null ? ManaCost.NO_COST : v.getMainPart().getManaCost(); + this.v2 = v == null || v.getSplitType() != CardSplitType.Split ? null : v.getOtherPart().getManaCost(); + this.setToolTipText(v2 == null ? v1.toString() : v1.toString() + " / " + v2.toString()); return super.getTableCellRendererComponent(table, "", isSelected, hasFocus, row, column); } @@ -60,18 +70,38 @@ public class ManaCostRenderer extends DefaultTableCellRenderer { public final void paint(final Graphics g) { super.paint(g); - final int elemtWidth = 13; - final int elemtGap = 0; - final int padding = 1; - - float xpos = padding; - - final int genericManaCost = this.value.getGenericCost(); - final int xManaCosts = this.value.countX(); - final boolean hasGeneric = (genericManaCost > 0) || this.value.isPureGeneric(); - final List shards = this.value.getShards(); - final int cellWidth = this.getWidth(); + + if ( null == v2 ) + drawCost(g, v1, padding0, cellWidth); + else + { + int shards1 = v1.isPureGeneric() || v1.getGenericCost() > 0 ? 1 : 0; + int shards2 = v2.isPureGeneric() || v2.getGenericCost() > 0 ? 1 : 0; + shards1 += v1.getShards().size(); + shards2 += v2.getShards().size(); + + int perGlyph = (cellWidth - padding0 - spaceBetweenSplitCosts) / (shards1 + shards2); + perGlyph = Math.min(perGlyph, elemtWidth + elemtGap); + drawCost(g, v1, padding0, padding0 + perGlyph * shards1); + drawCost(g, v2, cellWidth - perGlyph * shards2, cellWidth ); + } + } + + /** + * TODO: Write javadoc for this method. + * @param g + * @param padding + * @param cellWidth + */ + private void drawCost(final Graphics g, ManaCost value, final int padding, final int cellWidth) { + float xpos = padding; + final int genericManaCost = value.getGenericCost(); + final int xManaCosts = value.countX(); + final boolean hasGeneric = (genericManaCost > 0) || this.v1.isPureGeneric(); + final List shards = value.getShards(); + + final int cntGlyphs = hasGeneric ? shards.size() + 1 : shards.size(); final float offsetIfNoSpace = cntGlyphs > 1 ? (cellWidth - padding - elemtWidth) / (cntGlyphs - 1f) : elemtWidth + elemtGap; diff --git a/src/main/java/forge/gui/deckeditor/tables/SColumnUtil.java b/src/main/java/forge/gui/deckeditor/tables/SColumnUtil.java index 1d6c2c531ba..502730706cd 100644 --- a/src/main/java/forge/gui/deckeditor/tables/SColumnUtil.java +++ b/src/main/java/forge/gui/deckeditor/tables/SColumnUtil.java @@ -33,6 +33,7 @@ import forge.Singletons; import forge.card.CardAiHints; import forge.card.CardEdition; import forge.card.CardRarity; +import forge.card.CardRules; import forge.card.ColorSet; import forge.card.mana.ManaCost; import forge.deck.DeckBase; @@ -318,11 +319,14 @@ public final class SColumnUtil { private static final Pattern AE_FINDER = Pattern.compile("AE", Pattern.LITERAL); private static ManaCost toManaCost(final InventoryItem i) { - return i instanceof CardPrinted ? ((IPaperCard) i).getRules().getManaCost() : ManaCost.NO_COST; + return i instanceof IPaperCard ? ((IPaperCard) i).getRules().getManaCost() : ManaCost.NO_COST; } - + private static CardRules toCardRules(final InventoryItem i) { + return i instanceof IPaperCard ? ((IPaperCard) i).getRules() : null; + } + private static ColorSet toColor(final InventoryItem i) { - return i instanceof CardPrinted ? ((IPaperCard) i).getRules().getColor() : ColorSet.getNullColor(); + return i instanceof IPaperCard ? ((IPaperCard) i).getRules().getColor() : ColorSet.getNullColor(); } private static int toPower(final InventoryItem i) { @@ -430,7 +434,7 @@ public final class SColumnUtil { private static final Function, Object> FN_COST_GET = new Function, Object>() { @Override public Object apply(final Entry from) { - return SColumnUtil.toManaCost(from.getKey()); + return SColumnUtil.toCardRules(from.getKey()); } }; diff --git a/src/main/java/forge/gui/download/GuiDownloadPicturesLQ.java b/src/main/java/forge/gui/download/GuiDownloadPicturesLQ.java index a824fdbb5a0..27c7ee3cebe 100644 --- a/src/main/java/forge/gui/download/GuiDownloadPicturesLQ.java +++ b/src/main/java/forge/gui/download/GuiDownloadPicturesLQ.java @@ -61,7 +61,11 @@ public class GuiDownloadPicturesLQ extends GuiDownloader { for (final IPaperCard c : CardDb.instance().getUniqueCards()) { CardRules cardRules = c.getRules(); - this.createDLObjects(cardRules.getPictureUrl(), cardRules.getMainPart().getName()); + if (cardRules != null && cardRules.getSplitType() == CardSplitType.Split && cardRules.getOtherPart() != null) { + this.createDLObjects(cardRules.getPictureUrl(), String.format("%s%s", cardRules.getMainPart().getName(), cardRules.getOtherPart().getName())); + } else { + this.createDLObjects(cardRules.getPictureUrl(), cardRules.getMainPart().getName()); + } ICardCharacteristics secondSide = cardRules.getOtherPart(); if (secondSide != null && cardRules.getSplitType() == CardSplitType.Transform) { diff --git a/src/main/java/forge/gui/home/StartButton.java b/src/main/java/forge/gui/home/StartButton.java index 67e4ed4b23e..20302f888ee 100644 --- a/src/main/java/forge/gui/home/StartButton.java +++ b/src/main/java/forge/gui/home/StartButton.java @@ -1,9 +1,12 @@ package forge.gui.home; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.JButton; +import javax.swing.SwingUtilities; import forge.gui.toolbox.FSkin; @@ -30,5 +33,17 @@ public class StartButton extends JButton { setIcon(FSkin.getIcon(FSkin.ButtonImages.IMG_BTN_START_OVER)); } }); + + addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setEnabled(false); + + // ensure the click action can resolve before we allow the button to be clicked again + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { setEnabled(true); } + }); + } + }); } } diff --git a/src/main/java/forge/gui/match/nonsingleton/CField.java b/src/main/java/forge/gui/match/nonsingleton/CField.java index fb6c7bcf443..155794a7e5f 100644 --- a/src/main/java/forge/gui/match/nonsingleton/CField.java +++ b/src/main/java/forge/gui/match/nonsingleton/CField.java @@ -34,6 +34,7 @@ import forge.Command; import forge.Constant; import forge.Constant.Preferences; import forge.Singletons; +import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactoryUtil; import forge.card.spellability.SpellAbility; import forge.control.input.Input; @@ -298,7 +299,7 @@ public class CField implements ICDoc { faceDown.setName("Face Down"); choices2.add(faceDown); } else { - final Card faceDown = Singletons.getModel().getCardFactory().copyCard(crd); + final Card faceDown = CardFactory.copyCard(crd); faceDown.setState(CardCharacteristicName.Original); choices2.add(faceDown); } diff --git a/src/main/java/forge/gui/toolbox/CardFaceSymbols.java b/src/main/java/forge/gui/toolbox/CardFaceSymbols.java index 7734d2d0906..4bfe0b07a35 100644 --- a/src/main/java/forge/gui/toolbox/CardFaceSymbols.java +++ b/src/main/java/forge/gui/toolbox/CardFaceSymbols.java @@ -221,6 +221,7 @@ public class CardFaceSymbols { g.drawImage(image, x, y, null); } + /** *

* getWidth. @@ -239,4 +240,8 @@ public class CardFaceSymbols { //System.out.println(String.format("%d for %s", width, manaCost.toString())); return width * 14; } + + public static int getHeight() { + return 14; + } } diff --git a/src/main/java/forge/item/CardDb.java b/src/main/java/forge/item/CardDb.java index b3e058d4cc8..54223c1af35 100644 --- a/src/main/java/forge/item/CardDb.java +++ b/src/main/java/forge/item/CardDb.java @@ -20,7 +20,6 @@ package forge.item; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -80,7 +79,7 @@ public final class CardDb { * @param list * the new up */ - public static void setup(final Iterator list) { + public static void setup(final Iterable list) { if (CardDb.commonCards != null) { throw new RuntimeException("CardDb has already been initialized, don't do it twice please"); } @@ -425,9 +424,9 @@ public final class CardDb { uniqueSpecialCards.put(cardName, lastAdded); } - CardSorter(final Iterator parser) { - while (parser.hasNext()) { - this.addNewCard(parser.next()); + CardSorter(final Iterable parser) { + for (CardRules cr : parser) { + this.addNewCard(cr); } } } diff --git a/src/main/java/forge/item/CardPrinted.java b/src/main/java/forge/item/CardPrinted.java index 06e2b43d810..7f8d5dcdc56 100644 --- a/src/main/java/forge/item/CardPrinted.java +++ b/src/main/java/forge/item/CardPrinted.java @@ -27,6 +27,7 @@ import forge.CardUtil; import forge.Singletons; import forge.card.CardRarity; import forge.card.CardRules; +import forge.card.cardfactory.CardFactory; import forge.game.player.Player; @@ -295,7 +296,7 @@ public final class CardPrinted implements Comparable, InventoryItemF */ @Override public Card toForgeCard(Player owner) { - final Card c = Singletons.getModel().getCardFactory().getCard(this, owner); + final Card c = CardFactory.getCard(this, owner); return c; } diff --git a/src/main/java/forge/item/CardToken.java b/src/main/java/forge/item/CardToken.java index 80aa2399c6e..0dcb5098c8d 100644 --- a/src/main/java/forge/item/CardToken.java +++ b/src/main/java/forge/item/CardToken.java @@ -1,9 +1,9 @@ package forge.item; import forge.Card; -import forge.Singletons; import forge.card.CardRarity; import forge.card.CardRules; +import forge.card.cardfactory.CardFactory; import forge.game.player.Player; public class CardToken implements InventoryItemFromSet, IPaperCard { @@ -40,7 +40,7 @@ public class CardToken implements InventoryItemFromSet, IPaperCard { @Override public Card toForgeCard(Player owner) { - final Card c = Singletons.getModel().getCardFactory().getCard(this, owner); + final Card c = CardFactory.getCard(this, owner); return c; } diff --git a/src/main/java/forge/model/FModel.java b/src/main/java/forge/model/FModel.java index f2de6f9a6ff..cf590051657 100644 --- a/src/main/java/forge/model/FModel.java +++ b/src/main/java/forge/model/FModel.java @@ -32,8 +32,9 @@ import forge.card.CardBlock; import forge.card.EditionCollection; import forge.card.FatPackData; import forge.card.FormatCollection; -import forge.card.cardfactory.CardFactory; +import forge.card.cardfactory.CardStorageReader; import forge.deck.CardCollections; +import forge.error.BugReporter; import forge.error.ExceptionHandler; import forge.game.GameState; import forge.game.GameType; @@ -41,8 +42,11 @@ import forge.game.MatchController; import forge.game.limited.GauntletMini; import forge.game.player.LobbyPlayer; import forge.gauntlet.GauntletData; +import forge.gui.GuiUtils; +import forge.item.CardDb; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; +import forge.properties.NewConstants; import forge.quest.QuestController; import forge.quest.QuestWorld; import forge.quest.data.QuestPreferences; @@ -74,7 +78,6 @@ public enum FModel { private GauntletData gauntletData; private GauntletMini gauntlet; - private final CardFactory cardFactory; private final QuestController quest; private final CardCollections decks; @@ -144,7 +147,16 @@ public enum FModel { this.loadDynamicGamedata(); // Loads all cards (using progress bar). - this.cardFactory = new CardFactory(); + GuiUtils.checkEDT("CardFactory$constructor", false); + final CardStorageReader reader = new CardStorageReader(NewConstants.CARD_DATA_DIR.defaultLoc, true); + try { + // this fills in our map of card names to Card instances. + CardDb.setup(reader.loadCards()); + + } catch (final Exception ex) { + BugReporter.reportException(ex); + } + this.decks = new CardCollections(); this.quest = new QuestController(); } @@ -380,14 +392,6 @@ public enum FModel { return gameState; } - /** - * TODO: Write javadoc for this method. - * @return - */ - public CardFactory getCardFactory() { - return cardFactory; - } - public GauntletMini getGauntletMini() { if (gauntlet == null) { diff --git a/src/main/java/forge/quest/QuestUtilUnlockSets.java b/src/main/java/forge/quest/QuestUtilUnlockSets.java index 5d070330d82..411ce99b1e5 100644 --- a/src/main/java/forge/quest/QuestUtilUnlockSets.java +++ b/src/main/java/forge/quest/QuestUtilUnlockSets.java @@ -46,6 +46,7 @@ import forge.util.storage.IStorageView; * */ public class QuestUtilUnlockSets { + private static int UNLOCK_COST = 4000; /** * Consider unlocking a new expansion in limited quest format. @@ -66,10 +67,10 @@ public class QuestUtilUnlockSets { final List> setPrices = new ArrayList>(); for (CardEdition ed : getUnlockableEditions(qData)) { - int price = 7500; + int price = UNLOCK_COST; if (mapPrices.containsKey(ed.getName() + " Booster Pack")) { price = Math.max(new Double(60 * Math.pow(Math.sqrt(mapPrices.get(ed.getName() - + " Booster Pack")), 1.65)).intValue(), 7500); + + " Booster Pack")), 1.65)).intValue(), UNLOCK_COST); } setPrices.add(ImmutablePair.of(ed, price)); } diff --git a/src/main/java/forge/view/arcane/CardPanel.java b/src/main/java/forge/view/arcane/CardPanel.java index cbbbe3dcb32..06d5d0c23ec 100644 --- a/src/main/java/forge/view/arcane/CardPanel.java +++ b/src/main/java/forge/view/arcane/CardPanel.java @@ -36,10 +36,13 @@ import javax.swing.JRootPane; import javax.swing.SwingUtilities; import forge.Card; +import forge.CardCharacteristicName; import forge.CounterType; import forge.ImageCache; import forge.Singletons; import forge.card.CardEdition; +import forge.card.CardSplitType; +import forge.card.mana.ManaCost; import forge.gui.CardContainer; import forge.gui.toolbox.CardFaceSymbols; import forge.properties.ForgePreferences.FPref; @@ -351,6 +354,17 @@ public class CardPanel extends JPanel implements CardContainer { } } + /** + * TODO: Write javadoc for this method. + * @param g + * @param manaCost + */ + private void drawManaCost(final Graphics g, ManaCost cost, int deltaY ) { + int width = CardFaceSymbols.getWidth(cost); + int height = CardFaceSymbols.getHeight(); + CardFaceSymbols.draw(g, cost, (this.cardXOffset + (this.cardWidth / 2)) - (width / 2), deltaY + this.cardYOffset + (this.cardHeight / 2) - height/2); + } + /** {@inheritDoc} */ @Override protected final void paintChildren(final Graphics g) { @@ -361,10 +375,16 @@ public class CardPanel extends JPanel implements CardContainer { } if (this.showCastingCost) { - int width = CardFaceSymbols.getWidth(this.getGameCard().getManaCost()); if (this.cardWidth < 200) { - CardFaceSymbols.draw(g, this.getGameCard().getManaCost(), (this.cardXOffset + (this.cardWidth / 2)) - - (width / 2), this.cardYOffset + (this.cardHeight / 2)); + Card gameCard = this.getGameCard(); + boolean showSplitMana = gameCard.getRules() != null && gameCard.getRules().getSplitType() == CardSplitType.Split && gameCard.getCurState() == CardCharacteristicName.Original; + if ( !showSplitMana ) { + drawManaCost(g, gameCard.getManaCost(), 0); + } else { + drawManaCost(g, gameCard.getRules().getMainPart().getManaCost(), +12); + drawManaCost(g, gameCard.getRules().getOtherPart().getManaCost(), -12); + } + } } diff --git a/src/test/java/forge/deck/generate/Generate2ColorDeckTest.java b/src/test/java/forge/deck/generate/Generate2ColorDeckTest.java index 713faff083c..47e5a873bf6 100644 --- a/src/test/java/forge/deck/generate/Generate2ColorDeckTest.java +++ b/src/test/java/forge/deck/generate/Generate2ColorDeckTest.java @@ -18,7 +18,7 @@ public class Generate2ColorDeckTest { @Test(enabled = false) public void generate2ColorDeckTest1() { final Generate2ColorDeck gen = new Generate2ColorDeck("white", "blue"); - final ItemPoolView cardList = gen.get2ColorDeck(60, null); + final ItemPoolView cardList = gen.getDeck(60, null); Assert.assertNotNull(cardList); } } diff --git a/src/test/java/forge/deck/generate/Generate3ColorDeckTest.java b/src/test/java/forge/deck/generate/Generate3ColorDeckTest.java index 7d33f05b731..77c70046b1e 100644 --- a/src/test/java/forge/deck/generate/Generate3ColorDeckTest.java +++ b/src/test/java/forge/deck/generate/Generate3ColorDeckTest.java @@ -18,7 +18,7 @@ public class Generate3ColorDeckTest { @Test(timeOut = 1000, enabled = false) public void generate3ColorDeckTest1() { final Generate3ColorDeck gen = new Generate3ColorDeck("white", "blue", "black"); - final ItemPoolView cardList = gen.get3ColorDeck(60, null); + final ItemPoolView cardList = gen.getDeck(60, null); Assert.assertNotNull(cardList); } } diff --git a/src/test/java/forge/deck/generate/Generate5ColorDeckTest.java b/src/test/java/forge/deck/generate/Generate5ColorDeckTest.java index 1e2a0708225..65179d3cd36 100644 --- a/src/test/java/forge/deck/generate/Generate5ColorDeckTest.java +++ b/src/test/java/forge/deck/generate/Generate5ColorDeckTest.java @@ -18,7 +18,7 @@ public class Generate5ColorDeckTest { @Test(timeOut = 1000, enabled = false) public void generate5ColorDeckTest1() { final Generate5ColorDeck gen = new Generate5ColorDeck(); - final ItemPoolView cardList = gen.get5ColorDeck(60, null); + final ItemPoolView cardList = gen.getDeck(60, null); Assert.assertNotNull(cardList); } }