diff --git a/CHANGES.txt b/CHANGES.txt
index 7363b03a87c..67d47ffd57f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,7 @@
-Forge Beta: 0#-##-2013 ver 1.3.9
+Forge Beta: 03-01-2013 ver 1.3.9
-12### cards in total.
+12193 cards in total.
Release Notes:
@@ -23,7 +23,7 @@ Work was also done on making the UI more keyboard-friendly. For example, the OK
Gatecrash Guild Sealed game mode has been added. To use it, start a new Sealed Mode Game, select "Block / Set" and "Gatecrash Guild Sealed". Select the first (default) configuration in the "Choose Set Combination" dialog, and when asked to pick your boosters, choose the guild you want twice (once for the guild-specific booster, and then for the extra promo cards).
The following cards are not included in the guild boosters of this game mode because they are not currently implemented in Forge: Bane Alley Broker, Bioshift, Killing Glare, Simic Manipulator.
-All Traditional sets are now up to 85% complete. Standard Format is supported at 99.19%. We are now at under 900 unsupported cards that are missing from Forge.
+All Traditional sets are now up to 85% complete. Standard Format is supported at 99.19%. We are now at under 800 unsupported cards that are missing from Forge.
A person reported "Love the game but I seem to be having a problem using a draft pool to start a quest. It works for sealed for me but when I select the draft deck option it's always blank even if I have one or several drafts completed." This should now be fixed and draft decks should now show up in quest start combobox.
@@ -34,151 +34,155 @@ Our snapshot and beta releases should now display the correct SVN revision numbe
New Cards:
-Ghosts of the Innocent
-Pendrell Flux
-Disruption Aura
-Library of Leng
-Temporal Extortion
+Archery Training
+Aurelia's Fury
Aven Shrine
+Barrin's Spite
+Battletide Alchemist
+Bioshift
+Blind Seer
+Blinding Powder
Bloom Tender
Bomb Squad
+Bounty of the Hunt
+Builder's Bane
Cabal Shrine
+Cannibalize
+Cephalid Shrine
Chant of Vitu-Ghazi
+Chaoslace
+Charm Peddler
+Circle of Despair
+Cleansing Meditation
+Common Cause
+Conflagrate
+Conjurer's Ban
+Cornered Market
Covenant of Minds
Crashing Boars
Crush Underfoot
+Cryptic Gateway
+Deathlace
+Deepwood Elder
Desecrator Hag
+Disruption Aura
+Duplicity
Dwarven Shrine
+Eight-and-a-Half-Tails
+Embolden
+Endemic Plague
+Epochrasite
+Ersatz Gnomes
Eye for an Eye
+Eye of Singularity
+Eye of Yawgmoth
+Feint
+Fiery Bombardment
+Fiery Justice
+Fire and Brimstone
+Fire Covenant
+Flash
+Flickerform
+Forbidden Crypt
+Forked Lightning
+Frostwielder
+Game Preserve
+Gargantuan Gorilla
+Ghosts of the Innocent
+Glamer Spinners
+Guard Dogs
+Hail of Arrows
+Heartseeker
+Heroic Defiance
Hint of Insanity
Holistic Wisdom
Infectious Rage
-Knowledge Exploitation
-Lightning Dart
-Memory Crystal
-Mist of Stagnation
-Moonring Mirror
-Nantuko Shrine
-Phyrexian Purge
-Planeswalker's Mischief
-Protean Hulk
-Purgatory
-Quenchable Fire
-Retribution
-Reviving Vapors
-Reweave
-Sapphire Drake
-Searing Rays
-Sphinx Ambassador
-Talara's Bane
-Thelon of Havenwood
-Mist of Stagnation
-Unforge
-Warren Weirding
-Worldpurge
-Flash
-Forked Lightning
-Embolden
+Infernal Harvest
+Invoke Prejudice
+Jaded Response
Jaws of Stone
+Killing Glare
+Knollspine Invocation
+Knowledge Exploitation
+Kumano's Blessing
+Kumano's Pupils
+Kumano, Master Yamabushi
+Leonin Bola
+Library of Leng
+Lifelace
+Light from Within
+Lightning Dart
Living Inferno
Magmatic Core
-Remedy
-Rolling Thunder
-Aurelia's Fury
-Pollen Remedy
-Conflagrate
-Hail of Arrows
-Fire Covenant
-Meteor Shower
-Infernal Harvest
-Rock Slide
-Serra's Hymn
-Volcanic Wind
-Spoils of War
-Fiery Justice
-Bounty of the Hunt
-Shambling Swarm
-Fire and Brimstone
-Marble Priest
-Talruum Piper
-Endemic Plague
-Feint
-Reincarnation
-Killing Glare
-Gargantuan Gorilla
-Psychic Allergy
-Invoke Prejudice
-Conjurer's Ban
-Cephalid Shrine
-Bioshift
-Glamer Spinners
-Mark of Eviction
-Plague Boiler
-Razia's Purification
-Reins of the Vinesteed
-Shared Animosity
-Simic Guildmage
-Rite of Ruin
-Common Cause
-Sabertooth Cobra
-Archery Training
-Barrin's Spite
-Cryptic Gateway
-Deepwood Elder
-Knollspine Invocation
-Eye of Yawgmoth
-Spike Cannibal
-Flickerform
-Epochrasite
-Eye of Singularity
-Heroic Defiance
-Forbidden Crypt
-Charm Peddler
-Heartseeker
-Blinding Powder
-Leonin Bola
-Razor Boomerang
-Surestrike Trident
-Sunforger
-Circle of Despair
-Martyr's Cause
-Cannibalize
-Shuriken
-Rally the Horde
-Deathlace
Mana Vapors
-Samite Elder
-Struggle for Sanity
-Takeno, Samurai General
-Thran Tome
-Chaoslace
-Lifelace
-Moonlace
-Purelace
-Thoughtlace
-Eight-and-a-Half-Tails
-Pious Kitsune
-Blind Seer
-Ersatz Gnomes
-Vodalian Mystic
-Light from Within
-Fiery Bombardment
-Phosphorescent Feast
-Guard Dogs
-Jaded Response
-Tainted Pact
-Pledge of Loyalty
+Marble Priest
+Mark of Eviction
+Martyr's Cause
+Memory Crystal
+Meteor Shower
Mirror Golem
-Reverent Mantra
-Frostwielder
-Kumano, Master Yamabushi
-Kumano's Pupils
-Kumano's Blessing
-Winnow
-Cornered Market
+Mist of Stagnation
+Mist of Stagnation
+Moonlace
+Moonring Mirror
+Nantuko Shrine
Not of This World
-Duplicity
+Pendrell Flux
+Phosphorescent Feast
+Phyrexian Purge
+Pious Kitsune
+Plague Boiler
+Planeswalker's Mischief
+Pledge of Loyalty
+Pollen Remedy
+Protean Hulk
+Psychic Allergy
+Purelace
+Purgatory
+Quenchable Fire
+Rally the Horde
+Razia's Purification
+Razor Boomerang
+Reincarnation
+Reins of the Vinesteed
+Remedy
+Retribution
+Reverent Mantra
+Reviving Vapors
+Reweave
+Rite of Ruin
+Rock Slide
+Rolling Thunder
+Sabertooth Cobra
+Samite Elder
+Sapphire Drake
+Searing Rays
+Serra's Hymn
+Shambling Swarm
+Shared Animosity
+Shuriken
+Simic Guildmage
+Sphinx Ambassador
+Spike Cannibal
+Spoils of War
+Struggle for Sanity
+Sunforger
+Surestrike Trident
+Tainted Pact
+Takeno, Samurai General
+Talara's Bane
+Talruum Piper
+Temporal Extortion
+Thelon of Havenwood
Thought Gorger
+Thoughtlace
+Thran Tome
+Unforge
+Vodalian Mystic
+Volcanic Wind
+Warren Weirding
+Winnow
+Worldpurge
New Phenomenons:
@@ -189,23 +193,25 @@ Planewide Disaster
New Planes:
-Undercity Reaches
Aretopolis
+Undercity Reaches
New Vanguard Avatars:
+Arcanis the Omnipotent Avatar
+Bosh, Iron Golem Avatar
Figure of Destiny Avatar
+Haakon Stromgald Scourge Avatar
+Jaya Ballard Avatar
+Maro Avatar
+Master of the Wild Hunt Avatar
+Necropotence Avatar
Sisters of Stone Death Avatar
Stuffy Doll Avatar
Two Headed Giant of Foriys Avatar
-Viridian Zealot Avatar
-Bosh, Iron Golem Avatar
-Maro Avatar
-Necropotence Avatar
Vampire Nocturnus Avatar
-Arcanis the Omnipotent Avatar
-Master of the Wild Hunt Avatar
+Viridian Zealot Avatar
Known Issues:
diff --git a/pom.xml b/pom.xml
index 494f74a1f00..349137aef61 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
forge
jar
Forge
- 1.3.9-SNAPSHOT
+ 1.3.10-SNAPSHOT
Forge lets you play the card game Magic: The Gathering against a computer opponent
using all of the rules.
@@ -211,7 +211,7 @@
true
-
+
@@ -464,7 +464,7 @@
-
+
@@ -478,7 +478,7 @@
-
+
@@ -491,7 +491,7 @@
-
+
@@ -504,7 +504,7 @@
-
+
diff --git a/res/cardTemplateScript.py b/res/cardTemplateScript.py
index 159b1861a73..ae05443e019 100755
--- a/res/cardTemplateScript.py
+++ b/res/cardTemplateScript.py
@@ -186,13 +186,13 @@ while inputName != 'quit' :
if text.find("When CARDNAME enters the battlefield") != -1 :
print "\n"+text
print ""
- print 'T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ | TriggerDescription$ '+text
+ print 'T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ | TriggerDescription$ '+text
print 'SVar::AB$ '
print "\n"
elif text.find("When CARDNAME leaves the battlefield") != -1 :
print "\n"+text
print ""
- print 'T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Creature.Self | Execute$ | TriggerDescription$ '+text
+ print 'T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ | TriggerDescription$ '+text
print 'SVar::AB$ '
print "\n"
elif text.find("Unleash") != -1 :
diff --git a/res/cardsfolder/a/astral_slide.txt b/res/cardsfolder/a/astral_slide.txt
index 804e3517424..d23e266dc02 100644
--- a/res/cardsfolder/a/astral_slide.txt
+++ b/res/cardsfolder/a/astral_slide.txt
@@ -2,9 +2,12 @@ Name:Astral Slide
ManaCost:2 W
Types:Enchantment
T:Mode$ Cycled | ValidCard$ Card | Execute$ TrigExile | TriggerZones$ Battlefield | OptionalDecider$ You | TriggerDescription$ Whenever a player cycles a card, you may exile target creature. If you do, return the exiled card to the battlefield under its owner's control at the beginning of the next end step.
-SVar:TrigExile:AB$ ChangeZone | Cost$ 0 | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | RememberTargets$ True | ForgetOtherTargets$ True | SubAbility$ DelTrig
-SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigBounce | TriggerDescription$ Return exiled creature to the battlefield.
-SVar:TrigBounce:AB$ ChangeZone | Cost$ 0 | Origin$ Exile | Destination$ Battlefield | Defined$ Remembered
+SVar:TrigExile:AB$ ChangeZone | Cost$ 0 | ValidTgts$ Creature | TgtPrompt$ Select target creature | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DelTrigEffect
+SVar:DelTrigEffect:DB$ Effect | Triggers$ EOTTrig | SVars$ TrigBounce,RemoveEffect | RememberObjects$ Remembered | SubAbility$ DBCleanup | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1
+SVar:EOTTrig:Mode$ Phase | Phase$ End of Turn | Execute$ TrigBounce | TriggerZones$ Command | TriggerDescription$ Return exiled creature to the battlefield.
+SVar:TrigBounce:AB$ ChangeZone | Cost$ 0 | Origin$ Exile | Destination$ Battlefield | Defined$ Remembered | SubAbility$ RemoveEffect
+SVar:RemoveEffect:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:RemAIDeck:True
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/astral_slide.jpg
diff --git a/res/cardsfolder/x/xantid_swarm.txt b/res/cardsfolder/x/xantid_swarm.txt
index 3bb19ca316b..3f80ec07854 100644
--- a/res/cardsfolder/x/xantid_swarm.txt
+++ b/res/cardsfolder/x/xantid_swarm.txt
@@ -4,8 +4,9 @@ Types:Creature Insect
PT:0/1
K:Flying
T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ When CARDNAME attacks, defending player can't cast spells this turn.
-SVar:TrigEffect:AB$ Effect | Cost$ 0 | Name$ Xantid Swarm Effect | StaticAbilities$ CantBeCast
-SVar:CantBeCast:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card | Caster$ DefendingPlayer | Description$ Defending player can't cast spells.
+SVar:TrigEffect:AB$ Effect | Cost$ 0 | Name$ Xantid Swarm Effect | RememberObjects$ DefendingPlayer | StaticAbilities$ CantBeCast | SubAbility$ DBCleanup
+SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
+SVar:CantBeCast:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card | Caster$ Player.IsRemembered | Description$ Defending player can't cast spells.
SVar:RemRandomDeck:True
SVar:HasAttackEffect:TRUE
SVar:Rarity:Rare
diff --git a/res/quest/quest-opponent-icons.txt b/res/quest/quest-opponent-icons.txt
index 83ecac9f885..9932bb93404 100644
--- a/res/quest/quest-opponent-icons.txt
+++ b/res/quest/quest-opponent-icons.txt
@@ -293,3 +293,37 @@ http://www.cardforge.org/fpics/questAvatars/WereHyena.jpg
http://www.cardforge.org/fpics/questAvatars/WitchDoctor.jpg
http://www.cardforge.org/fpics/questAvatars/Yemaya.jpg
http://www.cardforge.org/fpics/questAvatars/Yewa.jpg
+#RAVNICA WORLD ICONS
+http://www.cardforge.org/fpics/questAvatars/Agrus.jpg
+http://www.cardforge.org/fpics/questAvatars/Aurelia.jpg
+http://www.cardforge.org/fpics/questAvatars/Azorius-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Bep.jpg
+http://www.cardforge.org/fpics/questAvatars/Borborygmos.jpg
+http://www.cardforge.org/fpics/questAvatars/Boros-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Caprio.jpg
+http://www.cardforge.org/fpics/questAvatars/Ghost%20Council.jpg
+http://www.cardforge.org/fpics/questAvatars/Golgari-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Gruul-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Hameln.jpg
+http://www.cardforge.org/fpics/questAvatars/Isperia.jpg
+http://www.cardforge.org/fpics/questAvatars/Izzet-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Jarad.jpg
+http://www.cardforge.org/fpics/questAvatars/Kraj.jpg
+http://www.cardforge.org/fpics/questAvatars/Lazav.jpg
+http://www.cardforge.org/fpics/questAvatars/Lyzolda.jpg
+http://www.cardforge.org/fpics/questAvatars/Momir.jpg
+http://www.cardforge.org/fpics/questAvatars/Niv-Mizzet.jpg
+http://www.cardforge.org/fpics/questAvatars/Orzhov-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Rakdos.jpg
+http://www.cardforge.org/fpics/questAvatars/Savra.jpg
+http://www.cardforge.org/fpics/questAvatars/Selesnya-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Simic-precon.jpg
+http://www.cardforge.org/fpics/questAvatars/Sisters%20of%20Stone%20Death.jpg
+http://www.cardforge.org/fpics/questAvatars/Sus Antigoon.jpg
+http://www.cardforge.org/fpics/questAvatars/Szadek.jpg
+http://www.cardforge.org/fpics/questAvatars/Teysa.jpg
+http://www.cardforge.org/fpics/questAvatars/Token.jpg
+http://www.cardforge.org/fpics/questAvatars/Tolsimir.jpg
+http://www.cardforge.org/fpics/questAvatars/Trostani.jpg
+http://www.cardforge.org/fpics/questAvatars/Ulasht.jpg
+http://www.cardforge.org/fpics/questAvatars/Zegana.jpg
diff --git a/src/main/java/forge/deck/CardPool.java b/src/main/java/forge/deck/CardPool.java
index 115585b39ef..1c307ef6cb2 100644
--- a/src/main/java/forge/deck/CardPool.java
+++ b/src/main/java/forge/deck/CardPool.java
@@ -19,6 +19,7 @@ package forge.deck;
import java.util.List;
import java.util.Map.Entry;
+import java.util.NoSuchElementException;
import forge.Card;
@@ -139,7 +140,7 @@ public class CardPool extends ItemPool {
if ( cp != null)
this.add(cp);
else
- throw new RuntimeException(String.format("Card %s is not supported by Forge, as it's neither a known common card nor one of casual variants' card.", cardName));
+ throw new NoSuchElementException(String.format("Card %s is not supported by Forge, as it's neither a known common card nor one of casual variants' card.", cardName));
}
/**
diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java
index 32260cd63a8..ec4e3a61669 100644
--- a/src/main/java/forge/game/player/PlayerController.java
+++ b/src/main/java/forge/game/player/PlayerController.java
@@ -65,7 +65,7 @@ public abstract class PlayerController {
*/
public void passPriority() {
PhaseHandler handler = game.getPhaseHandler();
- // may pass only priority is has
+
if ( handler.getPriorityPlayer() == getPlayer() )
game.getPhaseHandler().passPriority();
}
@@ -95,5 +95,6 @@ public abstract class PlayerController {
public Card chooseSingleCardForEffect(List sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); }
public abstract Card chooseSingleCardForEffect(List sourceList, SpellAbility sa, String title, boolean isOptional);
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);
}
diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java
index ae0f2c0ecc1..ef9bd250e0b 100644
--- a/src/main/java/forge/game/player/PlayerControllerAi.java
+++ b/src/main/java/forge/game/player/PlayerControllerAi.java
@@ -198,6 +198,10 @@ public class PlayerControllerAi extends PlayerController {
return brains.confirmAction(sa, mode, message);
}
+ @Override
+ public boolean getWillPlayOnFirstTurn(String message) {
+ return true; // AI is brave :)
+ }
@Override
public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) {
return brains.confirmStaticApplication(hostCard, affected, logic, message);
diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java
index 0131af3b94a..cd2787e786e 100644
--- a/src/main/java/forge/game/player/PlayerControllerHuman.java
+++ b/src/main/java/forge/game/player/PlayerControllerHuman.java
@@ -250,5 +250,14 @@ public class PlayerControllerHuman extends PlayerController {
return GuiDialog.confirm(hostCard, message);
}
+ @Override
+ public boolean getWillPlayOnFirstTurn(String message) {
+ final String[] possibleValues = { "Play", "Draw" };
+ final Object playDraw = JOptionPane.showOptionDialog(null, message + "\n\nWould you like to play or draw?",
+ "Play or Draw?", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null,
+ possibleValues, possibleValues[0]);
+
+ return !playDraw.equals(1);
+ }
}
diff --git a/src/main/java/forge/quest/QuestEventManager.java b/src/main/java/forge/quest/QuestEventManager.java
index b56c243eda0..ada8d550f4a 100644
--- a/src/main/java/forge/quest/QuestEventManager.java
+++ b/src/main/java/forge/quest/QuestEventManager.java
@@ -144,7 +144,8 @@ public class QuestEventManager {
outList.add(duel);
return;
}
-
+
+ continue;
}
targetIdx -= opponents.size();
diff --git a/src/main/java/forge/view/arcane/PlayArea.java b/src/main/java/forge/view/arcane/PlayArea.java
index 3f60b16ff68..3b878e19d3f 100644
--- a/src/main/java/forge/view/arcane/PlayArea.java
+++ b/src/main/java/forge/view/arcane/PlayArea.java
@@ -23,11 +23,9 @@ import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
import javax.swing.JScrollPane;
-
import forge.Card;
import forge.view.arcane.util.CardPanelMouseListener;
@@ -76,6 +74,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
* a {@link javax.swing.JScrollPane} object.
* @param mirror
* a boolean.
+ * @param modelRef
*/
public PlayArea(final JScrollPane scrollPane, final boolean mirror) {
super(scrollPane);
@@ -196,46 +195,40 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
this.playAreaWidth = rect.width;
this.playAreaHeight = rect.height;
- final CardStackRow allLands = collectAllLands();
- final CardStackRow allTokens = collectAllTokens();
- final CardStackRow allCreatures = new CardStackRow(this.getCardPanels(), RowType.creatureNonToken);
- final CardStackRow allOthers = new CardStackRow(this.getCardPanels(), RowType.other);
+ final CardStackRow lands = collectAllLands();
+ final CardStackRow tokens = collectAllTokens();
+ final CardStackRow creatures = new CardStackRow(this.getCardPanels(), RowType.CreatureNonToken);
+ final CardStackRow others = new CardStackRow(this.getCardPanels(), RowType.Other);
// should find an appropriate width of card
- this.cardWidth = this.getCardWidthMax();
int maxCardWidth = this.getCardWidthMax();
+ setCardWidth(maxCardWidth);
int minCardWidth = this.getCardWidthMin();
int lastGoodCardWidth = minCardWidth;
int deltaCardWidth = (maxCardWidth - minCardWidth) / 2;
- boolean workedLastTime = false;
- //boolean isFirstRun = true;
+ List lastTemplate = null;
while (deltaCardWidth > 0) {
- final CardStackRow creatures = (CardStackRow) allCreatures.clone();
- final CardStackRow tokens = (CardStackRow) allTokens.clone();
- final CardStackRow lands = (CardStackRow) allLands.clone();
- CardStackRow others = (CardStackRow) allOthers.clone();
- workedLastTime = canAdjustWidth(lands, tokens, creatures, others);
-
- deltaCardWidth = (cardWidth - lastGoodCardWidth) / 2;
- if (workedLastTime) {
- lastGoodCardWidth = cardWidth;
- cardWidth += deltaCardWidth;
+ List template = tryArrangePilesOfWidth(lands, tokens, creatures, others);
+
+ deltaCardWidth = (getCardWidth() - lastGoodCardWidth) / 2;
+ if (template != null) {
+ lastTemplate = template;
+ lastGoodCardWidth = getCardWidth();
+ setCardWidth(getCardWidth() + deltaCardWidth);
if (lastGoodCardWidth == maxCardWidth) {
break;
}
}
else {
- cardWidth -= deltaCardWidth;
+ setCardWidth(getCardWidth() - deltaCardWidth);
}
}
- cardWidth = lastGoodCardWidth;
- final CardStackRow creatures = (CardStackRow) allCreatures.clone();
- final CardStackRow tokens = (CardStackRow) allTokens.clone();
- final CardStackRow lands = (CardStackRow) allLands.clone();
- CardStackRow others = (CardStackRow) allOthers.clone();
- workedLastTime = canAdjustWidth(lands, tokens, creatures, others);
+ setCardWidth(lastGoodCardWidth);
+ if ( null == lastTemplate )
+ lastTemplate = tryArrangePilesOfWidth(lands, tokens, creatures, others);
+ this.rows = lastTemplate;
// Get size of all the rows.
int x, y = PlayArea.GUTTER_Y;
int maxRowWidth = 0;
@@ -252,25 +245,26 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
}
this.setPreferredSize(new Dimension(maxRowWidth - this.cardSpacingX, y - this.cardSpacingY));
this.revalidate();
- positionAllCards();
+ positionAllCards(lastTemplate);
}
- private void positionAllCards() {
+ private void positionAllCards(List template) {
// Position all card panels.
int x = 0;
int y = PlayArea.GUTTER_Y;
- for (final CardStackRow row : this.rows) {
+ for (final CardStackRow row : template) {
int rowBottom = 0;
x = PlayArea.GUTTER_X;
for (int stackIndex = 0, stackCount = row.size(); stackIndex < stackCount; stackIndex++) {
final CardStack stack = row.get(stackIndex);
// Align others to the right.
- if (RowType.other.isType(stack.get(0).getGameCard())) {
+ if (RowType.Other.isGoodFor(stack.get(0).getGameCard())) {
x = (this.playAreaWidth - PlayArea.GUTTER_X) + this.extraCardSpacingX;
for (int i = stackIndex, n = row.size(); i < n; i++) {
- x -= row.get(i).getWidth();
+ CardStack r = row.get(i);
+ x -= r.getWidth();
}
}
for (int panelIndex = 0, panelCount = stack.size(); panelIndex < panelCount; panelIndex++) {
@@ -279,68 +273,62 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
this.setComponentZOrder(panel, panelIndex);
final int panelX = x + (stackPosition * this.stackSpacingX);
final int panelY = y + (stackPosition * this.stackSpacingY);
- panel.setCardBounds(panelX, panelY, this.cardWidth, this.cardHeight);
+ panel.setCardBounds(panelX, panelY, this.getCardWidth(), this.cardHeight);
}
rowBottom = Math.max(rowBottom, y + stack.getHeight());
- x += stack.getWidth();
+ x += stack.getWidth() + cardSpacingX;
}
y = rowBottom;
}
}
- private boolean canAdjustWidth(final CardStackRow lands, final CardStackRow tokens, final CardStackRow creatures, CardStackRow others) {
- this.rows.clear();
- this.cardHeight = Math.round(this.cardWidth * CardPanel.ASPECT_RATIO);
- this.extraCardSpacingX = Math.round(this.cardWidth * PlayArea.EXTRA_CARD_SPACING_X);
- this.cardSpacingX = (this.cardHeight - this.cardWidth) + this.extraCardSpacingX;
- this.cardSpacingY = Math.round(this.cardHeight * PlayArea.CARD_SPACING_Y);
- this.stackSpacingX = Math.round(this.cardWidth * PlayArea.STACK_SPACING_X);
- this.stackSpacingY = Math.round(this.cardHeight * PlayArea.STACK_SPACING_Y);
-
+ private List tryArrangePilesOfWidth(final CardStackRow lands, final CardStackRow tokens, final CardStackRow creatures, CardStackRow others) {
+ List template = new ArrayList();
+
int afterFirstRow;
+ boolean landsFit, tokensFit, creaturesFit;
if (this.mirror) {
// Wrap all creatures and lands.
- this.wrap(lands, this.rows, -1);
- afterFirstRow = this.rows.size();
- this.wrap(tokens, this.rows, afterFirstRow);
- this.wrap(creatures, this.rows, this.rows.size());
+ landsFit = this.planRow(lands, template, -1);
+ afterFirstRow = template.size();
+ tokensFit = this.planRow(tokens, template, afterFirstRow);
+ creaturesFit = this.planRow(creatures, template, template.size());
} else {
// Wrap all creatures and lands.
- this.wrap(creatures, this.rows, -1);
- afterFirstRow = this.rows.size();
- this.wrap(tokens, this.rows, afterFirstRow);
- this.wrap(lands, this.rows, this.rows.size());
+ creaturesFit = this.planRow(creatures, template, -1);
+ afterFirstRow = template.size();
+ tokensFit = this.planRow(tokens, template, afterFirstRow);
+ landsFit = this.planRow(lands, template, template.size());
}
- // Store the current rows and others.
- final List storedRows = new ArrayList(this.rows.size());
- for (final CardStackRow row : this.rows) {
- try {
- storedRows.add((CardStackRow) row.clone());
- }
- catch (NullPointerException e) {
- System.out.println("Null pointer exception in Row Spacing. Possibly also part of the issue.");
- }
+
+ if ( !landsFit || !creaturesFit || !tokensFit )
+ return null;
+
+ // Other cards may be stored at end of usual rows or on their own row.
+ int cntOthers = others.size();
+
+ // Copy the template for the case 1st approach won't work
+ final List templateCopy = new ArrayList(template.size());
+ for (final CardStackRow row : template) {
+ templateCopy.add((CardStackRow) row.clone());
}
- final CardStackRow storedOthers = (CardStackRow) others.clone();
+
// Fill in all rows with others.
- for (final CardStackRow row : this.rows) {
- this.fillRow(others, this.rows, row);
+ int nextOther = 0;
+ for (final CardStackRow row : template) {
+ nextOther = this.planOthersRow(others, nextOther, template, row);
+ if ( nextOther == cntOthers )
+ return template; // everything was successfully placed
}
- // Stop if everything fits, otherwise revert back to the stored
- // values.
- if (creatures.isEmpty() && tokens.isEmpty() && lands.isEmpty() && others.isEmpty()) {
- return true;
- }
- this.rows = storedRows;
- others = storedOthers;
- // Try to put others on their own row(s) and fill in the rest.
- this.wrap(others, this.rows, afterFirstRow);
- for (final CardStackRow row : this.rows) {
- this.fillRow(others, this.rows, row);
- }
- // If that still doesn't fit, scale down.
- return creatures.isEmpty() && tokens.isEmpty() && lands.isEmpty() && others.isEmpty();
+
+ template = templateCopy;
+ // Try to put others on their own row(s)
+ if ( this.planRow(others, template, afterFirstRow) )
+ return template;
+
+
+ return null; // Cannot fit everything with that width;
}
/**
@@ -350,69 +338,53 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
*
* @param sourceRow
* a {@link forge.view.arcane.PlayArea.CardStackRow} object.
- * @param rows
+ * @param template
* a {@link java.util.List} object.
* @param insertIndex
* a int.
* @return a int.
*/
-// private int cntRepaints = 0;
- private int wrap(final CardStackRow sourceRow, final List rows, final int insertIndex) {
+ // Won't modify the first parameter
+ private boolean planRow(final CardStackRow sourceRow, final List template, final int insertIndex) {
// The cards are sure to fit (with vertical scrolling) at the minimum
// card width.
- final boolean allowHeightOverflow = this.cardWidth == this.getCardWidthMin();
+ final boolean isMinimalSize = this.getCardWidth() == this.getCardWidthMin();
// System.err.format("[%d] @ %d - Repaint playarea - %s %n", new Date().getTime(), cntRepaints++, mirror ? "MIRROR" : "DIRECT");
CardStackRow currentRow = new CardStackRow();
- for (int i = 0, n = sourceRow.size() - 1; i <= n; i++) {
- final CardStack stack = sourceRow.get(i);
- // If the row is not empty and this stack doesn't fit, add the row.
+ for (final CardStack stack : sourceRow) {
final int rowWidth = currentRow.getWidth();
- if (!currentRow.isEmpty() && ((rowWidth + stack.getWidth()) > this.playAreaWidth)) {
+ // If the row is not empty and this stack doesn't fit, add the row.
+ if (rowWidth + stack.getWidth() > this.playAreaWidth && !currentRow.isEmpty() ) {
+
// Stop processing if the row is too wide or tall.
- if (!allowHeightOverflow && (rowWidth > this.playAreaWidth)) {
- break;
- }
- if (!allowHeightOverflow && ((this.getRowsHeight(rows) + sourceRow.getHeight()) > this.playAreaHeight)) {
- break;
- }
- try {
- rows.add(insertIndex == -1 ? rows.size() : insertIndex, currentRow);
- }
- catch (ArrayIndexOutOfBoundsException e) {
- System.out.println("ArrayIndex Out of Bounds when trying to add row in PlayArea. Someone fix this logic, "
- + " I believe it causes the no cards loading in issue we've noticed.");
- // TODO: There's a crash here, maybe when rows == [null] and currentRow == [[Plant Wall]] and insertIndex is 0
+ if (rowWidth > this.playAreaWidth || this.getRowsHeight(template) + sourceRow.getHeight() > this.playAreaHeight) {
+ if ( !isMinimalSize )
+ return false;
}
+
+ if ( insertIndex == -1)
+ template.add(currentRow);
+ else
+ template.add(insertIndex, currentRow);
+
currentRow = new CardStackRow();
}
+
currentRow.add(stack);
}
// Add the last row if it is not empty and it fits.
if (!currentRow.isEmpty()) {
final int rowWidth = currentRow.getWidth();
- if (allowHeightOverflow
- || (rowWidth <= this.playAreaWidth)
- && (allowHeightOverflow || ((this.getRowsHeight(rows) + sourceRow.getHeight()) <= this.playAreaHeight))) {
- try {
- rows.add(insertIndex == -1 ? rows.size() : insertIndex, currentRow);
- }
- catch (ArrayIndexOutOfBoundsException e) {
- System.out.println("ArrayIndex Out of Bounds when trying to add row in PlayArea. Someone fix this logic, "
- + " I believe it causes the no cards loading in issue we've noticed.");
- // TODO: There's a crash here, maybe when rows == [null] and currentRow == [[Plant Wall]] and insertIndex is 0
- }
- }
+ if (isMinimalSize || rowWidth <= this.playAreaWidth && this.getRowsHeight(template) + sourceRow.getHeight() <= this.playAreaHeight) {
+ if ( insertIndex == -1)
+ template.add(currentRow);
+ else
+ template.add(insertIndex, currentRow);
+ } else return false;
}
- // Remove the wrapped stacks from the source row.
- for (int iRow = 0; iRow < rows.size(); iRow++) {
- CardStackRow row = rows.get(iRow);
- if (row != null) {
- sourceRow.removeAll(row);
- }
- }
- return insertIndex;
+ return true;
}
@@ -423,32 +395,30 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
*
* @param sourceRow
* a {@link forge.view.arcane.PlayArea.CardStackRow} object.
- * @param rows
+ * @param template
* a {@link java.util.List} object.
- * @param rows
+ * @param template
* a {@link java.util.List} object.
- * @param row
+ * @param rowToFill
* a {@link forge.view.arcane.PlayArea.CardStackRow} object.
*/
- private void fillRow(final CardStackRow sourceRow, final List rows, final CardStackRow row) {
- int rowWidth = row.getWidth();
+ private int planOthersRow(final List sourceRow, final int firstPile, final List template, final CardStackRow rowToFill) {
+ int rowWidth = rowToFill.getWidth();
- final Iterator itr = sourceRow.iterator();
-
- while (itr.hasNext()) {
- final CardStack stack = itr.next();
+ for (int i = firstPile; i < sourceRow.size(); i++ ) {
+ CardStack stack = sourceRow.get(i);
rowWidth += stack.getWidth();
- if (rowWidth > this.playAreaWidth) {
- break;
+ if (rowWidth > this.playAreaWidth) return i; // cannot add any more piles in a row
+
+ if (stack.getHeight() > rowToFill.getHeight()) { // if row becomes taller
+ int newAllRowsHeight = this.getRowsHeight(template) - rowToFill.getHeight() + stack.getHeight();
+ if ( newAllRowsHeight > this.playAreaHeight)
+ return i; // refuse to add here because it won't fit in height
}
- if (stack.getHeight() > row.getHeight()
- && (((this.getRowsHeight(rows) - row.getHeight()) + stack.getHeight()) > this.playAreaHeight)) {
- break;
- }
- row.add(stack);
- itr.remove();
+ rowToFill.add(stack);
}
+ return sourceRow.size();
}
/**
@@ -509,20 +479,18 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
}
private static enum RowType {
- land, creature, creatureNonToken, other;
+ Land,
+ Creature,
+ CreatureNonToken,
+ Other;
- public boolean isType(final Card card) {
+ public boolean isGoodFor(final Card card) {
switch (this) {
- case land:
- return card.isLand();
- case creature:
- return card.isCreature();
- case creatureNonToken:
- return card.isCreature() && !card.isToken();
- case other:
- return !card.isLand() && !card.isCreature();
- default:
- throw new RuntimeException("Unhandled type: " + this);
+ case Land: return card.isLand();
+ case Creature: return card.isCreature();
+ case CreatureNonToken: return card.isCreature() && !card.isToken();
+ case Other: return !card.isLand() && !card.isCreature();
+ default: throw new RuntimeException("Unhandled type: " + this);
}
}
}
@@ -541,7 +509,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
private void addAll(final List cardPanels, final RowType type) {
for (final CardPanel panel : cardPanels) {
- if (!type.isType(panel.getGameCard()) || (panel.getAttachedToPanel() != null)) {
+ if (!type.isGoodFor(panel.getGameCard()) || (panel.getAttachedToPanel() != null)) {
continue;
}
final CardStack stack = new CardStack();
@@ -606,4 +574,17 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
+ PlayArea.this.cardSpacingY;
}
}
+ private int getCardWidth() {
+ return cardWidth;
+ }
+
+ private void setCardWidth(int cardWidth0) {
+ this.cardWidth = cardWidth0;
+ this.cardHeight = Math.round(this.cardWidth * CardPanel.ASPECT_RATIO);
+ this.extraCardSpacingX = Math.round(this.cardWidth * PlayArea.EXTRA_CARD_SPACING_X);
+ this.cardSpacingX = (this.cardHeight - this.cardWidth) + this.extraCardSpacingX;
+ this.cardSpacingY = Math.round(this.cardHeight * PlayArea.CARD_SPACING_Y);
+ this.stackSpacingX = Math.round(this.cardWidth * PlayArea.STACK_SPACING_X);
+ this.stackSpacingY = Math.round(this.cardHeight * PlayArea.STACK_SPACING_Y);
+ }
}