merge latest trunk

This commit is contained in:
myk
2013-03-01 18:59:44 +00:00
12 changed files with 338 additions and 297 deletions

View File

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

12
pom.xml
View File

@@ -5,7 +5,7 @@
<artifactId>forge</artifactId>
<packaging>jar</packaging>
<name>Forge</name>
<version>1.3.9-SNAPSHOT</version>
<version>1.3.10-SNAPSHOT</version>
<description>
Forge lets you play the card game Magic: The Gathering against a computer opponent
using all of the rules.
@@ -211,7 +211,7 @@
<exportAntProperties>true</exportAntProperties>
<target>
<condition property="fullversionstring" value="${project.version}-r${forge.revision}${forge.specialStatus}" else="${project.version}-r${forge.revision}${forge.specialStatus} (mixed revisions detected; please update from the root directory)">
<contains string="${forge.mixedRevisions}" substring="false"/>
<contains string="${forge.mixedRevisions}" substring="false" />
</condition>
</target>
</configuration>
@@ -464,7 +464,7 @@
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
<ignore />
</action>
</pluginExecution>
<pluginExecution>
@@ -478,7 +478,7 @@
</goals>
</pluginExecutionFilter>
<action>
<execute/>
<execute />
</action>
</pluginExecution>
<pluginExecution>
@@ -491,7 +491,7 @@
</goals>
</pluginExecutionFilter>
<action>
<execute/>
<execute />
</action>
</pluginExecution>
<pluginExecution>
@@ -504,7 +504,7 @@
</goals>
</pluginExecutionFilter>
<action>
<execute/>
<execute />
</action>
</pluginExecution>
</pluginExecutions>

View File

@@ -186,13 +186,13 @@ while inputName != 'quit' :
if text.find("When CARDNAME enters the battlefield") != -1 :
print "\n"+text
print "<Trigger Script Start>"
print 'T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ <TriggerFunc> | TriggerDescription$ '+text
print 'T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ <TriggerFunc> | TriggerDescription$ '+text
print 'SVar:<TriggerFunc>:AB$ <Added Triggered Ability HERE>'
print "<Trigger Script End>\n"
elif text.find("When CARDNAME leaves the battlefield") != -1 :
print "\n"+text
print "<Trigger Script Start>"
print 'T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Creature.Self | Execute$ <TriggerFunc> | TriggerDescription$ '+text
print 'T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ <TriggerFunc> | TriggerDescription$ '+text
print 'SVar:<TriggerFunc>:AB$ <Added Triggered Ability HERE>'
print "<Trigger Script End>\n"
elif text.find("Unleash") != -1 :

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<Card> sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); }
public abstract Card chooseSingleCardForEffect(List<Card> 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);
}

View File

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

View File

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

View File

@@ -145,6 +145,7 @@ public class QuestEventManager {
return;
}
continue;
}
targetIdx -= opponents.size();

View File

@@ -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<CardStackRow> 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);
List<CardStackRow> template = tryArrangePilesOfWidth(lands, tokens, creatures, others);
deltaCardWidth = (cardWidth - lastGoodCardWidth) / 2;
if (workedLastTime) {
lastGoodCardWidth = cardWidth;
cardWidth += deltaCardWidth;
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<CardStackRow> 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<CardStackRow> tryArrangePilesOfWidth(final CardStackRow lands, final CardStackRow tokens, final CardStackRow creatures, CardStackRow others) {
List<CardStackRow> template = new ArrayList<PlayArea.CardStackRow>();
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<CardStackRow> storedRows = new ArrayList<CardStackRow>(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<CardStackRow> templateCopy = new ArrayList<CardStackRow>(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<CardStackRow> rows, final int insertIndex) {
// Won't modify the first parameter
private boolean planRow(final CardStackRow sourceRow, final List<CardStackRow> 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<CardStackRow> rows, final CardStackRow row) {
int rowWidth = row.getWidth();
private int planOthersRow(final List<CardStack> sourceRow, final int firstPile, final List<CardStackRow> template, final CardStackRow rowToFill) {
int rowWidth = rowToFill.getWidth();
final Iterator<CardStack> 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<CardPanel> 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);
}
}