diff --git a/.gitattributes b/.gitattributes
index e4eb5dd4fcb..8bd64727cf9 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -9774,6 +9774,7 @@ src/main/java/forge/gui/ListChooser.java svneol=native#text/plain
src/main/java/forge/gui/MultiLineLabel.java svneol=native#text/plain
src/main/java/forge/gui/MultiLineLabelUI.java svneol=native#text/plain
src/main/java/forge/gui/MultiPhaseProgressMonitorWithETA.java svneol=native#text/plain
+src/main/java/forge/gui/SelectablePanel.java -text
src/main/java/forge/gui/deckeditor/CardPanelBase.java -text
src/main/java/forge/gui/deckeditor/CardPanelHeavy.java -text
src/main/java/forge/gui/deckeditor/CardPanelLite.java -text
@@ -9822,6 +9823,10 @@ src/main/java/forge/properties/NewConstants.java svneol=native#text/plain
src/main/java/forge/properties/Preferences.java svneol=native#text/plain
src/main/java/forge/properties/SavePreferencesListener.java svneol=native#text/plain
src/main/java/forge/properties/package-info.java svneol=native#text/plain
+src/main/java/forge/quest/data/DeckSingleBattle.java svneol=native#text/plain
+src/main/java/forge/quest/data/DeckSingleQuest.java svneol=native#text/plain
+src/main/java/forge/quest/data/ManagerBattle.java svneol=native#text/plain
+src/main/java/forge/quest/data/ManagerQuest.java -text
src/main/java/forge/quest/data/QuestBattleManager.java svneol=native#text/plain
src/main/java/forge/quest/data/QuestBoosterPack.java svneol=native#text/plain
src/main/java/forge/quest/data/QuestData.java svneol=native#text/plain
@@ -9853,8 +9858,11 @@ src/main/java/forge/quest/data/pet/QuestPetManager.java svneol=native#text/plain
src/main/java/forge/quest/data/pet/QuestPetPlant.java svneol=native#text/plain
src/main/java/forge/quest/data/pet/QuestPetWolf.java svneol=native#text/plain
src/main/java/forge/quest/data/pet/package-info.java svneol=native#text/plain
+src/main/java/forge/quest/gui/PanelSingleBattle.java -text
+src/main/java/forge/quest/gui/PanelSingleQuest.java -text
src/main/java/forge/quest/gui/QuestAbstractPanel.java svneol=native#text/plain
src/main/java/forge/quest/gui/QuestFrame.java svneol=native#text/plain
+src/main/java/forge/quest/gui/QuestMainPanel.java svneol=native#text/plain
src/main/java/forge/quest/gui/QuestOptions.java svneol=native#text/plain
src/main/java/forge/quest/gui/bazaar/QuestBazaarItem.java svneol=native#text/plain
src/main/java/forge/quest/gui/bazaar/QuestBazaarPanel.java svneol=native#text/plain
@@ -9923,7 +9931,6 @@ src/test/java/forge/GuiWinLoseTest.java svneol=native#text/plain
src/test/java/forge/PanelTest.java svneol=native#text/plain
src/test/java/forge/PhaseTest.java svneol=native#text/plain
src/test/java/forge/ReadBoosterPackTest.java svneol=native#text/plain
-src/test/java/forge/ReadQuestAssignmentTest.java svneol=native#text/plain
src/test/java/forge/RunTest.java svneol=native#text/plain
src/test/java/forge/TinyTest.java svneol=native#text/plain
src/test/java/forge/card/cardFactory/CardFactoryTest.java svneol=native#text/plain
diff --git a/res/quest/decks/quest1.dck b/res/quest/decks/quest1.dck
index 3bd1e8985c3..2595ae05aca 100644
--- a/res/quest/decks/quest1.dck
+++ b/res/quest/decks/quest1.dck
@@ -1,6 +1,17 @@
-quest1
-[general]
-constructed
+[metadata]
+ID=1
+Name=quest1
+DisplayName=Dungeon Crawling (White)
+Difficulty=Medium
+Description=This realm is guarded by a divine entity laying his protecting hand on each living being.
+Repeatable=true
+NumberWinsRequired=20
+CardReward=3 white rares
+CreditsReward=100
+Icon=Dungeon Crawling White.jpg
+AILife=25
+Deck Type=constructed
+Comment=
[main]
24 Plains
1 Abbey Gargoyles
@@ -40,3 +51,6 @@ constructed
1 Retribution of the Meek
1 Sunlance
[sideboard]
+[human_extra_cards]
+[ai_extra_cards]
+1 Divine Presence
\ No newline at end of file
diff --git a/res/quest/decks/quest10.dck b/res/quest/decks/quest10.dck
index a2363fab55e..47c32055eef 100644
--- a/res/quest/decks/quest10.dck
+++ b/res/quest/decks/quest10.dck
@@ -1,6 +1,17 @@
-quest10
-[general]
-constructed
+[metadata]
+ID=10
+Name=quest10
+DisplayName=Zombie Attack!
+Difficulty=Hard
+Description=The village of Haven is getting attacked by the Zombie horde! Protect the village.
+Repeatable=false
+NumberWinsRequired=40
+CardReward=4 black rares
+CreditsReward=200
+Icon=Zombie Attack.jpg
+AILife=30
+Deck Type=constructed
+Comment=
[main]
1 Cadaverous Knight
1 Bog Raiders
@@ -27,3 +38,11 @@ constructed
1 Warpath Ghoul
1 Viscera Dragger
[sideboard]
+[human_extra_cards]
+TOKEN|W|1|1|citizen|creature
+TOKEN|W|1|1|citizen|creature
+TOKEN|W|1|1|citizen|creature
+Wall of Spears
+[ai_extra_cards]
+Scathe Zombies
+Mass of Ghouls
\ No newline at end of file
diff --git a/res/quest/decks/quest11.dck b/res/quest/decks/quest11.dck
index 2565a2b4cd9..25c71bc3148 100644
--- a/res/quest/decks/quest11.dck
+++ b/res/quest/decks/quest11.dck
@@ -1,49 +1,64 @@
-quest11
-[general]
-constructed
-[main]
-1 Isamaru, Hound of Konda
-4 Plateau
-1 Radiant, Archangel
-4 Taiga
-1 Zuo Ci, the Mocking Sage
-1 Mox Emerald
-1 Rakka Mar
-1 Concordant Crossroads
-1 Meng Huo, Barbarian King
-1 Jeska, Warrior Adept
-2 Loyal Retainers
-1 Jacques le Vert
-1 Tolsimir Wolfblood
-1 Squee, Goblin Nabob
-1 Kaysa
-1 Earthcraft
-1 Mox Pearl
-1 Mikokoro, Center of the Sea
-1 Eladamri, Lord of Leaves
-1 Eladamri's Call
-1 Mirri, Cat Warrior
-1 Exploration
-4 Savannah
-1 Gaddock Teeg
-1 Honden of Cleansing Fire
-1 Pianna, Nomad Captain
-1 Azusa, Lost but Seeking
-3 Plains
-1 Captain Sisay
-1 Okina, Temple to the Grandfathers
-1 Honden of Infinite Rage
-1 Reki, the History of Kamigawa
-1 Goblin Assault
-1 Umezawa's Jitte
-3 Forest
-1 Fecundity
-1 Mox Ruby
-1 Tuktuk the Explorer
-1 Zuberi, Golden Feather
-1 Fastbond
-1 Honden of Life's Web
-1 Kongming, "Sleeping Dragon"
-3 Mountain
-1 Tuknir Deathlock
-[sideboard]
+[metadata]
+ID=11
+Name=quest11
+DisplayName=The King's Contest
+Difficulty=Hard
+Description=The king is holding a contest. You are invited to participate and compete against other legends of this era.
+Repeatable=false
+NumberWinsRequired=40
+CardReward=3 random rares
+CreditsReward=150
+Icon=The Kings Contest.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+1 Isamaru, Hound of Konda
+4 Plateau
+1 Radiant, Archangel
+4 Taiga
+1 Zuo Ci, the Mocking Sage
+1 Mox Emerald
+1 Rakka Mar
+1 Concordant Crossroads
+1 Meng Huo, Barbarian King
+1 Jeska, Warrior Adept
+2 Loyal Retainers
+1 Jacques le Vert
+1 Tolsimir Wolfblood
+1 Squee, Goblin Nabob
+1 Kaysa
+1 Earthcraft
+1 Mox Pearl
+1 Mikokoro, Center of the Sea
+1 Eladamri, Lord of Leaves
+1 Eladamri's Call
+1 Mirri, Cat Warrior
+1 Exploration
+4 Savannah
+1 Gaddock Teeg
+1 Honden of Cleansing Fire
+1 Pianna, Nomad Captain
+1 Azusa, Lost but Seeking
+3 Plains
+1 Captain Sisay
+1 Okina, Temple to the Grandfathers
+1 Honden of Infinite Rage
+1 Reki, the History of Kamigawa
+1 Goblin Assault
+1 Umezawa's Jitte
+3 Forest
+1 Fecundity
+1 Mox Ruby
+1 Tuktuk the Explorer
+1 Zuberi, Golden Feather
+1 Fastbond
+1 Honden of Life's Web
+1 Kongming, "Sleeping Dragon"
+3 Mountain
+1 Tuknir Deathlock
+[sideboard]
+[human_extra_cards]
+Seal of Cleansing
+[ai_extra_cards]
+Loyal Retainers
\ No newline at end of file
diff --git a/res/quest/decks/quest12.dck b/res/quest/decks/quest12.dck
index 4c0da6a5ac3..7cfecdba3e9 100644
--- a/res/quest/decks/quest12.dck
+++ b/res/quest/decks/quest12.dck
@@ -1,26 +1,43 @@
-quest12
-[general]
-constructed
-[main]
-2 Birds of Paradise
-4 Knight of the Reliquary
-2 Elspeth, Knight-Errant
-2 Eternal Witness
-1 Progenitus
-5 Plains
-3 Natural Order
-2 Garruk Wildspeaker
-1 Umezawa's Jitte
-7 Forest
-2 Stoneforge Mystic
-4 Swords to Plowshares
-1 Dryad Arbor
-4 Qasali Pridemage
-4 Tarmogoyf
-1 Loxodon Warhammer
-2 Path to Exile
-4 Noble Hierarch
-1 Quietus Spike
-4 Savannah
-4 Wasteland
-[sideboard]
+[metadata]
+ID=12
+Name=quest12
+DisplayName=Barroom Brawl
+Difficulty=Hard
+Description=A drunken giant of a man takes a swing at you, your brew spills and a fight breaks out.
+Repeatable=false
+NumberWinsRequired=64
+CardReward=4 random rares
+CreditsReward=250
+Icon=Barroom Brawl.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+2 Birds of Paradise
+4 Knight of the Reliquary
+2 Elspeth, Knight-Errant
+2 Eternal Witness
+1 Progenitus
+5 Plains
+3 Natural Order
+2 Garruk Wildspeaker
+1 Umezawa's Jitte
+7 Forest
+2 Stoneforge Mystic
+4 Swords to Plowshares
+1 Dryad Arbor
+4 Qasali Pridemage
+4 Tarmogoyf
+1 Loxodon Warhammer
+2 Path to Exile
+4 Noble Hierarch
+1 Quietus Spike
+4 Savannah
+4 Wasteland
+[sideboard]
+[human_extra_cards]
+TOKEN|W|1|1|soldier ally|creature|soldier|ally
+TOKEN|W|1|1|soldier ally|creature|soldier|ally
+TOKEN|W|1|1|soldier ally|creature|soldier|ally
+[ai_extra_cards]
+Lowland Giant
\ No newline at end of file
diff --git a/res/quest/decks/quest13.dck b/res/quest/decks/quest13.dck
index fdc2ba1a02d..4f755357b46 100644
--- a/res/quest/decks/quest13.dck
+++ b/res/quest/decks/quest13.dck
@@ -1,27 +1,42 @@
-quest13
-[general]
-constructed
-[main]
-4 Underground Sea
-4 Spreading Seas
-4 Remove Soul
-1 Avatar of Fury
-1 Mox Sapphire
-2 Damnation
-4 Crumbling Necropolis
-2 Island
-1 Ancestral Recall
-4 Badlands
-4 Fire-Field Ogre
-4 Kargan Dragonlord
-4 Lightning Bolt
-2 Nicol Bolas
-1 Mox Ruby
-4 Sedraxis Specter
-4 Volcanic Island
-1 Time Walk
-4 Counterspell
-2 Cruel Ultimatum
-1 Mox Jet
-2 Mountain
-[sideboard]
+[metadata]
+ID=13
+Name=quest13
+DisplayName=The Court Jester
+Difficulty=Hard
+Description=The Court Jester pulls a prank on you. Will you laugh it off or pay him back?
+Repeatable=false
+NumberWinsRequired=52
+CardReward=4 multi-colored rares
+CreditsReward=300
+Icon=The Court Jester.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+4 Underground Sea
+4 Spreading Seas
+4 Remove Soul
+1 Avatar of Fury
+1 Mox Sapphire
+2 Damnation
+4 Crumbling Necropolis
+2 Island
+1 Ancestral Recall
+4 Badlands
+4 Fire-Field Ogre
+4 Kargan Dragonlord
+4 Lightning Bolt
+2 Nicol Bolas
+1 Mox Ruby
+4 Sedraxis Specter
+4 Volcanic Island
+1 Time Walk
+4 Counterspell
+2 Cruel Ultimatum
+1 Mox Jet
+2 Mountain
+[sideboard]
+[human_extra_cards]
+Sensei's Divining Top
+[ai_extra_cards]
+Teferi's Puzzle Box
\ No newline at end of file
diff --git a/res/quest/decks/quest14.dck b/res/quest/decks/quest14.dck
index 7734d6457dc..622de5daf5d 100644
--- a/res/quest/decks/quest14.dck
+++ b/res/quest/decks/quest14.dck
@@ -1,6 +1,17 @@
-quest14
-[general]
-constructed
+[metadata]
+ID=14
+Name=quest14
+DisplayName=Ancient Battlefield
+Difficulty=Hard
+Description=You visit an ancient battlefield at midnight. It is overgrown and dark. You trip on a root, utter a curse and wish that you could see.
+Repeatable=false
+NumberWinsRequired=64
+CardReward=4 random rares
+CreditsReward=250
+Icon=Ancient Battlefield.jpg
+AILife=30
+Deck Type=constructed
+Comment=
[main]
4 Birds of Paradise
1 Plague Wind
@@ -21,3 +32,9 @@ constructed
1 Mox Jet
4 Essence Warden
[sideboard]
+[human_extra_cards]
+Glasses of Urza
+Blight Sickle
+[ai_extra_cards]
+Bad Moon
+Wall of Brambles
\ No newline at end of file
diff --git a/res/quest/decks/quest15.dck b/res/quest/decks/quest15.dck
index 9a34aac4b1f..234a89f183f 100644
--- a/res/quest/decks/quest15.dck
+++ b/res/quest/decks/quest15.dck
@@ -1,24 +1,42 @@
-quest15
-[general]
-constructed
-[main]
-4 Plateau
-4 Druid of the Anima
-4 Taiga
-1 Mox Emerald
-3 Temple Garden
-3 Glorious Anthem
-3 Stomping Ground
-4 Elvish Champion
-4 Lightning Bolt
-1 Mox Ruby
-1 Mox Pearl
-3 Gaea's Anthem
-4 Blaze
-3 Fireball
-4 Savannah
-3 Beastmaster Ascension
-3 Keen-Eyed Archers
-4 Elvish Harbinger
-4 Llanowar Elves
-[sideboard]
+[metadata]
+ID=15
+Name=quest15
+DisplayName=Don't Play With Matches
+Difficulty=Hard
+Description=The goblins are battling the elves. Those pesky elves, will they ever learn. Do not play with fire!
+Repeatable=false
+NumberWinsRequired=52
+CardReward=4 red rares
+CreditsReward=200
+Icon=Dont Play With Matches.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+4 Plateau
+4 Druid of the Anima
+4 Taiga
+1 Mox Emerald
+3 Temple Garden
+3 Glorious Anthem
+3 Stomping Ground
+4 Elvish Champion
+4 Lightning Bolt
+1 Mox Ruby
+1 Mox Pearl
+3 Gaea's Anthem
+4 Blaze
+3 Fireball
+4 Savannah
+3 Beastmaster Ascension
+3 Keen-Eyed Archers
+4 Elvish Harbinger
+4 Llanowar Elves
+[sideboard]
+[human_extra_cards]
+1 Mudbutton Torchrunner
+1 Scuzzback Scrapper
+[ai_extra_cards]
+1 Heedless One
+1 Norwood Archers
+1 Wildslayer Elves
\ No newline at end of file
diff --git a/res/quest/decks/quest16.dck b/res/quest/decks/quest16.dck
index 91382935c9e..3209425a6f7 100644
--- a/res/quest/decks/quest16.dck
+++ b/res/quest/decks/quest16.dck
@@ -1,28 +1,50 @@
-quest16
-[general]
-constructed
-[main]
-1 Proper Burial
-2 Disenchant
-1 Mox Emerald
-4 Sporesower Thallid
-2 Psychotrope Thallid
-5 Plains
-4 Graypelt Refuge
-2 Mycoloth
-4 Sunpetal Grove
-2 Rampant Growth
-1 Enlightened Tutor
-4 Mycologist
-2 Kodama's Reach
-5 Forest
-4 Thallid
-1 Mox Pearl
-1 Desert Twister
-1 Exploration
-4 Pallid Mycoderm
-2 Defense of the Heart
-4 Savannah
-1 Doubling Season
-3 Thelonite Hermit
-[sideboard]
+[metadata]
+ID=16
+Name=quest16
+DisplayName=Mines of Kazum Durl
+Difficulty=Hard
+Description=While exploring a mine with a group of dwarves the mine caves in. You start to dig out and then realize that your group is not alone.
+Repeatable=false
+NumberWinsRequired=52
+CardReward=4 green rares
+CreditsReward=250
+Icon=Mines of Kazum Durl.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+1 Proper Burial
+2 Disenchant
+1 Mox Emerald
+4 Sporesower Thallid
+2 Psychotrope Thallid
+5 Plains
+4 Graypelt Refuge
+2 Mycoloth
+4 Sunpetal Grove
+2 Rampant Growth
+1 Enlightened Tutor
+4 Mycologist
+2 Kodama's Reach
+5 Forest
+4 Thallid
+1 Mox Pearl
+1 Desert Twister
+1 Exploration
+4 Pallid Mycoderm
+2 Defense of the Heart
+4 Savannah
+1 Doubling Season
+3 Thelonite Hermit
+[sideboard]
+[human_extra_cards]
+Dwarven Demolition Team
+Dwarven Pony
+Dwarven Trader
+[ai_extra_cards]
+Wall of Earth
+Wall of Air
+Wall of Ice
+Wall of Light
+Carrion Wall
+Steel Wall
\ No newline at end of file
diff --git a/res/quest/decks/quest17.dck b/res/quest/decks/quest17.dck
index 9befd406764..9e26a1d950f 100644
--- a/res/quest/decks/quest17.dck
+++ b/res/quest/decks/quest17.dck
@@ -1,28 +1,46 @@
-quest17
-[general]
-constructed
-[main]
-4 Terminate
-4 Taiga
-2 Dragonskull Summit
-1 Mox Emerald
-1 Swamp
-4 Putrid Leech
-2 Bituminous Blast
-2 Siege-Gang Commander
-1 Garruk Wildspeaker
-3 Savage Lands
-1 Forest
-4 Badlands
-4 Blightning
-4 Lightning Bolt
-1 Mox Ruby
-4 Maelstrom Pulse
-2 Rootbound Crag
-4 Bayou
-4 Bloodbraid Elf
-2 Sarkhan the Mad
-1 Mox Jet
-4 Sprouting Thrinax
-1 Mountain
-[sideboard]
+[metadata]
+ID=17
+Name=quest17
+DisplayName=House Party
+Difficulty=Hard
+Description=Your friends decide to hold a house party at your home tonight. Later that night uninvited guests show up and disrupt the party.
+Repeatable=false
+NumberWinsRequired=64
+CardReward=4 colorless rares
+CreditsReward=250
+Icon=House Party.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+4 Terminate
+4 Taiga
+2 Dragonskull Summit
+1 Mox Emerald
+1 Swamp
+4 Putrid Leech
+2 Bituminous Blast
+2 Siege-Gang Commander
+1 Garruk Wildspeaker
+3 Savage Lands
+1 Forest
+4 Badlands
+4 Blightning
+4 Lightning Bolt
+1 Mox Ruby
+4 Maelstrom Pulse
+2 Rootbound Crag
+4 Bayou
+4 Bloodbraid Elf
+2 Sarkhan the Mad
+1 Mox Jet
+4 Sprouting Thrinax
+1 Mountain
+[sideboard]
+[human_extra_cards]
+Hopping Automaton
+Honden of Life's Web
+Forbidden Orchard
+[ai_extra_cards]
+Honden of Infinite Rage
+Mikokoro, Center of the Sea
\ No newline at end of file
diff --git a/res/quest/decks/quest18.dck b/res/quest/decks/quest18.dck
index 3a5374e9b0f..8ab89db2a5b 100644
--- a/res/quest/decks/quest18.dck
+++ b/res/quest/decks/quest18.dck
@@ -1,25 +1,45 @@
-quest18
-[general]
-constructed
-[main]
-4 Birds of Paradise
-4 Knight of the Reliquary
-2 Sejiri Steppe
-1 Mox Emerald
-4 Tropical Island
-2 Elspeth, Knight-Errant
-1 Mox Sapphire
-2 Plains
-2 Island
-4 Forest
-4 Sovereigns of Lost Alara
-3 Jace Beleren
-1 Mox Pearl
-4 Dauntless Escort
-4 Tundra
-4 Steppe Lynx
-4 Baneslayer Angel
-4 Noble Hierarch
-4 Savannah
-2 Eldrazi Conscription
-[sideboard]
+[metadata]
+ID=18
+Name=quest18
+DisplayName=Crows in the Field
+Difficulty=Hard
+Description=Crows are eating the seed planted in the local farm fields. The farmers are scared. Those crows are big, you will need some help.
+Repeatable=false
+NumberWinsRequired=64
+CardReward=5 random rares
+CreditsReward=300
+Icon=Crows in the Field.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+4 Birds of Paradise
+4 Knight of the Reliquary
+2 Sejiri Steppe
+1 Mox Emerald
+4 Tropical Island
+2 Elspeth, Knight-Errant
+1 Mox Sapphire
+2 Plains
+2 Island
+4 Forest
+4 Sovereigns of Lost Alara
+3 Jace Beleren
+1 Mox Pearl
+4 Dauntless Escort
+4 Tundra
+4 Steppe Lynx
+4 Baneslayer Angel
+4 Noble Hierarch
+4 Savannah
+2 Eldrazi Conscription
+[sideboard]
+[human_extra_cards]
+Straw Soldiers
+Femeref Archers
+Moonglove Extract
+[ai_extra_cards]
+Defiant Falcon
+Soulcatcher
+Storm Crow
+Hypnotic Specter
\ No newline at end of file
diff --git a/res/quest/decks/quest19.dck b/res/quest/decks/quest19.dck
index cbde1cde189..ad1d425ec45 100644
--- a/res/quest/decks/quest19.dck
+++ b/res/quest/decks/quest19.dck
@@ -1,22 +1,39 @@
-quest19
-[general]
-constructed
-[main]
-4 Birds of Paradise
-3 Armistice
-1 Mox Emerald
-6 Plains
-4 Temple Garden
-4 Utopia Tree
-3 Door to Nothingness
-4 Darksteel Ingot
-3 Harmonize
-1 Enlightened Tutor
-6 Forest
-4 Peacekeeper
-4 Gemhide Sliver
-4 Magus of the Moat
-1 Mox Pearl
-4 Eladamri's Call
-4 Savannah
-[sideboard]
+[metadata]
+ID=19
+Name=quest19
+DisplayName=The Desert Caravan
+Difficulty=Hard
+Description=A caravan is transporting silk across the desert. While setting up camp for the night you are attacked by thieves. Defend yourself.
+Repeatable=false
+NumberWinsRequired=80
+CardReward=5 random rares
+CreditsReward=300
+Icon=The Desert Caravan.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+4 Birds of Paradise
+3 Armistice
+1 Mox Emerald
+6 Plains
+4 Temple Garden
+4 Utopia Tree
+3 Door to Nothingness
+4 Darksteel Ingot
+3 Harmonize
+1 Enlightened Tutor
+6 Forest
+4 Peacekeeper
+4 Gemhide Sliver
+4 Magus of the Moat
+1 Mox Pearl
+4 Eladamri's Call
+4 Savannah
+[sideboard]
+[human_extra_cards]
+Spidersilk Net
+Dromad Purebred
+[ai_extra_cards]
+4 Ambush Party
+Gnat Alley Creeper
diff --git a/res/quest/decks/quest2.dck b/res/quest/decks/quest2.dck
index 841139a6b30..c441c50006c 100644
--- a/res/quest/decks/quest2.dck
+++ b/res/quest/decks/quest2.dck
@@ -1,6 +1,17 @@
-quest2
-[general]
-constructed
+[metadata]
+ID=2
+Name=quest2
+DisplayName=Dungeon Crawling (Blue)
+Difficulty=Medium
+Description=This realm holds knowledge so vast it can overburden the unwary.
+Repeatable=true
+NumberWinsRequired=20
+CardReward=3 blue rares
+CreditsReward=100
+Icon=Dungeon Crawling Blue.jpg
+AILife=25
+Deck Type=constructed
+Comment=
[main]
20 Island
4 Wasteland
@@ -27,3 +38,7 @@ constructed
1 Fugitive Wizard
1 Grayscaled Gharial
[sideboard]
+[human_extra_cards]
+1 Quest for Ancient Secrets
+[ai_extra_cards]
+1 Forced Fruition
\ No newline at end of file
diff --git a/res/quest/decks/quest20.dck b/res/quest/decks/quest20.dck
index d6ddaf27b80..760b796215e 100644
--- a/res/quest/decks/quest20.dck
+++ b/res/quest/decks/quest20.dck
@@ -1,34 +1,53 @@
-quest20
-[general]
-constructed
-[main]
-4 Underground Sea
-4 Tropical Island
-1 Mox Emerald
-4 Oath of Druids
-1 Kozilek, Butcher of Truth
-1 Thirst for Knowledge
-4 Island
-1 Brainstorm
-1 Ancestral Recall
-4 Spell Pierce
-1 Sphinx of the Steel Wind
-2 Duress
-1 Mox Pearl
-1 Iona, Shield of Emeria
-1 Mox Jet
-1 Balance
-1 Ulamog, the Infinite Gyre
-1 Mox Sapphire
-4 Accumulated Knowledge
-1 Tinker
-2 Echoing Truth
-4 Forbidden Orchard
-1 Mox Ruby
-3 Serum Visions
-4 Volcanic Island
-1 Time Walk
-4 Counterspell
-1 Thoughtseize
-1 Emrakul, the Aeons Torn
-[sideboard]
+[metadata]
+ID=20
+Name=quest20
+DisplayName=Blood Oath
+Difficulty=Hard
+Description=A druid saves your life and the two of you take a blood oath. It is now time to fulfill your oath.
+Repeatable=false
+NumberWinsRequired=80
+CardReward=5 colorless rares
+CreditsReward=300
+Icon=Blood Oath.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+4 Underground Sea
+4 Tropical Island
+1 Mox Emerald
+4 Oath of Druids
+1 Kozilek, Butcher of Truth
+1 Thirst for Knowledge
+4 Island
+1 Brainstorm
+1 Ancestral Recall
+4 Spell Pierce
+1 Sphinx of the Steel Wind
+2 Duress
+1 Mox Pearl
+1 Iona, Shield of Emeria
+1 Mox Jet
+1 Balance
+1 Ulamog, the Infinite Gyre
+1 Mox Sapphire
+4 Accumulated Knowledge
+1 Tinker
+2 Echoing Truth
+4 Forbidden Orchard
+1 Mox Ruby
+3 Serum Visions
+4 Volcanic Island
+1 Time Walk
+4 Counterspell
+1 Thoughtseize
+1 Emrakul, the Aeons Torn
+[sideboard]
+[human_extra_cards]
+Counterbalance
+Hatching Plans
+Ley Druid
+[ai_extra_cards]
+Ior Ruin Expedition
+Oversold Cemetery
+Trapjaw Kelpie
\ No newline at end of file
diff --git a/res/quest/decks/quest21.dck b/res/quest/decks/quest21.dck
index 546b00c8bac..ebd53ee54d2 100644
--- a/res/quest/decks/quest21.dck
+++ b/res/quest/decks/quest21.dck
@@ -1,6 +1,17 @@
-quest21
-[general]
-constructed
+[metadata]
+ID=21
+Name=quest21
+DisplayName=Private Domain
+Difficulty=Expert
+Description=During your travels, you accidentally stumble upon the domain of an evil, powerful wizard. A fight to the death ensues.
+Repeatable=false
+NumberWinsRequired=96
+CardReward=6 random rares
+CreditsReward=500
+Icon=Private Domain.jpg
+AILife=50
+Deck Type=constructed
+Comment=
[main]
2 Underground Sea
2 Plateau
@@ -29,3 +40,11 @@ constructed
1 Mountain
2 Wandering Stream
[sideboard]
+[human_extra_cards]
+Strip Mine
+[ai_extra_cards]
+Plains
+Island
+Mountain
+Swamp
+Forest
diff --git a/res/quest/decks/quest22.dck b/res/quest/decks/quest22.dck
index 538293a4152..e33e6b1fca7 100644
--- a/res/quest/decks/quest22.dck
+++ b/res/quest/decks/quest22.dck
@@ -1,6 +1,17 @@
-quest22
-[general]
-constructed
+[metadata]
+ID=22
+Name=quest22
+DisplayName=The Pied Piper
+Difficulty=Hard
+Description=A mysterious man threatens to flood the land with a relentless stream of hungry rats. Cross his plan before it's too late.
+Repeatable=false
+NumberWinsRequired=32
+CardReward=3 random rares
+CreditsReward=150
+Icon=The Pied Piper.jpg
+AILife=30
+Deck Type=constructed
+Comment=
[main]
6 Swamp
4 Underground Sea
@@ -12,3 +23,11 @@ constructed
4 Thrumming Stone
29 Relentless Rats
[sideboard]
+[human_extra_cards]
+1 Volunteer Militia
+1 Land Tax
+1 Elvish Farmer
+1 An-Havva Township
+[ai_extra_cards]
+1 Darksteel Citadel
+1 Relentless Rats
\ No newline at end of file
diff --git a/res/quest/decks/quest3.dck b/res/quest/decks/quest3.dck
index a1245344791..c0d123d1a22 100644
--- a/res/quest/decks/quest3.dck
+++ b/res/quest/decks/quest3.dck
@@ -1,6 +1,17 @@
-quest3
-[general]
-constructed
+[metadata]
+ID=3
+Name=quest3
+DisplayName=Dungeon Crawling (Black)
+Difficulty=Medium
+Description=Guarded by black creatures.
+Repeatable=true
+NumberWinsRequired=20
+CardReward=3 black rares
+CreditsReward=100
+Icon=Dungeon Crawling Black.jpg
+AILife=25
+Deck Type=constructed
+Comment=
[main]
20 Swamp
4 Charcoal Diamond
@@ -27,3 +38,6 @@ constructed
1 Beacon of Unrest
1 Zombify
[sideboard]
+[human_extra_cards]
+[ai_extra_cards]
+Infernal Genesis
\ No newline at end of file
diff --git a/res/quest/decks/quest4.dck b/res/quest/decks/quest4.dck
index 198ca32feef..06a93cc8478 100644
--- a/res/quest/decks/quest4.dck
+++ b/res/quest/decks/quest4.dck
@@ -1,6 +1,17 @@
-quest4
-[general]
-constructed
+[metadata]
+ID=4
+Name=quest4
+DisplayName=Dungeon Crawling (Red)
+Difficulty=Medium
+Description=Guarded by red creatures.
+Repeatable=true
+NumberWinsRequired=20
+CardReward=3 red rares
+CreditsReward=100
+Icon=Dungeon Crawling Red.jpg
+AILife=25
+Deck Type=constructed
+Comment=
[main]
49 Mountain
1 Fury Sliver
@@ -85,3 +96,6 @@ constructed
1 Tarfire
1 Volcanic Hammer
[sideboard]
+[human_extra_cards]
+[ai_extra_cards]
+Furnace of Rath
\ No newline at end of file
diff --git a/res/quest/decks/quest5.dck b/res/quest/decks/quest5.dck
index 9e0bcf69496..81b8e7b65ad 100644
--- a/res/quest/decks/quest5.dck
+++ b/res/quest/decks/quest5.dck
@@ -1,6 +1,17 @@
-quest5
-[general]
-constructed
+[metadata]
+ID=5
+Name=quest5
+DisplayName=Dungeon Crawling (Green)
+Difficulty=Medium
+Description=Guarded by green creatures.
+Repeatable=true
+NumberWinsRequired=20
+CardReward=3 green rares
+CreditsReward=100
+Icon=Dungeon Crawling Green.jpg
+AILife=25
+Deck Type=constructed
+Comment=
[main]
33 Forest
1 Alpha Tyrranax
@@ -65,3 +76,8 @@ constructed
1 Woodfall Primus
1 Yavimaya Wurm
[sideboard]
+[human_extra_cards]
+Defense of the Heart
+[ai_extra_cards]
+Eladamri's Vineyard
+Upwelling
\ No newline at end of file
diff --git a/res/quest/decks/quest6.dck b/res/quest/decks/quest6.dck
index d1ad5300b39..76242092e62 100644
--- a/res/quest/decks/quest6.dck
+++ b/res/quest/decks/quest6.dck
@@ -1,76 +1,90 @@
-quest6
-[general]
-constructed
-[main]
-20 Island
-20 Swamp
-1 Complex Automaton
-1 Eldrazi Monument
-1 Esperzoa
-1 Forethought Amulet
-1 Masticore
-1 Molten-Tail Masticore
-1 Puppet Conjurer
-1 Razormane Masticore
-1 Rejuvenation Chamber
-1 Rusting Golem
-1 Soldevi Simulacrum
-1 Soultether Golem
-1 Thran War Machine
-1 Urza's Blueprints
-1 Aku Djinn
-1 Arnjlot's Ascent
-1 Benthic Djinn
-1 Binding Grasp
-1 Blessing of Leeches
-1 Breeding Pit
-1 Carnophage
-1 Cloudskate
-1 Cosmic Horror
-1 Demonic Appetite
-1 Drifter il-Dal
-1 Ebon Praetor
-1 Fledgling Djinn
-1 Flow of Maggots
-1 Greater Harvester
-1 Grinning Demon
-1 Illusionary Forces
-1 Illusionary Wall
-1 Illusions of Grandeur
-1 Imaginary Pet
-1 Junun Efreet
-1 Juzam Djinn
-1 Kezzerdrix
-1 Kuro, Pitlord
-1 Liege of the Pit
-1 Lord of the Pit
-1 Melancholy
-1 Minion of Tevesh Szat
-1 Molting Harpy
-1 Moroii
-1 Mystic Remora
-1 Oni Possession
-4 Pact of Negation
-1 Pact of the Titan
-1 Phantasmal Forces
-1 Phobian Phantasm
-1 Pit Raptor
-1 Pit Spawn
-1 Raven Familiar
-1 Sangrophage
-1 Sarcomancy
-1 School of Piranha
-1 Serendib Efreet
-1 Shauku, Endbringer
-1 Skull Collector
-4 Slaughter Pact
-1 Spindrift Drake
-1 Spiteful Bully
-1 Thirst
-1 Trusted Advisor
-1 Unstable Mutation
-1 Vampire Lacerator
-1 Waning Wurm
-1 Whipstitched Zombie
-1 Yawgmoth Demon
-[sideboard]
+[metadata]
+ID=6
+Name=quest6
+DisplayName=Dungeon Crawling (Colorless)
+Difficulty=Hard
+Description=The inhabitants of this plane tread with a light step.
+Repeatable=true
+NumberWinsRequired=28
+CardReward=3 colorless rares
+CreditsReward=150
+Icon=Dungeon Crawling Colorless.jpg
+AILife=30
+Deck Type=constructed
+Comment=
+[main]
+20 Island
+20 Swamp
+1 Complex Automaton
+1 Eldrazi Monument
+1 Esperzoa
+1 Forethought Amulet
+1 Masticore
+1 Molten-Tail Masticore
+1 Puppet Conjurer
+1 Razormane Masticore
+1 Rejuvenation Chamber
+1 Rusting Golem
+1 Soldevi Simulacrum
+1 Soultether Golem
+1 Thran War Machine
+1 Urza's Blueprints
+1 Aku Djinn
+1 Arnjlot's Ascent
+1 Benthic Djinn
+1 Binding Grasp
+1 Blessing of Leeches
+1 Breeding Pit
+1 Carnophage
+1 Cloudskate
+1 Cosmic Horror
+1 Demonic Appetite
+1 Drifter il-Dal
+1 Ebon Praetor
+1 Fledgling Djinn
+1 Flow of Maggots
+1 Greater Harvester
+1 Grinning Demon
+1 Illusionary Forces
+1 Illusionary Wall
+1 Illusions of Grandeur
+1 Imaginary Pet
+1 Junun Efreet
+1 Juzam Djinn
+1 Kezzerdrix
+1 Kuro, Pitlord
+1 Liege of the Pit
+1 Lord of the Pit
+1 Melancholy
+1 Minion of Tevesh Szat
+1 Molting Harpy
+1 Moroii
+1 Mystic Remora
+1 Oni Possession
+4 Pact of Negation
+1 Pact of the Titan
+1 Phantasmal Forces
+1 Phobian Phantasm
+1 Pit Raptor
+1 Pit Spawn
+1 Raven Familiar
+1 Sangrophage
+1 Sarcomancy
+1 School of Piranha
+1 Serendib Efreet
+1 Shauku, Endbringer
+1 Skull Collector
+4 Slaughter Pact
+1 Spindrift Drake
+1 Spiteful Bully
+1 Thirst
+1 Trusted Advisor
+1 Unstable Mutation
+1 Vampire Lacerator
+1 Waning Wurm
+1 Whipstitched Zombie
+1 Yawgmoth Demon
+[sideboard]
+[human_extra_cards]
+[ai_extra_cards]
+3 Eon Hub
\ No newline at end of file
diff --git a/res/quest/decks/quest7.dck b/res/quest/decks/quest7.dck
index 7a2a715b6d5..2fc207e569e 100644
--- a/res/quest/decks/quest7.dck
+++ b/res/quest/decks/quest7.dck
@@ -1,6 +1,17 @@
-quest7
-[general]
-constructed
+[metadata]
+ID=7
+Name=quest7
+DisplayName=Dungeon Crawling (Gold)
+Difficulty=Hard
+Description=Guarded by gold creatures.
+Repeatable=true
+NumberWinsRequired=28
+CardReward=3 multi-colored rares
+CreditsReward=150
+Icon=Dungeon Crawling Gold.jpg
+AILife=30
+Deck Type=constructed
+Comment=
[main]
4 Glimmervoid
4 Reflecting Pool
@@ -37,3 +48,6 @@ constructed
1 Conflux
1 Maelstrom Nexus
[sideboard]
+[human_extra_cards]
+[ai_extra_cards]
+Darksteel Ingot
\ No newline at end of file
diff --git a/res/quest/decks/quest8.dck b/res/quest/decks/quest8.dck
index b0f07d3d4d7..8bab6103fc9 100644
--- a/res/quest/decks/quest8.dck
+++ b/res/quest/decks/quest8.dck
@@ -1,6 +1,17 @@
-quest8
-[general]
-constructed
+[metadata]
+ID=8
+Name=quest8
+DisplayName=A Wolf in Sheep's Clothing
+Difficulty=Medium
+Description=The local sheep farm is under attack by a pack of wolves. Kill the wolves, save the sheep!
+Repeatable=false
+NumberWinsRequired=28
+CardReward=3 random rares
+CreditsReward=200
+Icon=A Wolf in Sheeps Clothing.jpg
+AILife=25
+Deck Type=constructed
+Comment=
[main]
1 Temple Garden
1 Mox Emerald
@@ -20,3 +31,8 @@ constructed
4 Tundra Wolves
4 Savannah
[sideboard]
+[human_extra_cards]
+TOKEN|G|0|1|sheep|creature|sheep
+TOKEN|G|0|1|sheep|creature|sheep
+TOKEN|G|0|1|sheep|creature|sheep
+[ai_extra_cards]
\ No newline at end of file
diff --git a/res/quest/decks/quest9.dck b/res/quest/decks/quest9.dck
index 1e609e6fb36..6916e106766 100644
--- a/res/quest/decks/quest9.dck
+++ b/res/quest/decks/quest9.dck
@@ -1,6 +1,17 @@
-quest9
-[general]
-constructed
+[metadata]
+ID=9
+Name=quest9
+DisplayName=Bushwhacked!
+Difficulty=Hard
+Description=You find yourself surrounded by carnivorous plants and poisonous vines. Cut your way out of this foresty mess and make it home safe.
+Repeatable=false
+NumberWinsRequired=40
+CardReward=4 green rares
+CreditsReward=225
+Icon=Bushwhacked.jpg
+AILife=30
+Deck Type=constructed
+Comment=
[main]
2 Avenger of Zendikar
1 Sapling of Colfenor
@@ -21,3 +32,7 @@ constructed
4 Vine Trellis
1 Overbeing of Myth
[sideboard]
+[human_extra_cards]
+Trusty Machete
+[ai_extra_cards]
+3 Wall of Wood
\ No newline at end of file
diff --git a/src/main/java/forge/AllZone.java b/src/main/java/forge/AllZone.java
index 41fe213057a..3f7f2ef3ea1 100644
--- a/src/main/java/forge/AllZone.java
+++ b/src/main/java/forge/AllZone.java
@@ -17,6 +17,7 @@ import forge.properties.ForgeProps;
import forge.properties.NewConstants;
import forge.quest.data.QuestMatchState;
import forge.quest.data.QuestData;
+import forge.quest.data.DeckSingleQuest;
/**
* Please use public getters and setters instead of direct field access.
@@ -40,9 +41,9 @@ public final class AllZone implements NewConstants {
/** Global questData. */
private static forge.quest.data.QuestData questData = null;
-
- /** Global QuestAssignment. */
- private static Quest_Assignment questAssignment = null;
+
+ /** Global currentQuest. */
+ private static forge.quest.data.DeckSingleQuest currentQuest = null;
/** Constant NAME_CHANGER. */
private static final NameChanger NAME_CHANGER = new NameChanger();
@@ -129,25 +130,25 @@ public final class AllZone implements NewConstants {
public static void setQuestData(final QuestData questData0) {
questData = questData0;
}
-
+
/**
- *
getQuestAssignment.
+ *getCurrentQuest.
* - * @return a {@link forge.Quest_Assignment} object. - * @since 1.0.15 + * @return a {@link forge.quest.data.QuestData} object. + * */ - public static Quest_Assignment getQuestAssignment() { - return questAssignment; + public static forge.quest.data.DeckSingleQuest getCurrentQuest() { + return currentQuest; } /** - *setQuestAssignment.
+ *setCurrentQuest.
* - * @param assignment a {@link forge.Quest_Assignment} object. - * @since 1.0.15 + * @param a DeckSingleQuest object. + * */ - public static void setQuestAssignment(final Quest_Assignment assignment) { - questAssignment = assignment; + public static void setCurrentQuest(final DeckSingleQuest qq) { + currentQuest = qq; } /** diff --git a/src/main/java/forge/gui/SelectablePanel.java b/src/main/java/forge/gui/SelectablePanel.java new file mode 100644 index 00000000000..5af89c1013c --- /dev/null +++ b/src/main/java/forge/gui/SelectablePanel.java @@ -0,0 +1,43 @@ +package forge.gui; + +import java.awt.Color; + +import javax.swing.JPanel; + + +/** + *SelectablePanel
+ * VIEW - Standard selectable JPanel used for many places in the GUI. + * + */ +@SuppressWarnings("serial") +public abstract class SelectablePanel extends JPanel { + + private boolean selected = false; + protected Color backgroundColor = this.getBackground(); + + /** + *Getter for the field selected
Setter for the field selected
DeckSingleBattle
+ * MODEL - Assembles and stores information from a battle deck. + * + * @author Forge + * @version $Id$ + */ +public class DeckSingleBattle { + private String deckName; + private String displayName; + private String diff; + private String desc; + private String iconFilename; + private ImageIcon icon; + private Deck deckObj; + + /** + *Constructor for DeckSingleBattle.
+ * + * @param {@link java.lang.String} storing name of AI deck for this battle + */ + public DeckSingleBattle(Deck d) { + // Get deck object and properties for this opponent. + this.deckObj = d; + this.deckName = d.getName(); + this.displayName = deckObj.getMetadata("DisplayName"); + this.diff = deckObj.getMetadata("Difficulty"); + this.desc = deckObj.getMetadata("Description"); + this.iconFilename = deckObj.getMetadata("Icon"); + + // Default icon + this.icon = GuiUtils.getIconFromFile(displayName + ".jpg"); + + // If non-default icon defined, use it. Any filetype accepted. + if(!iconFilename.equals("")) { + this.icon = GuiUtils.getIconFromFile(iconFilename); + } + } + + /** + *getDifficulty()
+ * Retrieve rated difficulty of this battle deck. + * + * @return {@link java.lang.String} + */ + public String getDifficulty() { + return this.diff; + } + + /** + *getDescription()
+ * Retrieve description of this battle deck. + * + * @return {@link java.lang.String} + */ + public String getDescription() { + return this.desc; + } + + /** + *getDisplayName()
+ * Retrieve display name of this battle deck. + * + * @return {@link java.lang.String} + */ + public String getDisplayName() { + return this.displayName; + } + + /** + *getDeckName()
+ * Retrieve file name of this battle deck. + * + * @return {@link java.lang.String} + */ + public String getDeckName() { + return this.deckName; + } + + /** + *getIconFilename()
+ * Retrieve file name of preferred icon + * + * @return {@link java.lang.String} + */ + public String getIconFilename() { + return this.iconFilename; + } + + /** + *getDeck()
+ * Retrieve this battle deck. + * + * @return {@link java.lang.String} + */ + public Deck getDeck() { + return this.deckObj; + } + + /** + *getIcon()
+ * Retrieve the icon used with this battle deck. + * + * @return {@link java.lang.String} + */ + public ImageIcon getIcon() { + return this.icon; + } + +} diff --git a/src/main/java/forge/quest/data/DeckSingleQuest.java b/src/main/java/forge/quest/data/DeckSingleQuest.java new file mode 100644 index 00000000000..7143a1c80d0 --- /dev/null +++ b/src/main/java/forge/quest/data/DeckSingleQuest.java @@ -0,0 +1,270 @@ +package forge.quest.data; + +import java.util.ArrayList; +import javax.swing.ImageIcon; + +import com.google.code.jyield.Generator; +import com.google.code.jyield.YieldUtils; + +import forge.AllZone; +import forge.Card; +import forge.Constant; +import forge.deck.Deck; +import forge.gui.GuiUtils; + +/** + *DeckSingleQuest
+ * MODEL - Assembles and stores information from a quest deck. + * + * @author Forge + * @version $Id$ + */ +public class DeckSingleQuest { + private String deckName; + private String displayName; + private String diff; + private String desc; + private String iconFilename; + private String cardReward; + + private int creditsReward; + private int numberWinsRequired; + private int AILife; + private int id; + + private boolean repeatable; + private ImageIcon icon; + private Deck deckObj; + + /** + *Constructor for DeckSingleQuest
+ * + * @param {@link java.lang.String} storing name of AI deck for this quest + */ + public DeckSingleQuest(Deck d) { + // Get deck object and properties for this opponent. + this.deckObj = d; + this.deckName = d.getName(); + this.id = Integer.parseInt(deckObj.getMetadata("ID")); + this.displayName = deckObj.getMetadata("DisplayName"); + this.diff = deckObj.getMetadata("Difficulty"); + this.desc = deckObj.getMetadata("Description"); + this.iconFilename = deckObj.getMetadata("Icon"); + this.cardReward = deckObj.getMetadata("CardReward"); + this.creditsReward = Integer.parseInt(deckObj.getMetadata("CreditsReward")); + this.numberWinsRequired = Integer.parseInt(deckObj.getMetadata("NumberWinsRequired")); + this.AILife = Integer.parseInt(deckObj.getMetadata("AILife")); + + // Default icon + this.icon = GuiUtils.getIconFromFile(displayName + ".jpg"); + + // If non-default icon defined, use it. Any filetype accepted. + if(!iconFilename.equals("")) { + this.icon = GuiUtils.getIconFromFile(iconFilename); + } + + // Repeatability test + if(deckObj.getMetadata("Repeatable").equals("true")) { + this.repeatable = true; + } + else { + this.repeatable = false; + } + + } + + /** + *getID()
+ * Retrieve ID number of this quest deck for recordkeeping. + * + * @return {@link java.lang.int} + */ + public int getID() { + return this.id; + } + + /** + *getDifficulty()
+ * Retrieve rated difficulty of this quest deck. + * + * @return {@link java.lang.String} + */ + public String getDifficulty() { + return this.diff; + } + + /** + *getDescription()
+ * Retrieve description of this quest deck. + * + * @return {@link java.lang.String} + */ + public String getDescription() { + return this.desc; + } + + /** + *getDisplayName()
+ * Retrieve display name of this quest deck. + * + * @return {@link java.lang.String} + */ + public String getDisplayName() { + return this.displayName; + } + + /** + *getDeckName()
+ * Retrieve file name of this quest deck. + * + * @return {@link java.lang.String} + */ + public String getDeckName() { + return this.deckName; + } + + /** + *getCardReward()
+ * Retrieve cards rewarded after a win with this quest deck. + * + * @return {@link java.lang.String} + */ + private String getCardReward() { + return this.cardReward; + } + + /** + *getCardRewardList()
+ * Retrieve cards rewarded after a win with this quest deck. + * + * @return String[] + */ + public ArrayListgetNumberWinsRequired()
+ * Retrieve number of wins required to play against this quest deck. + * + * @return {@link java.lang.int} + */ + public int getNumberWinsRequired() { + return this.numberWinsRequired; + } + + /** + *getAILife()
+ * Retrieve starting value of life for the AI playing this quest deck. + * + * @return {@link java.lang.int} + */ + public int getAILife() { + return this.AILife; + } + + /** + *getCreditsReward()
+ * Retrieve number of credits rewarded after a win against this quest deck. + * + * @return {@link java.lang.int} + */ + public int getCreditsReward() { + return this.creditsReward; + } + + /** + *getDeck()
+ * Retrieve this quest deck. + * + * @return {@link forge.deck.Deck} + */ + public Deck getDeck() { + return this.deckObj; + } + + /** + *getIconFilename()
+ * Retrieve file name of preferred icon + * + * @return {@link java.lang.String} + */ + public String getIconFilename() { + return this.iconFilename; + } + + /** + *getIcon()
+ * Retrieve the icon used with this quest deck. + * + * @return {@link javax.swing.ImageIcon} + */ + public ImageIcon getIcon() { + return this.icon; + } + + /** + *getRepeatable.
+ * Retrieve boolean indicating if this quest is repeatable. + * + */ + public boolean getRepeatable() { + return this.repeatable; + } + +} diff --git a/src/main/java/forge/quest/data/ManagerBattle.java b/src/main/java/forge/quest/data/ManagerBattle.java new file mode 100644 index 00000000000..4cb3579693d --- /dev/null +++ b/src/main/java/forge/quest/data/ManagerBattle.java @@ -0,0 +1,237 @@ +package forge.quest.data; + +import forge.AllZone; +import forge.FileUtil; +import forge.deck.Deck; +import forge.deck.DeckManager; +import forge.error.ErrorViewer; +import forge.properties.ForgeProps; +import forge.properties.NewConstants; + +import java.io.File; +import java.util.*; + +/** + *QuestBattleManager
+ * MODEL - Provides static methods to accomplish two key tasks: + * 1. Stores various AI difficulty decks and generates opponent list based on quest record. + * + * 2. Can instantiate a DeckSingleBattle for each opponent in the list. + * + * @author Forge + * @version $Id$ + */ + +// This could be combined with BattleManager and moved into QuestUtil. +public class ManagerBattle { + /** ConstantaiDecks */
+ private static transient MapeasyAIDecks */
+ private static transient ListmediumAIDecks */
+ private static transient ListhardAIDecks */
+ private static transient ListveryHardAIDecks */
+ private static transient ListremoveAIDeck.
+ * Removes a deck object stored in the + *{@link forge.quest.gui.main.aiDecks} map. + * + * @param deckName a {@link java.lang.String} object. + */ + public static void removeAIDeck(String deckName) { + aiDecks.remove(deckName); + } + + /** + *addAIDeck.
+ * Adds a deck object stored in the + *{@link forge.quest.gui.main.aiDecks} map. + * + * @param d a {@link forge.deck.Deck} object. + */ + public static void addAIDeck(Deck d) { + aiDecks.put(d.getName(), d); + } + + /** + *getAIDeckFromMap.
+ * Returns a deck object stored in the + *{@link forge.quest.gui.main.aiDecks} map. + * + * @param deckName a {@link java.lang.String} object. + * @return a {@link forge.deck.Deck} object. + */ + public static Deck getAIDeckFromMap(String deckName) { + if (!aiDecks.containsKey(deckName)) { + ErrorViewer.showError(new Exception(), + "QuestData : getAIDeckFromMap(String deckName) error, deck name not found - %s", deckName); + } + + return aiDecks.get(deckName); + } + + /** + *getAIDeckNames.
+ * Returns a list of decks stored in the + * {@link forge.quest.gui.main.aiDecks} map. + * + * @return a {@link java.util.List} object. + */ + public static ListgetDeckFromFile.
+ * Returns a deck object built from a file name. + * Req'd because NewConstants.QUEST.DECKS must be used. + * + * @param deckName a {@link java.lang.String} object. + * @return a {@link forge.deck.Deck} object. + */ + public static Deck getAIDeckFromFile(String deckName) { + final File file = ForgeProps.getFile(NewConstants.QUEST.DECKS); + final DeckManager manager = new DeckManager(file); + return manager.getDeck(deckName); + } + + /** + *getOpponent.
+ * + * Poorly named; AllZoneUtil already has a method called getOpponents. + * ????? Mechanics of this still a mystery ????? + * + * @param aiDeck a {@link java.util.List} object. + * @param number a int. + * @return a {@link java.lang.String} object. + */ + public static String getOpponent(ListgenerateBattles.
+ * Generates an array of new opponents based on current win conditions. + * + * @return an array of {@link java.lang.String} objects. + */ + public static String[] generateBattles() { + int index = AllZone.getQuestData().getDifficultyIndex(); + + if (AllZone.getQuestData().getWin() < QuestPreferences.getWinsForMediumAI(index)) { + return new String[]{ + getOpponent(easyAIDecks, 0), + getOpponent(easyAIDecks, 1), + getOpponent(easyAIDecks, 2)}; + } + + if (AllZone.getQuestData().getWin() == QuestPreferences.getWinsForMediumAI(index)) { + return new String[]{ + getOpponent(easyAIDecks, 0), + getOpponent(mediumAIDecks, 0), + getOpponent(mediumAIDecks, 1)}; + } + + if (AllZone.getQuestData().getWin() < QuestPreferences.getWinsForHardAI(index)) { + return new String[]{ + getOpponent(mediumAIDecks, 0), + getOpponent(mediumAIDecks, 1), + getOpponent(mediumAIDecks, 2)}; + } + + if (AllZone.getQuestData().getWin() == QuestPreferences.getWinsForHardAI(index)) { + return new String[]{ + getOpponent(mediumAIDecks, 0), + getOpponent(hardAIDecks, 0), + getOpponent(hardAIDecks, 1)}; + } + + if (AllZone.getQuestData().getWin() >= QuestPreferences.getWinsForVeryHardAI(index)) { + return new String[]{ + getOpponent(hardAIDecks, 0), + getOpponent(hardAIDecks, 1), + getOpponent(veryHardAIDecks, 0)}; + } + + return new String[]{ + getOpponent(hardAIDecks, 0), + getOpponent(hardAIDecks, 1), + getOpponent(hardAIDecks, 2)}; + } // End generateBattles() + + /** + *readFile.
+ * A reader util for accessing the AI deck list text files. + * + * @param file a {@link java.io.File} object. + * @param aiDecks a {@link java.util.List} object. + * @return a {@link java.util.List} object. + */ + private static ListgetBattles.
+ * + * Returns list of DeckSingleBattle objects storing data + * of the battles currently available. + * + * @return a {@link java.util.List} object. + */ + public static ListManagerQuest
+ * MODEL - Provides static methods to work with quest-related tasks. + * + */ + +// This could be combined with BattleManager and moved into QuestUtil? +public class ManagerQuest { + /** + *getQuests
+ * + * Returns list of DeckSingleQuest objects storing data + * of the quests currently available. + * + * @return a {@link java.util.List} object. + */ + + public static ListgetDeckFromFile.
+ * Returns a deck object built from a file name. + * Req'd because NewConstants.QUEST.DECKS must be used. + * + * @param deckName a {@link java.lang.String} object. + * @return a {@link forge.deck.Deck} object. + */ + public static Deck getAIDeckFromFile(String deckName) { + final File file = ForgeProps.getFile(NewConstants.QUEST.DECKS); + final DeckManager manager = new DeckManager(file); + return manager.getDeck(deckName); + } +} diff --git a/src/main/java/forge/quest/gui/PanelSingleBattle.java b/src/main/java/forge/quest/gui/PanelSingleBattle.java new file mode 100644 index 00000000000..02b8b18ef2b --- /dev/null +++ b/src/main/java/forge/quest/gui/PanelSingleBattle.java @@ -0,0 +1,100 @@ +package forge.quest.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; + +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; + +import forge.gui.GuiUtils; +import forge.gui.SelectablePanel; +import forge.quest.data.DeckSingleBattle; + +/** + *PanelSingleBattle
+ * VIEW - A selectable panel for battles available in Quest mode. + * + */ +@SuppressWarnings("serial") +public class PanelSingleBattle extends SelectablePanel { + + private final DeckSingleBattle battle; + + public PanelSingleBattle(DeckSingleBattle b) { + battle = b; + final JPanel centerPanel = new JPanel(); + + this.setLayout(new BorderLayout(5, 5)); + + // Icon stuff + JLabel iconLabel; + + if (battle.getIcon() == null) { + iconLabel = new JLabel(GuiUtils.getEmptyIcon(40, 40)); + } else { + iconLabel = new JLabel(GuiUtils.getResizedIcon(battle.getIcon(), 40, 40)); + } + + iconLabel.setBorder(new LineBorder(Color.BLACK)); + iconLabel.setAlignmentY(TOP_ALIGNMENT); + + JPanel iconPanel = new JPanel(new BorderLayout()); + iconPanel.setOpaque(false); + iconPanel.add(iconLabel, BorderLayout.NORTH); + this.add(iconPanel, BorderLayout.WEST); + + centerPanel.setOpaque(false); + centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); + this.add(centerPanel, BorderLayout.CENTER); + + JPanel centerTopPanel = new JPanel(); + centerTopPanel.setOpaque(false); + centerTopPanel.setAlignmentX(LEFT_ALIGNMENT); + centerTopPanel.setLayout(new BoxLayout(centerTopPanel, BoxLayout.X_AXIS)); + + JLabel nameLabel = new JLabel(battle.getDisplayName()); + GuiUtils.setFontSize(nameLabel, 20); + nameLabel.setAlignmentY(BOTTOM_ALIGNMENT); + centerTopPanel.add(nameLabel); + + GuiUtils.addExpandingHorizontalSpace(centerTopPanel); + + JLabel difficultyLabel = new JLabel(battle.getDifficulty()); + difficultyLabel.setAlignmentY(BOTTOM_ALIGNMENT); + centerTopPanel.add(difficultyLabel); + centerPanel.add(centerTopPanel); + + GuiUtils.addGap(centerPanel); + + JLabel descriptionLabel = new JLabel(battle.getDescription()); + descriptionLabel.setAlignmentX(LEFT_ALIGNMENT); + centerPanel.add(descriptionLabel); + + this.setMaximumSize(new Dimension(Integer.MAX_VALUE, 80)); + this.setBorder(new CompoundBorder(new LineBorder(Color.BLACK), new EmptyBorder(5, 5, 5, 5))); + } + + /** + *getIconFilename()
+ * Retrieves filename of icon used in this panel's display. + * + * @return + */ + public String getIconFilename() { + return this.battle.getIconFilename(); + } + + /** + *getQuest()
+ * + * @return the DeckSingleBattle model associated with this panel. + */ + public DeckSingleBattle getBattle() { + return this.battle; + } +} diff --git a/src/main/java/forge/quest/gui/PanelSingleQuest.java b/src/main/java/forge/quest/gui/PanelSingleQuest.java new file mode 100644 index 00000000000..b4f119ca457 --- /dev/null +++ b/src/main/java/forge/quest/gui/PanelSingleQuest.java @@ -0,0 +1,110 @@ +package forge.quest.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; + +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; + +import forge.gui.GuiUtils; +import forge.gui.SelectablePanel; +import forge.quest.data.DeckSingleQuest; + +/** + *PanelSingleQuest
+ * VIEW - A selectable panel for quests available in Quest mode. + * + */ +@SuppressWarnings("serial") +public class PanelSingleQuest extends SelectablePanel { + + private final DeckSingleQuest quest; + + public PanelSingleQuest(DeckSingleQuest q) { + quest = q; + final JPanel centerPanel = new JPanel(); + + this.setLayout(new BorderLayout(5, 5)); + + JLabel iconLabel; + + if (quest.getIcon() == null) { + iconLabel = new JLabel(GuiUtils.getEmptyIcon(40, 40)); + } else { + iconLabel = new JLabel(GuiUtils.getResizedIcon(quest.getIcon(), 40, 40)); + } + + iconLabel.setBorder(new LineBorder(Color.BLACK)); + iconLabel.setAlignmentY(TOP_ALIGNMENT); + + JPanel iconPanel = new JPanel(new BorderLayout()); + iconPanel.setOpaque(false); + iconPanel.add(iconLabel, BorderLayout.NORTH); + this.add(iconPanel, BorderLayout.WEST); + + centerPanel.setOpaque(false); + centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); + this.add(centerPanel, BorderLayout.CENTER); + + JPanel centerTopPanel = new JPanel(); + centerTopPanel.setOpaque(false); + centerTopPanel.setAlignmentX(LEFT_ALIGNMENT); + centerTopPanel.setLayout(new BoxLayout(centerTopPanel, BoxLayout.X_AXIS)); + + JLabel nameLabel = new JLabel(quest.getDisplayName()); + GuiUtils.setFontSize(nameLabel, 20); + nameLabel.setAlignmentY(BOTTOM_ALIGNMENT); + centerTopPanel.add(nameLabel); + + GuiUtils.addExpandingHorizontalSpace(centerTopPanel); + + JLabel difficultyLabel = new JLabel(quest.getDifficulty()); + difficultyLabel.setAlignmentY(BOTTOM_ALIGNMENT); + centerTopPanel.add(difficultyLabel); + centerPanel.add(centerTopPanel); + + GuiUtils.addGap(centerPanel); + + JLabel descriptionLabel = new JLabel(quest.getDescription()); + descriptionLabel.setAlignmentX(LEFT_ALIGNMENT); + centerPanel.add(descriptionLabel); + + // Temporarily removed; no meaning yet (all quests repeat anyway) + /*JLabel repeatabilityLabel; + if (quest.getRepeatable()) { + repeatabilityLabel = new JLabel("This quest is repeatable"); + } else { + repeatabilityLabel = new JLabel("This quest is not repeatable"); + } + + GuiUtils.addGap(centerPanel); + centerPanel.add(repeatabilityLabel);*/ + + this.setMaximumSize(new Dimension(Integer.MAX_VALUE, 80)); + this.setBorder(new CompoundBorder(new LineBorder(Color.BLACK), new EmptyBorder(5, 5, 5, 5))); + } + + /** + *getIconFilename()
+ * Retrieves filename of icon used in this panel's display. + * + * @return + */ + public String getIconFilename() { + return this.quest.getIconFilename(); + } + + /** + *getQuest()
+ * + * @return the DeckSingleQuest model associated with this panel. + */ + public DeckSingleQuest getQuest() { + return this.quest; + } +} diff --git a/src/main/java/forge/quest/gui/QuestFrame.java b/src/main/java/forge/quest/gui/QuestFrame.java index d72750139ce..0fad3534028 100644 --- a/src/main/java/forge/quest/gui/QuestFrame.java +++ b/src/main/java/forge/quest/gui/QuestFrame.java @@ -3,7 +3,6 @@ package forge.quest.gui; import forge.AllZone; import forge.gui.GuiUtils; import forge.quest.gui.bazaar.QuestBazaarPanel; -import forge.quest.gui.main.QuestMainPanel; import forge.view.swing.OldGuiNewGame; import javax.swing.*; diff --git a/src/main/java/forge/quest/gui/QuestMainPanel.java b/src/main/java/forge/quest/gui/QuestMainPanel.java new file mode 100644 index 00000000000..a945f8d2897 --- /dev/null +++ b/src/main/java/forge/quest/gui/QuestMainPanel.java @@ -0,0 +1,817 @@ +package forge.quest.gui; + + +import forge.*; +import forge.deck.Deck; +import forge.gui.GuiUtils; +import forge.gui.SelectablePanel; +import forge.quest.data.DeckSingleBattle; +import forge.quest.data.ManagerBattle; +import forge.quest.data.QuestData; +import forge.quest.data.DeckSingleQuest; +import forge.quest.data.ManagerQuest; +import forge.quest.data.item.QuestItemZeppelin; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.*; +import java.util.List; + + +/** + *QuestMainPanel class.
+ * VIEW - handler for main screen of Quest GUI. + * + * @author Forge + * @version $Id$ + */ +public class QuestMainPanel extends QuestAbstractPanel { + /** ConstantserialVersionUID=6142934729724012402L */
+ private static final long serialVersionUID = 6142934729724012402L;
+
+ private forge.quest.data.QuestData questData;
+
+ JLabel creditsLabel = new JLabel();
+ JLabel lifeLabel = new JLabel();
+ JLabel statsLabel = new JLabel();
+ JLabel titleLabel = new JLabel();
+ JLabel nextQuestLabel = new JLabel();
+
+ JComboBox petComboBox = new JComboBox();
+ JComboBox deckComboBox = new JComboBox();
+
+ JButton questButton = new JButton("Quests");
+ JButton playButton = new JButton("Play");
+
+ private SelectablePanel lastPanelSelected;
+
+ JPanel nextMatchPanel = new JPanel();
+ CardLayout nextMatchLayout;
+
+ boolean isShowingQuests = false;
+ private JCheckBox devModeCheckBox = new JCheckBox("Developer Mode");
+ //private JCheckBox newGUICheckbox = new JCheckBox("Use new UI", true);
+ private JCheckBox smoothLandCheckBox = new JCheckBox("Adjust AI Land");
+ private JCheckBox petCheckBox = new JCheckBox("Summon Pet");
+
+ private JCheckBox plantBox = new JCheckBox("Summon Plant");
+ /** Constant NO_DECKS_AVAILABLE="No decks available" */
+ private static final String NO_DECKS_AVAILABLE = "No decks available";
+ /** Constant BATTLES="Battles" */
+ private static final String BATTLES = "Battles";
+ /** Constant QUESTS="Quests" */
+ private static final String QUESTS = "Quests";
+
+ //TODO: Make this ordering permanent
+ /** Constant lastUsedDeck="//TODO: Make this ordering permanent" */
+ private static String lastUsedDeck;
+ private JButton zeppelinButton = new JButton("LaunchConstructor for QuestMainPanel.
+ * + * @param mainFrame a {@link forge.quest.gui.QuestFrame} object. + */ + public QuestMainPanel(QuestFrame mainFrame) { + super(mainFrame); + questData = AllZone.getQuestData(); + + initUI(); + } + + /** + *initUI.
+ */ + private void initUI() { + refresh(); + this.setLayout(new BorderLayout(5, 5)); + JPanel centerPanel = new JPanel(new BorderLayout()); + this.add(centerPanel, BorderLayout.CENTER); + + JPanel northPanel = createStatusPanel(); + this.add(northPanel, BorderLayout.NORTH); + + JPanel eastPanel = createSidePanel(); + this.add(eastPanel, BorderLayout.EAST); + + JPanel matchSettingsPanel = createMatchSettingsPanel(); + centerPanel.add(matchSettingsPanel, BorderLayout.SOUTH); + + centerPanel.add(nextMatchPanel, BorderLayout.CENTER); + this.setBorder(new EmptyBorder(5, 5, 5, 5)); + + } + + /** + *createStatusPanel.
+ * + * @return a {@link javax.swing.JPanel} object. + */ + private JPanel createStatusPanel() { + JPanel northPanel = new JPanel(); + JLabel modeLabel; + JLabel difficultyLabel;//Create labels at the top + titleLabel.setFont(new Font(Font.DIALOG, Font.PLAIN, 28)); + titleLabel.setAlignmentX(LEFT_ALIGNMENT); + northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.Y_AXIS)); + northPanel.add(titleLabel); + + northPanel.add(Box.createVerticalStrut(5)); + + JPanel statusPanel = new JPanel(); + statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.X_AXIS)); + statusPanel.setAlignmentX(LEFT_ALIGNMENT); + + modeLabel = new JLabel(questData.getMode()); + statusPanel.add(modeLabel); + statusPanel.add(Box.createHorizontalGlue()); + + difficultyLabel = new JLabel(questData.getDifficulty()); + statusPanel.add(difficultyLabel); + statusPanel.add(Box.createHorizontalGlue()); + + statusPanel.add(statsLabel); + + northPanel.add(statusPanel); + return northPanel; + } + + /** + *createSidePanel.
+ * + * @return a {@link javax.swing.JPanel} object. + */ + private JPanel createSidePanel() { + JPanel panel = new JPanel(); + JPanel optionsPanel; //Create options checkbox list + optionsPanel = createOptionsPanel(); + + ListcreateOptionsPanel.
+ * + * @return a {@link javax.swing.JPanel} object. + */ + private JPanel createOptionsPanel() { + JPanel optionsPanel; + optionsPanel = new JPanel(); + optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS)); + + //optionsPanel.add(this.newGUICheckbox); + optionsPanel.add(Box.createVerticalStrut(5)); + optionsPanel.add(this.smoothLandCheckBox); + optionsPanel.add(Box.createVerticalStrut(5)); + optionsPanel.add(this.devModeCheckBox); + optionsPanel.setBorder(new TitledBorder(new EtchedBorder(), "Options")); + return optionsPanel; + } + + /** + *createMatchSettingsPanel.
+ * + * @return a {@link javax.swing.JPanel} object. + */ + private JPanel createMatchSettingsPanel() { + + JPanel matchPanel = new JPanel(); + matchPanel.setLayout(new BoxLayout(matchPanel, BoxLayout.Y_AXIS)); + + JPanel deckPanel = new JPanel(); + deckPanel.setLayout(new BoxLayout(deckPanel, BoxLayout.X_AXIS)); + + JLabel deckLabel = new JLabel("Use Deck"); + deckPanel.add(deckLabel); + GuiUtils.addGap(deckPanel); + + this.deckComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + playButton.setEnabled(canGameBeLaunched()); + lastUsedDeck = (String) deckComboBox.getSelectedItem(); + } + }); + + deckPanel.add(this.deckComboBox); + GuiUtils.addGap(deckPanel); + + JButton editDeckButton = new JButton("Deck Editor"); + editDeckButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + showDeckEditor(); + } + }); + deckPanel.add(editDeckButton); + deckPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, deckPanel.getPreferredSize().height)); + deckPanel.setAlignmentX(LEFT_ALIGNMENT); + matchPanel.add(deckPanel); + + + GuiUtils.addGap(matchPanel); + + if (questData.getMode().equals(forge.quest.data.QuestData.FANTASY)) { + JPanel fantasyPanel = new JPanel(); + fantasyPanel.setLayout(new BorderLayout()); + + JPanel petPanel = new JPanel(); + petPanel.setLayout(new BoxLayout(petPanel, BoxLayout.X_AXIS)); + + this.petCheckBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + if (petCheckBox.isSelected()) { + questData.getPetManager().setSelectedPet((String) petComboBox.getSelectedItem()); + } else { + questData.getPetManager().setSelectedPet(null); + } + + petComboBox.setEnabled(petCheckBox.isSelected()); + } + }); + + petPanel.add(this.petCheckBox); + GuiUtils.addGap(petPanel); + this.petComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + if (petCheckBox.isSelected()) { + questData.getPetManager().setSelectedPet((String) petComboBox.getSelectedItem()); + } else { + questData.getPetManager().setSelectedPet(null); + } + } + }); + this.petComboBox.setMaximumSize( + new Dimension(Integer.MAX_VALUE, + (int) this.petCheckBox.getPreferredSize().getHeight())); + petPanel.add(this.petComboBox); + + this.plantBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + questData.getPetManager().usePlant = plantBox.isSelected(); + } + }); + + GuiUtils.addGap(petPanel, 10); + petPanel.add(this.plantBox); + petPanel.setMaximumSize(petPanel.getPreferredSize()); + petPanel.setAlignmentX(LEFT_ALIGNMENT); + + fantasyPanel.add(petPanel, BorderLayout.WEST); + + zeppelinButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + questData.randomizeOpponents(); + refreshNextMatchPanel(); + QuestItemZeppelin zeppelin = (QuestItemZeppelin) questData.getInventory().getItem("Zeppelin"); + zeppelin.setZeppelinUsed(true); + zeppelinButton.setEnabled(false); + } + }); + + zeppelinButton.setMaximumSize(zeppelinButton.getPreferredSize()); + zeppelinPanel.setLayout(new BorderLayout()); + + fantasyPanel.add(zeppelinPanel, BorderLayout.EAST); + fantasyPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + matchPanel.add(fantasyPanel); + } + return matchPanel; + } + + /** + *createBattlePanel
+ * Returns a JPanel containing PanelSingleBattle instances for each battle. + * + * @return a {@link javax.swing.JPanel} object. + */ + private JPanel createBattlePanel() { + JPanel pan = new JPanel(); + pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS)); + pan.setBorder(new TitledBorder(new EtchedBorder(), "Available Battles")); + PanelSingleBattle pb; + + ListcreateQuestPanel.
+ * Returns a JPanel containing PanelSingleQuest instances for each battle. + * + * @return a {@link javax.swing.JPanel} object. + */ + private JPanel createQuestPanel() { + JPanel pan = new JPanel(); + pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS)); + pan.setBorder(new TitledBorder(new EtchedBorder(), "Available Quests")); + PanelSingleQuest pq; + + Listrefresh.
+ */ + void refresh() { + AllZone.getQuestData().saveData(); + + devModeCheckBox.setSelected(Constant.Runtime.DevMode[0]); + smoothLandCheckBox.setSelected(Constant.Runtime.Smooth[0]); + //newGUICheckbox.setSelected(OldGuiNewGame.preferences.newGui); + + creditsLabel.setText(" " + questData.getCredits()); + statsLabel.setText(questData.getWin() + " wins / " + questData.getLost() + " losses"); + titleLabel.setText(questData.getRank()); + + //copy lastUsedDeck as removal triggers selection change. + String lastUsedDeck = QuestMainPanel.lastUsedDeck; + deckComboBox.removeAllItems(); + + if (questData.getDeckNames().size() > 0) { + deckComboBox.setEnabled(true); + + ListrefreshNextMatchPanel.
+ */ + private void refreshNextMatchPanel() { + nextMatchPanel.removeAll(); + nextMatchLayout = new CardLayout(); + nextMatchPanel.setLayout(nextMatchLayout); + nextMatchPanel.add(createBattlePanel(), BATTLES); + nextMatchPanel.add(createQuestPanel(), QUESTS); + if (isShowingQuests) { + this.nextMatchLayout.show(nextMatchPanel, QUESTS); + } else { + this.nextMatchLayout.show(nextMatchPanel, BATTLES); + } + } + + /** + *nextQuestInWins.
+ * + * @return a int. + */ + private int nextQuestInWins() { + + // Number of wins was 25, lowereing the number to 20 to help short term questers. + if (questData.getWin() < 20) { + return 20 - questData.getWin(); + } + + // The int mul has been lowered by one, should face special opps more frequently. + int questsPlayed = questData.getQuestsPlayed(); + int mul = 5; + + if (questData.getInventory().hasItem("Zeppelin")) { + mul = 3; + } else if (questData.getInventory().hasItem("Map")) { + mul = 4; + } + + int delta = (questsPlayed * mul) - questData.getWin(); + + return (delta > 0) ? delta : 0; + } + + + /** + *showDeckEditor.
+ */ + void showDeckEditor() { + Command exit = new Command() { + private static final long serialVersionUID = -5110231879431074581L; + + public void execute() { + //saves all deck data + AllZone.getQuestData().saveData(); + + new QuestFrame(); + } + }; + + Gui_Quest_DeckEditor g = new Gui_Quest_DeckEditor(); + + g.show(exit); + g.setVisible(true); + mainFrame.dispose(); + }//deck editor button + + /** + *showBazaar.
+ */ + void showBazaar() { + mainFrame.showBazaarPane(); + } + + /** + *showCardShop.
+ */ + void showCardShop() { + Command exit = new Command() { + private static final long serialVersionUID = 8567193482568076362L; + + public void execute() { + //saves all deck data + AllZone.getQuestData().saveData(); + + new QuestFrame(); + } + }; + + Gui_CardShop g = new Gui_CardShop(questData); + + g.show(exit); + g.setVisible(true); + + this.mainFrame.dispose(); + + }//card shop button + + /** + *launchGame.
+ */ + private void launchGame() { + //TODO: This is a temporary hack to see if the image cache affects the heap usage significantly. + ImageCache.clear(); + + QuestItemZeppelin zeppelin = (QuestItemZeppelin) questData.getInventory().getItem("Zeppelin"); + zeppelin.setZeppelinUsed(false); + questData.randomizeOpponents(); + + String humanDeckName = (String) deckComboBox.getSelectedItem(); + Deck humanDeck = questData.getDeck(humanDeckName); + Constant.Runtime.HumanDeck[0] = humanDeck; + moveDeckToTop(humanDeckName); + + Constant.Quest.oppIconName[0] = getMatchIcon(); + + // Dev Mode occurs before Display + Constant.Runtime.DevMode[0] = devModeCheckBox.isSelected(); + + //DO NOT CHANGE THIS ORDER, GuiDisplay needs to be created before cards are added + //if (newGUICheckbox.isSelected()) { + AllZone.setDisplay(new GuiDisplay4()); + //} else { + // AllZone.setDisplay(new GuiDisplay3()); + //} + + //OldGuiNewGame.preferences.newGui = newGUICheckbox.isSelected(); + + Constant.Runtime.Smooth[0] = smoothLandCheckBox.isSelected(); + + AllZone.getMatchState().reset(); + if (isShowingQuests) { + setupQuest(humanDeck); + } else { + setupBattle(humanDeck); + } + + AllZone.getQuestData().saveData(); + + AllZone.getDisplay().setVisible(true); + mainFrame.dispose(); + } + + + /** + *setupBattle.
+ * + * @param humanDeck a {@link forge.deck.Deck} object. + */ + void setupBattle(Deck humanDeck) { + Deck computer = ((PanelSingleBattle) lastPanelSelected).getBattle().getDeck(); + Constant.Runtime.ComputerDeck[0] = computer; + + AllZone.getGameAction().newGame(humanDeck, computer, forge.quest.data.QuestUtil.getHumanExtraCards(questData), + new CardList(), questData.getLife(), 20, null); + } + + /** + *setupQuest.
+ * + * @param humanDeck a {@link forge.deck.Deck} object. + */ + private void setupQuest(Deck humanDeck) { + DeckSingleQuest selectedQuest = ((PanelSingleQuest) lastPanelSelected).getQuest(); + AllZone.setCurrentQuest(selectedQuest); + + Deck computerDeck = ManagerBattle.getAIDeckFromFile("quest" + selectedQuest.getID()); + Constant.Runtime.ComputerDeck[0] = computerDeck; + + int extraLife = 0; + + if (questData.getInventory().getItemLevel("Gear") == 2) { + extraLife = 3; + } + + AllZone.getGameAction().newGame(humanDeck, computerDeck, + forge.quest.data.QuestUtil.getHumanExtraCards(questData, selectedQuest), new CardList(), + questData.getLife() + extraLife, selectedQuest.getAILife(), selectedQuest); + + } + + /** + *getMatchIcon.
+ * + * @return a {@link java.lang.String} object. + */ + String getMatchIcon() { + String oppIconName; + + if (isShowingQuests) { + PanelSingleQuest selectedQuest = (PanelSingleQuest) lastPanelSelected; + oppIconName = selectedQuest.getIconFilename(); + } else { + PanelSingleBattle selectedBattle = (PanelSingleBattle) lastPanelSelected; + oppIconName = selectedBattle.getIconFilename(); + } + return oppIconName; + } + + /** + *toggleBattleQuest.
+ * Toggles view between battle and quest screen. + */ + void toggleBattleQuest() { + if (isShowingQuests) { + isShowingQuests = false; + questButton.setText("Quests"); + } else { + isShowingQuests = true; + questButton.setText("Battles"); + } + + if (lastPanelSelected != null) { + lastPanelSelected.setSelected(false); + } + + lastPanelSelected = null; + + refresh(); + } + + /** + *SelectionAdapter
+ * Handles (de)selection of various SelectablePanel instances using lastPanelSelected field. + * Also toggles launch button after (de)selection. + * + */ + class SelectionAdapter extends MouseAdapter { + SelectablePanel targetPanel; + + SelectionAdapter(SelectablePanel sp) { + super(); + this.targetPanel = sp; + } + + @Override + public void mouseClicked(MouseEvent mouseEvent) { + // Deselect previous + if (lastPanelSelected != null) { + lastPanelSelected.setSelected(false); + } + + targetPanel.setSelected(true); + + lastPanelSelected = targetPanel; + playButton.setEnabled(canGameBeLaunched()); + } + + } + + /** + *moveDeckToTop.
+ * + * @param humanDeckName a {@link java.lang.String} object. + */ + private void moveDeckToTop(String humanDeckName) { + QuestMainPanel.lastUsedDeck = humanDeckName; + } + + + /** + *canGameBeLaunched.
+ * + * @return a boolean. + */ + boolean canGameBeLaunched() { + return !(NO_DECKS_AVAILABLE.equals(deckComboBox.getSelectedItem()) || lastPanelSelected == null); + } + + /** {@inheritDoc} */ + @Override + public void refreshState() { + this.refresh(); + } + +} diff --git a/src/test/java/forge/ReadQuestAssignmentTest.java b/src/test/java/forge/ReadQuestAssignmentTest.java deleted file mode 100644 index 9e426d40bbd..00000000000 --- a/src/test/java/forge/ReadQuestAssignmentTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package forge; - -import forge.error.ErrorViewer; -import forge.properties.ForgeProps; -import forge.properties.NewConstants; -import org.testng.annotations.Test; - -/** - * Created by IntelliJ IDEA. - * User: dhudson - */ -@Test(groups = {"UnitTest"}, timeOut = 1000) -public class ReadQuestAssignmentTest implements NewConstants { - /** - * - * - */ - @Test(groups = {"UnitTest", "fast"}, timeOut = 1000) - public void ReadQuestAssignmentTest1() { - try { - ReadQuest_Assignment read = new ReadQuest_Assignment(ForgeProps.getFile(QUEST.QUESTS), null); - - javax.swing.SwingUtilities.invokeAndWait(read); - // read.run(); - - Quest_Assignment qa[] = new Quest_Assignment[read.allQuests.size()]; - read.allQuests.toArray(qa); - for (int i = 0; i < qa.length; i++) { - System.out.println(qa[i].getId()); - System.out.println(qa[i].getName()); - System.out.println(qa[i].getDesc()); - System.out.println(qa[i].getDifficulty()); - System.out.println(qa[i].isRepeatable()); - System.out.println(qa[i].getRequiredNumberWins()); - } - } catch (Exception ex) { - ErrorViewer.showError(ex); - System.out.println("Error reading file " + ex); - } - } -}