merge latest trunk

This commit is contained in:
myk
2013-03-08 03:13:43 +00:00
79 changed files with 1019 additions and 504 deletions

20
.gitattributes vendored
View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -1631,6 +1631,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public final ManaCost getManaCost() {
return this.getCharacteristics().getManaCost();
}
/**
* <p>
@@ -9194,7 +9195,7 @@ public class Card extends GameEntity implements Comparable<Card> {
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;
}

View File

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

View File

@@ -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<Card> 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<Card> 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);

View File

@@ -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<Card> 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;
}

View File

@@ -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++) {

View File

@@ -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());

View File

@@ -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

View File

@@ -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());

View File

@@ -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<Card> battlefield = p.getCardsIn(ZoneType.Battlefield);
List<Card> 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()));

View File

@@ -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;
/**
* <p>
@@ -65,33 +63,6 @@ import forge.properties.NewConstants;
* @version $Id$
*/
public class CardFactory {
/**
* <p>
* Constructor for CardFactory.
* </p>
*
* @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<CardRules> listCardRules = reader.loadCards();
CardDb.setup(listCardRules.iterator());
} catch (final Exception ex) {
BugReporter.reportException(ex);
}
} // constructor
/**
* <p>
* 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 {
}
}
/**
* <p>
* Copies stats like power, toughness, etc.
* </p>
*
* @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

View File

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

View File

@@ -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);
}
/**
* <p>
* Copies stats like power, toughness, etc.
* </p>
*
* @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);

View File

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

View File

@@ -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();

View File

@@ -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

View File

@@ -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<CardPrinted> 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<CardPrinted> 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.",

View File

@@ -76,7 +76,7 @@ public class Generate2ColorDeck extends GenerateColoredDeckBase {
}
public final ItemPoolView<CardPrinted> get2ColorDeck(final int size, final PlayerType pt) {
public final ItemPoolView<CardPrinted> getDeck(final int size, final PlayerType pt) {
addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt);
// Add lands

View File

@@ -75,7 +75,7 @@ public class Generate3ColorDeck extends GenerateColoredDeckBase {
* the pt
* @return a {@link forge.CardList} object.
*/
public final ItemPoolView<CardPrinted> get3ColorDeck(final int size, final PlayerType pt) {
public final ItemPoolView<CardPrinted> getDeck(final int size, final PlayerType pt) {
addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt);
// Add lands

View File

@@ -65,7 +65,7 @@ public class Generate5ColorDeck extends GenerateColoredDeckBase {
* a PlayerType
* @return a {@link forge.CardList} object.
*/
public final ItemPoolView<CardPrinted> get5ColorDeck(final int size, final PlayerType pt) {
public final ItemPoolView<CardPrinted> getDeck(final int size, final PlayerType pt) {
addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt);
// Add lands

View File

@@ -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<CardPrinted> getDeck(final int size, final PlayerType pt) {
return null; // all but theme deck do override this method
}
protected void addSome(int cnt, List<CardPrinted> source) {
for (int i = 0; i < cnt; i++) {

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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;
/**
* <p>
* Generate2ColorDeck class.
* </p>
*
* @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<FilterCMC> 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)
/**
* <p>
* Constructor for Generate2ColorDeck.
* </p>
*
* @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<CardPrinted> 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;
}
}

View File

@@ -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);

View File

@@ -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<Card> 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<Card> choices = new ArrayList<Card>();
for (final Card c : untappedCreats) {
choices.add(c);
}
choices.addAll(untappedCreats);
ArrayList<String> usableColors = new ArrayList<String>();
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

View File

@@ -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<Card> lib = p.getCardsIn(ZoneType.Library);
Predicate<Card> 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 <handsize> 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<Player, List<Card>> playerLibraries) {
Map<LobbyPlayer, PlayerStartConditions> players = match.getPlayers();
Map<Player, PlayerStartConditions> playersConditions = new HashMap<Player, PlayerStartConditions>();
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<Player, PlayerStartConditions> 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<Card> 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 <handsize> 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<Player, List<Card>> playerLibraries) {
Map<LobbyPlayer, PlayerStartConditions> players = match.getPlayers();
Map<Player, PlayerStartConditions> playersConditions = new HashMap<Player, PlayerStartConditions>();
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<Player, PlayerStartConditions> 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<Card> 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<Card> lib = p.getCardsIn(ZoneType.Library);
Predicate<Card> 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 <handsize> 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<CardPrinted> cAnteRemoved) {
StringBuilder sb = new StringBuilder(firstLine);
int i = 0;

View File

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

View File

@@ -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<Card> orderedBlockers = null;
if (player.isHuman()) {
GuiUtils.setPanelSelection(attacker);
List<Card> ordered = GuiChoose.order("Choose Blocking Order", "Damaged First", 0, blockers, null, attacker);
orderedBlockers = new ArrayList<Card>();
for (Object o : ordered) {
orderedBlockers.add((Card) o);
}
}
else {
orderedBlockers = ComputerUtilBlock.orderBlockers(attacker, blockers);
}
combat.setBlockerList(attacker, orderedBlockers);
List<Card> orderedBlockers = player.getController().orderBlockers(attacker, blockers);
combat.setBlockerList(attacker, orderedBlockers);
}
CombatUtil.showCombat();
// Refresh Combat Panel
@@ -459,19 +444,7 @@ public class CombatUtil {
continue;
}
List<Card> orderedAttacker = null;
if (blocker.getController().isHuman()) {
GuiUtils.setPanelSelection(blocker);
List<Card> ordered = GuiChoose.order("Choose Blocking Order", "Damaged First", 0, attackers, null, blocker);
orderedAttacker = new ArrayList<Card>();
for (Object o : ordered) {
orderedAttacker.add((Card) o);
}
}
else {
orderedAttacker = ComputerUtilBlock.orderAttackers(blocker, attackers);
}
List<Card> orderedAttacker = blocker.getController().getController().orderAttackers(blocker, attackers);
combat.setAttackersBlockedByList(blocker, orderedAttacker);
}
CombatUtil.showCombat();

View File

@@ -52,9 +52,9 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
/** Constant <code>serialVersionUID=5207222278370963197L</code>. */
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<ExtraTurn> extraTurns = new Stack<ExtraTurn>();
private final transient Map<PhaseType, Stack<PhaseType>> extraPhases = new HashMap<PhaseType, Stack<PhaseType>>();
@@ -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);

View File

@@ -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"),

View File

@@ -114,28 +114,7 @@ public class PhaseUtil {
game.getPhaseHandler().setPlayersPriorityPermission(false);
}
/**
* <p>
* skipDraw.
* </p>
*
* @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 ***********

View File

@@ -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);

View File

@@ -3241,6 +3241,20 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
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.
*

View File

@@ -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<Card> orderBlockers(Card attacker, List<Card> blockers);
public abstract List<Card> orderAttackers(Card blocker, List<Card> attackers);
}

View File

@@ -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<Card> orderBlockers(Card attacker, List<Card> blockers) {
return ComputerUtilBlock.orderBlockers(attacker, blockers);
}
@Override
public List<Card> orderAttackers(Card blocker, List<Card> attackers) {
return ComputerUtilBlock.orderAttackers(blocker, attackers);
}
}

View File

@@ -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<Card> orderBlockers(Card attacker, List<Card> blockers) {
GuiUtils.setPanelSelection(attacker);
return GuiChoose.order("Choose Blocking Order", "Damaged First", 0, blockers, null, attacker);
}
@Override
public List<Card> orderAttackers(Card blocker, List<Card> attackers) {
GuiUtils.setPanelSelection(blocker);
return GuiChoose.order("Choose Blocking Order", "Damaged First", 0, attackers, null, blocker);
}
}

View File

@@ -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);
}
}
};

View File

@@ -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));

View File

@@ -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<Player> 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;
}

View File

@@ -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

View File

@@ -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:
}

View File

@@ -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<ManaCostShard> 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<ManaCostShard> shards = value.getShards();
final int cntGlyphs = hasGeneric ? shards.size() + 1 : shards.size();
final float offsetIfNoSpace = cntGlyphs > 1 ? (cellWidth - padding - elemtWidth) / (cntGlyphs - 1f)
: elemtWidth + elemtGap;

View File

@@ -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<Entry<InventoryItem, Integer>, Object> FN_COST_GET = new Function<Entry<InventoryItem, Integer>, Object>() {
@Override
public Object apply(final Entry<InventoryItem, Integer> from) {
return SColumnUtil.toManaCost(from.getKey());
return SColumnUtil.toCardRules(from.getKey());
}
};

View File

@@ -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) {

View File

@@ -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); }
});
}
});
}
}

View File

@@ -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);
}

View File

@@ -221,6 +221,7 @@ public class CardFaceSymbols {
g.drawImage(image, x, y, null);
}
/**
* <p>
* 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;
}
}

View File

@@ -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<CardRules> list) {
public static void setup(final Iterable<CardRules> 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<CardRules> parser) {
while (parser.hasNext()) {
this.addNewCard(parser.next());
CardSorter(final Iterable<CardRules> parser) {
for (CardRules cr : parser) {
this.addNewCard(cr);
}
}
}

View File

@@ -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<IPaperCard>, 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;
}

View File

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

View File

@@ -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) {

View File

@@ -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<ImmutablePair<CardEdition, Integer>> setPrices = new ArrayList<ImmutablePair<CardEdition, Integer>>();
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));
}

View File

@@ -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);
}
}
}

View File

@@ -18,7 +18,7 @@ public class Generate2ColorDeckTest {
@Test(enabled = false)
public void generate2ColorDeckTest1() {
final Generate2ColorDeck gen = new Generate2ColorDeck("white", "blue");
final ItemPoolView<CardPrinted> cardList = gen.get2ColorDeck(60, null);
final ItemPoolView<CardPrinted> cardList = gen.getDeck(60, null);
Assert.assertNotNull(cardList);
}
}

View File

@@ -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<CardPrinted> cardList = gen.get3ColorDeck(60, null);
final ItemPoolView<CardPrinted> cardList = gen.getDeck(60, null);
Assert.assertNotNull(cardList);
}
}

View File

@@ -18,7 +18,7 @@ public class Generate5ColorDeckTest {
@Test(timeOut = 1000, enabled = false)
public void generate5ColorDeckTest1() {
final Generate5ColorDeck gen = new Generate5ColorDeck();
final ItemPoolView<CardPrinted> cardList = gen.get5ColorDeck(60, null);
final ItemPoolView<CardPrinted> cardList = gen.getDeck(60, null);
Assert.assertNotNull(cardList);
}
}