diff --git a/.gitattributes b/.gitattributes index d0cd306df1e..a2d664be4c6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14220,9 +14220,9 @@ src/main/java/forge/gui/GuiChoose.java -text src/main/java/forge/gui/GuiDialog.java -text src/main/java/forge/gui/GuiDisplayUtil.java svneol=native#text/plain src/main/java/forge/gui/GuiImportPicture.java svneol=native#text/plain -src/main/java/forge/gui/GuiInput.java svneol=native#text/plain src/main/java/forge/gui/GuiProgressBarWindow.java svneol=native#text/plain src/main/java/forge/gui/GuiUtils.java svneol=native#text/plain +src/main/java/forge/gui/InputProxy.java svneol=native#text/plain 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 diff --git a/CHANGES.txt b/CHANGES.txt index 67d47ffd57f..985c4d0c695 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,217 +1,36 @@ -Forge Beta: 03-01-2013 ver 1.3.9 +Forge Beta: 03-##-2013 ver 1.3.10 -12193 cards in total. +12### cards in total. Release Notes: -You can now cast cards for it's miracle cost without getting a crash. Blocking with a creature was not resetting after combat and this would prevent this creature from blocking in subsequent turns, now fixed. -Work continues on the quest worlds format and a new world based on Ravnica is coming along nicely. - -Added a 'copy to clipboard' button on WinLose screen so players can easily copy the game log. - -Find-as-you-type is now implemented for Deck Editor tables. Just start typing while the table has focus and the next card with a matching string in its name will be highlighted. If more than one card matches, hit Enter to select the next matching card. A popup panel will appear with the search string so you know what you are searching for. If no cards match the string, the string will be highlighted in red. Normally, if you hit the spacebar while a card is selected in one table, the card will be moved to the other table (catalog/deck). When the popup is displayed, space characters are interpreted as part of the search string. Find-as-you-type mode is automatically exited after 5 seconds of inactivity, or hit Escape to exit find-as-you-type mode immediately. - -The Deck Editor has also gained some hotkey and context menu abilities. R-click on a card (or a group of selected cards) for a list of actions and keyboard shortcuts. In particular, you can now transfer cards 4 at a time using the keyboard and interact with the sideboard from anywhere. Also remember that you can jump to the other table with the arrow keys and jump to the text filter with ctrl/cmd+f. From the text filter, you can jump down to the tables by pressing enter. - -In recent weeks people had noticed that the computer was picking the weakest cards in Draft mode rather than the strongest cards. This left the AI with a draft mode deck that was suboptimal. The computer should now pick the strongest cards rather than the weakest cards. - -Work was also done on making the UI more keyboard-friendly. For example, the OK button should now stay focused during matches, so you can advance through the stages by hitting Enter without having to go over and click the button all the time. If you find the button is losing focus, please report it as a bug. - -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 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. - -Several of the exiting sound files were changed and a handful of new sounds were added to the /res/sound/ folder. - -Our snapshot and beta releases should now display the correct SVN revision number in the title bar. This should allow people to file a bug report with the correct SVN revision number. New Cards: -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 -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 -Mana Vapors -Marble Priest -Mark of Eviction -Martyr's Cause -Memory Crystal -Meteor Shower -Mirror Golem -Mist of Stagnation -Mist of Stagnation -Moonlace -Moonring Mirror -Nantuko Shrine -Not of This World -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 +Dimensional Breach +Lim-Dul's Vault +Eureka +Hypergenesis New Phenomenons: -Chaotic AEther -Planewide Disaster + New Planes: -Aretopolis -Undercity Reaches +Feeding Grounds +Horizon Boughs 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 -Vampire Nocturnus Avatar -Viridian Zealot Avatar + Known Issues: @@ -222,8 +41,6 @@ Several people have noticed that the cards displayed on the battlefield will fai Some time was spent turning the static ETB triggers into the proper ETB replacement effects they should be, mainly to interact correctly with each other. This work is not yet finished. As a result there is currently some inconsistencies with "Enters the battlefield with counters" (Not incredibly noticeable). -It seems like the front face of double faced cards aren't triggering properly, but the back face and single faced cards are. - A recent contribution to the code base should fix some of the bugs that people noticed with cloning type abilities. At this time there are two remaining issues that we hope will be addressed in the near future. 1. Leave play triggers don't work correct for clones. @@ -240,21 +57,7 @@ Some people use the Windows application 7zip. This utility can be found at http: Contributors to This Release: -Agetian -Asepetci -Foreroes -Gos -Hellfish -Marc -Max -Myk -Rooger -RumbleBBU -Serrasmurf -Sloth -Sol -Swordshine -Chris H + (Quest icons used created by Teekatas, from his Legendora set http://raindropmemory.deviantart.com) diff --git a/README.txt b/README.txt index 0d2827b3215..135b489ca9e 100644 --- a/README.txt +++ b/README.txt @@ -496,6 +496,27 @@ The "Full catalog view" button appears to the left of the "Buy Card" button. Tog Multibuy: By selecting any number of items and hitting space (or selecting the "Buy Card" or "Sell Card" buttons), a player can buy one of everything selected. +Find-as-you-type is now implemented for Deck Editor tables. Just start typing while the table has focus and the next card with a matching string in its name will be highlighted. If more than one card matches, hit Enter to select the next matching card. A popup panel will appear with the search string so you know what you are searching for. If no cards match the string, the string will be highlighted in red. Normally, if you hit the spacebar while a card is selected in one table, the card will be moved to the other table (catalog/deck). When the popup is displayed, space characters are interpreted as part of the search string. Find-as-you-type mode is automatically exited after 5 seconds of inactivity, or hit Escape to exit find-as-you-type mode immediately. + +The Deck Editor has also gained some hotkey and context menu abilities. R-click on a card (or a group of selected cards) for a list of actions and keyboard shortcuts. In particular, you can now transfer cards 4 at a time using the keyboard and interact with the sideboard from anywhere. Also remember that you can jump to the other table with the arrow keys and jump to the text filter with ctrl/cmd+f. From the text filter, you can jump down to the tables by pressing enter. + + +The Game Log: + +Added a 'copy to clipboard' button on WinLose screen so players can easily copy the game log. + + +The UI more keyboard-friendly: + +Work was also done on making the UI more keyboard-friendly. For example, the OK button should now stay focused during matches, so you can advance through the stages by hitting Enter without having to go over and click the button all the time. If you find the button is losing focus, please report it as a bug. + + +Gatecrash Guild Sealed game mode: + +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. + Our Lawyers Made Us Do This: diff --git a/res/cardsfolder/g/gruul_charm.txt b/res/cardsfolder/g/gruul_charm.txt index 32df38a81eb..817734e9974 100644 --- a/res/cardsfolder/g/gruul_charm.txt +++ b/res/cardsfolder/g/gruul_charm.txt @@ -4,7 +4,7 @@ Types:Instant A:SP$ Charm | Cost$ R G | Choices$ CantBlockEffect,DBGainCtrl,DmgAll | CharmNum$ 1 | SpellDescription$ Choose one - Creatures without flying can't block this turn; or gain control of all permanents you own; or Gruul Charm deals 3 damage to each creature with flying. SVar:CantBlockEffect:DB$ Effect | Name$ Gruul Charm Effect | StaticAbilities$ KWPump | AILogic$ Evasion | SpellDescription$ Creatures without flying can't block this turn. SVar:KWPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.withoutFlying | AddHiddenKeyword$ CARDNAME can't block. | Description$ Creatures without flying can't block this turn. -SVar:DBGainCtrl:DB$ GainControl | Cost$ R | AllValid$ Permanent.YouOwn | SpellDescription$ Gain control of all permanents you own. +SVar:DBGainCtrl:DB$ GainControl | AllValid$ Permanent.YouOwn | SpellDescription$ Gain control of all permanents you own. SVar:DmgAll:DB$ DamageAll | NumDmg$ 3 | ValidCards$ Creature.withFlying | ValidDescription$ each creature with flying. | SpellDescription$ CARDNAME deals 3 damage to each creature with flying. SVar:Rarity:Uncommon SVar:Picture:http://www.wizards.com/global/images/magic/general/gruul_charm.jpg diff --git a/res/cardsfolder/j/jovens_tools.txt b/res/cardsfolder/j/jovens_tools.txt index 215f8d366df..1687a915830 100644 --- a/res/cardsfolder/j/jovens_tools.txt +++ b/res/cardsfolder/j/jovens_tools.txt @@ -2,7 +2,6 @@ Name:Joven's Tools ManaCost:6 Types:Artifact A:AB$ Pump | Cost$ 4 T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME can't be blocked except by Walls. | SpellDescription$ Target creature can't be blocked this turn except by Walls. -SVar:RemRandomDeck:True SVar:Rarity:Uncommon SVar:Picture:http://www.wizards.com/global/images/magic/general/jovens_tools.jpg SetInfo:HML|Uncommon|http://magiccards.info/scans/en/hl/133.jpg diff --git a/res/cardsfolder/v/varchilds_crusader.txt b/res/cardsfolder/v/varchilds_crusader.txt index a5fd7f12213..ba6c6e273f6 100644 --- a/res/cardsfolder/v/varchilds_crusader.txt +++ b/res/cardsfolder/v/varchilds_crusader.txt @@ -2,7 +2,7 @@ Name:Varchild's Crusader ManaCost:3 R Types:Creature Human Knight PT:3/2 -A:AB$ Pump | Cost$ 0 | KW$ CARDNAME can't be blocked except by Walls. & HIDDEN At the beginning of the end step, sacrifice CARDNAME. | SpellDescription$ CARDNAME can't be blocked this turn except by Walls. Sacrifice CARDNAME at the beginning of the next end step. +A:AB$ Pump | Cost$ 0 | KW$ HIDDEN CARDNAME can't be blocked except by Walls. & HIDDEN At the beginning of the end step, sacrifice CARDNAME. | SpellDescription$ CARDNAME can't be blocked this turn except by Walls. Sacrifice CARDNAME at the beginning of the next end step. SVar:RemAIDeck:True SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/varchilds_crusader.jpg diff --git a/src/main/java/forge/card/ability/AbilityUtils.java b/src/main/java/forge/card/ability/AbilityUtils.java index 26c4617a637..3a2cec6b930 100644 --- a/src/main/java/forge/card/ability/AbilityUtils.java +++ b/src/main/java/forge/card/ability/AbilityUtils.java @@ -1110,6 +1110,8 @@ public class AbilityUtils { if (paid) { unpaidCommand = paidCommand; } + ability.setActivatingPlayer(payer); + ability.setTarget(sa.getTarget()); GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, paidCommand, unpaidCommand, sa, game); waitForInput = true; // wait for the human input break; // multiple human players are not supported diff --git a/src/main/java/forge/card/ability/ai/AttachAi.java b/src/main/java/forge/card/ability/ai/AttachAi.java index f8623031015..8af929b570c 100644 --- a/src/main/java/forge/card/ability/ai/AttachAi.java +++ b/src/main/java/forge/card/ability/ai/AttachAi.java @@ -25,6 +25,7 @@ import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.card.staticability.StaticAbility; import forge.game.ai.ComputerUtilCard; +import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilMana; import forge.game.phase.CombatUtil; import forge.game.phase.PhaseHandler; @@ -46,7 +47,13 @@ public class AttachAi extends SpellAbilityAi { final Card source = sa.getSourceCard(); if (abCost != null) { - // No Aura spells have Additional Costs + // AI currently disabled for these costs + if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) { + return false; + } + if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) { + return false; + } } // prevent run-away activations - first time will always return true @@ -991,10 +998,12 @@ public class AttachAi extends SpellAbilityAi { if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { return false; } - final boolean evasive = (keyword.endsWith("Unblockable") || keyword.equals("Fear") + final boolean evasive = (keyword.equals("Unblockable") || keyword.equals("Fear") || keyword.equals("Intimidate") || keyword.equals("Shadow") || keyword.equals("Flying") || keyword.equals("Horsemanship") - || keyword.endsWith("walk")); + || keyword.endsWith("walk") || keyword.equals("CARDNAME can't be blocked except by Walls.") + || keyword.equals("All creatures able to block CARDNAME do so.") + || keyword.equals("CARDNAME can't be blocked by more than one creature.")); // give evasive keywords to creatures that can attack and deal damage if (evasive) { if (card.getNetCombatDamage() <= 0 diff --git a/src/main/java/forge/card/ability/ai/ControlGainAi.java b/src/main/java/forge/card/ability/ai/ControlGainAi.java index 2d11c84248d..8515e0f8ff4 100644 --- a/src/main/java/forge/card/ability/ai/ControlGainAi.java +++ b/src/main/java/forge/card/ability/ai/ControlGainAi.java @@ -77,6 +77,13 @@ public class ControlGainAi extends SpellAbilityAi { // if Defined, then don't worry about targeting if (tgt == null) { + if (sa.hasParam("AllValid")) { + List tgtCards = ai.getOpponent().getCardsIn(ZoneType.Battlefield); + tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa); + if (tgtCards.isEmpty()) { + return false; + } + } return true; } else { tgt.resetTargets(); diff --git a/src/main/java/forge/card/ability/ai/DamageAllAi.java b/src/main/java/forge/card/ability/ai/DamageAllAi.java index 27477bb4c06..6a8ea75c1ef 100644 --- a/src/main/java/forge/card/ability/ai/DamageAllAi.java +++ b/src/main/java/forge/card/ability/ai/DamageAllAi.java @@ -91,7 +91,7 @@ public class DamageAllAi extends SpellAbilityAi { } int minGain = 200; // The minimum gain in destroyed creatures - if (sa.getPayCosts().isReusuableResource()) { + if (sa.getPayCosts() != null && sa.getPayCosts().isReusuableResource()) { minGain = 100; } diff --git a/src/main/java/forge/card/ability/ai/PumpAiBase.java b/src/main/java/forge/card/ability/ai/PumpAiBase.java index 6d35c7e80b1..c7e8174d045 100644 --- a/src/main/java/forge/card/ability/ai/PumpAiBase.java +++ b/src/main/java/forge/card/ability/ai/PumpAiBase.java @@ -166,7 +166,7 @@ public abstract class PumpAiBase extends SpellAbilityAi { final boolean evasive = (keyword.endsWith("Unblockable") || keyword.endsWith("Fear") || keyword.endsWith("Intimidate") || keyword.endsWith("Shadow") - || keyword.contains("CantBeBlockedBy")); + || keyword.contains("CantBeBlockedBy") || keyword.endsWith("CARDNAME can't be blocked except by Walls.")); final boolean combatRelevant = (keyword.endsWith("First Strike") || keyword.contains("Bushido")); // give evasive keywords to creatures that can or do attack if (evasive) { diff --git a/src/main/java/forge/card/ability/effects/RestartGameEffect.java b/src/main/java/forge/card/ability/effects/RestartGameEffect.java index a4d60190aae..91fa1d8ccaa 100644 --- a/src/main/java/forge/card/ability/effects/RestartGameEffect.java +++ b/src/main/java/forge/card/ability/effects/RestartGameEffect.java @@ -48,7 +48,7 @@ public class RestartGameEffect extends SpellAbilityEffect { playerLibraries.put(p, newLibrary); } - GameNew.restartGame(game, sa.getActivatingPlayer(), playerLibraries); + GameNew.restartGame(Singletons.getModel().getMatch(), game, sa.getActivatingPlayer(), playerLibraries); } /* (non-Javadoc) diff --git a/src/main/java/forge/card/cardfactory/CardFactory.java b/src/main/java/forge/card/cardfactory/CardFactory.java index daef72a5c18..795dda16a00 100644 --- a/src/main/java/forge/card/cardfactory/CardFactory.java +++ b/src/main/java/forge/card/cardfactory/CardFactory.java @@ -32,6 +32,7 @@ import forge.card.CardRules; import forge.card.CardSplitType; import forge.card.ICardFace; import forge.card.cost.Cost; +import forge.card.mana.ManaCost; import forge.card.replacement.ReplacementHandler; import forge.card.spellability.AbilityActivated; import forge.card.spellability.SpellAbility; @@ -359,7 +360,31 @@ public class CardFactory { } if ( st == CardSplitType.Split ) { card.setName(rules.getName()); + // BUILD COMBINED 'Original' SIDE HERE + // Combined mana cost + ManaCost combinedManaCost = ManaCost.combine(rules.getMainPart().getManaCost(), rules.getOtherPart().getManaCost()); + card.setManaCost(combinedManaCost); + + // Combined card color + CardColor combinedCardColor = new CardColor(card); + combinedCardColor.addToCardColor(Color.fromColorSet(rules.getMainPart().getColor())); + combinedCardColor.addToCardColor(Color.fromColorSet(rules.getOtherPart().getColor())); + ArrayList combinedCardColorArr = new ArrayList(); + combinedCardColorArr.add(combinedCardColor); + card.setColor(combinedCardColorArr); + + // Combined abilities -- DOESN'T WORK AS DESIRED (?) + for (String a : rules.getMainPart().getAbilities()) { + card.addIntrinsicAbility(a); + } + for (String a : rules.getOtherPart().getAbilities()) { + card.addIntrinsicAbility(a); + } + + // Combined text -- CURRENTLY TAKES ORACLE TEXT BECAUSE THE ABILITY TEXT DOESN'T WORK (?) + String combinedText = String.format("%s: %s\n%s: %s", rules.getMainPart().getName(), rules.getMainPart().getOracleText(), rules.getOtherPart().getName(), rules.getOtherPart().getOracleText()); + card.setText(combinedText); } return card; diff --git a/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java b/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java index 3851f04a5a1..2fd9dca9477 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java @@ -3,10 +3,15 @@ package forge.card.cardfactory; import java.util.ArrayList; import java.util.List; +import javax.swing.JOptionPane; + import forge.Card; +import forge.Command; import forge.Singletons; import forge.card.cost.Cost; +import forge.card.mana.ManaCost; +import forge.card.spellability.Ability; import forge.card.spellability.AbilityActivated; import forge.card.spellability.Target; import forge.control.input.Input; @@ -193,5 +198,126 @@ class CardFactoryArtifacts { ability.setStackDescription(sbStack.toString()); card.addSpellAbility(ability); } // *************** END ************ END ************************** + + + // *************** START *********** START ************************** + else if (cardName.equals("Temporal Aperture")) { + /* + * 5, Tap: Shuffle your library, then reveal the top card. Until end + * of turn, for as long as that card remains on top of your library, + * play with the top card of your library revealed and you may play + * that card without paying its mana cost. (If it has X in its mana + * cost, X is 0.) + */ + final Card[] topCard = new Card[1]; + + final Ability freeCast = new Ability(card, ManaCost.ZERO) { + + @Override + public boolean canPlay() { + final PlayerZone lib = card.getController().getZone(ZoneType.Library); + return super.canPlay() && ((lib.size() > 0) && lib.get(0).equals(topCard[0])); + } + + @Override + public void resolve() { + final Card freeCard = topCard[0]; + final Player player = card.getController(); + if (freeCard != null) { + if (freeCard.isLand()) { + if (player.canPlayLand(freeCard)) { + player.playLand(freeCard); + } else { + JOptionPane.showMessageDialog(null, "You can't play any more lands this turn.", "", + JOptionPane.INFORMATION_MESSAGE); + } + } else { + Singletons.getModel().getGame().getActionPlay().playCardWithoutManaCost(freeCard, player); + } + } else { + final StringBuilder sb = new StringBuilder(); + sb.append("Error in ").append(cardName).append(". freeCard is null"); + JOptionPane.showMessageDialog(null, sb.toString(), "", JOptionPane.INFORMATION_MESSAGE); + } + + } + + @Override + public boolean canPlayAI() { + return false; + } + + }; + freeCast.setDescription("Play the previously revealed top card of your library for free."); + final StringBuilder sb = new StringBuilder(); + sb.append(cardName).append(" - play card without paying its mana cost."); + freeCast.setStackDescription(sb.toString()); + + class AbilityTemporalAperture extends AbilityActivated { + public AbilityTemporalAperture(final Card ca, final Cost co, final Target t) { + super(ca, co, t); + } + + @Override + public AbilityActivated getCopy() { + AbilityActivated res = new AbilityTemporalAperture(getSourceCard(), + getPayCosts(), getTarget() == null ? null : new Target(getTarget())); + CardFactoryUtil.copySpellAbility(this, res); + return res; + } + + private static final long serialVersionUID = -7328518969488588777L; + + @Override + public void resolve() { + final PlayerZone lib = card.getController().getZone(ZoneType.Library); + if (lib.size() > 0) { + + // shuffle your library + card.getController().shuffle(); + + // reveal the top card + topCard[0] = lib.get(0); + final StringBuilder sb = new StringBuilder(); + sb.append("Revealed card:\n").append(topCard[0].getName()); + JOptionPane.showMessageDialog(null, sb.toString(), card.getName(), JOptionPane.PLAIN_MESSAGE); + + card.addSpellAbility(freeCast); + card.addExtrinsicKeyword("Play with the top card of your library revealed."); + Singletons.getModel().getGame().getEndOfTurn().addUntil(new Command() { + private static final long serialVersionUID = -2860753262177388046L; + + @Override + public void execute() { + card.removeSpellAbility(freeCast); + card.removeExtrinsicKeyword("Play with the top card of your library revealed."); + } + }); + } + } // resolve + + @Override + public boolean canPlayAI() { + return false; + } + } + + final Cost abCost = new Cost(card, "5 T", true); + final AbilityActivated ability = new AbilityTemporalAperture(card, abCost, null); + + final StringBuilder sbStack = new StringBuilder(); + sbStack.append(card).append(" - Shuffle your library, then reveal the top card."); + ability.setStackDescription(sbStack.toString()); + + final StringBuilder sbDesc = new StringBuilder(); + sbDesc.append(abCost).append("Shuffle your library, then reveal the top card. "); + sbDesc.append("Until end of turn, for as long as that card remains on top of your "); + sbDesc.append("library, play with the top card of your library revealed "); + sbDesc.append("and you may play that card without paying its mana cost. "); + sbDesc.append("(If it has X in its mana cost, X is 0.)"); + ability.setDescription(sbDesc.toString()); + + card.addSpellAbility(ability); + } // *************** END ************ END ************************** } } diff --git a/src/main/java/forge/card/cost/Cost.java b/src/main/java/forge/card/cost/Cost.java index 7bc61487139..70176025dc7 100644 --- a/src/main/java/forge/card/cost/Cost.java +++ b/src/main/java/forge/card/cost/Cost.java @@ -373,7 +373,6 @@ public class Cost { } public final CostPartMana getCostMana() { - // TODO: Change where ChangeCost happens for (final CostPart part : this.costParts) { if (part instanceof CostPartMana) { return (CostPartMana) part; diff --git a/src/main/java/forge/control/input/InputBlock.java b/src/main/java/forge/control/input/InputBlock.java index d28f60d2cc1..108013f8e63 100644 --- a/src/main/java/forge/control/input/InputBlock.java +++ b/src/main/java/forge/control/input/InputBlock.java @@ -106,7 +106,6 @@ public class InputBlock extends Input { currentAttacker = null; allBlocking.clear(); - stop(); FControl.SINGLETON_INSTANCE.getPlayer().getController().passPriority(); } } diff --git a/src/main/java/forge/control/input/InputCleanup.java b/src/main/java/forge/control/input/InputCleanup.java index 808a940efe3..a478cca5575 100644 --- a/src/main/java/forge/control/input/InputCleanup.java +++ b/src/main/java/forge/control/input/InputCleanup.java @@ -59,7 +59,6 @@ public class InputCleanup extends Input { // goes to the next phase if (active.isUnlimitedHandSize() || n <= max || n <= 0 || active != turnOwner) { active.getController().passPriority(); - stop(); return; } ButtonUtil.disableAll(); diff --git a/src/main/java/forge/control/input/InputControl.java b/src/main/java/forge/control/input/InputControl.java index 83a3def162e..01cccb60cbf 100644 --- a/src/main/java/forge/control/input/InputControl.java +++ b/src/main/java/forge/control/input/InputControl.java @@ -25,6 +25,7 @@ import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.PlayerController; import forge.game.zone.MagicStack; +import forge.gui.match.controllers.CMessage; import forge.util.MyObservable; /** @@ -65,7 +66,7 @@ public class InputControl extends MyObservable implements java.io.Serializable { */ public final void setInput(final Input in) { boolean isInputEmpty = this.input == null || this.input instanceof InputPassPriority; - System.out.println(in.getClass().getName()); + //System.out.println(in.getClass().getName()); if (!this.game.getStack().isResolving() && isInputEmpty) { this.input = in; } else { @@ -134,12 +135,9 @@ public class InputControl extends MyObservable implements java.io.Serializable { * @param update * a boolean. */ - public final void resetInput() { resetInput(true); } - public final void resetInput(final boolean update) { + public final void resetInput() { this.input = null; - if (update) { - this.updateObservers(); - } + this.updateObservers(); } /** @@ -244,4 +242,19 @@ public class InputControl extends MyObservable implements java.io.Serializable { return pc.getDefaultInput(); } // getInput() + public final void setNewInput(GameState game) { + PhaseHandler ph = game.getPhaseHandler(); + + final Input tmp = getActualInput(); + //String message = String.format("%s's %s, priority of %s [%sP] input is %s", ph.getPlayerTurn(), ph.getPhase(), ph.getPriorityPlayer(), ph.isPlayerPriorityAllowed() ? "+" : "-", tmp == null ? "null" : tmp.getClass().getSimpleName()); + //System.out.println(message); + if (tmp != null) { + //System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ input is " + tmp.getClass().getName() ); + CMessage.SINGLETON_INSTANCE.getInputControl().setInput(tmp); + } else if (!ph.isPlayerPriorityAllowed()) { + // System.out.println("cannot have priority, forced to pass"); + ph.getPriorityPlayer().getController().passPriority(); + } + } + } // InputControl diff --git a/src/main/java/forge/control/input/InputMulligan.java b/src/main/java/forge/control/input/InputMulligan.java index ef7d36e8d7d..85c82714684 100644 --- a/src/main/java/forge/control/input/InputMulligan.java +++ b/src/main/java/forge/control/input/InputMulligan.java @@ -174,12 +174,11 @@ public class InputMulligan extends Input { next.initPlane(); } - //Set Field shown to current player. + //Set Field shown to current player. VField nextField = CMatchUI.SINGLETON_INSTANCE.getFieldViewFor(next); SDisplayUtil.showTab(nextField); game.getPhaseHandler().nextPhase(); - stop(); } @Override diff --git a/src/main/java/forge/control/input/InputPassPriority.java b/src/main/java/forge/control/input/InputPassPriority.java index 80d02562045..77ce4595bb0 100644 --- a/src/main/java/forge/control/input/InputPassPriority.java +++ b/src/main/java/forge/control/input/InputPassPriority.java @@ -75,7 +75,6 @@ public class InputPassPriority extends Input { @Override public final void selectButtonOK() { FControl.SINGLETON_INSTANCE.getPlayer().getController().passPriority(); - stop(); } /** {@inheritDoc} */ diff --git a/src/main/java/forge/game/GameNew.java b/src/main/java/forge/game/GameNew.java index f0ad3c3b15d..e80a1fa73c2 100644 --- a/src/main/java/forge/game/GameNew.java +++ b/src/main/java/forge/game/GameNew.java @@ -11,8 +11,6 @@ import java.util.Set; import javax.swing.JOptionPane; - - import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; @@ -25,11 +23,13 @@ import forge.CardUtil; import forge.Singletons; import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerType; +import forge.control.input.Input; import forge.control.input.InputControl; import forge.control.input.InputMulligan; import forge.deck.Deck; import forge.deck.CardPool; import forge.deck.DeckSection; +import forge.error.BugReporter; import forge.game.event.FlipCoinEvent; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; @@ -38,9 +38,11 @@ import forge.game.player.LobbyPlayer; import forge.game.player.Player; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; +import forge.gui.match.controllers.CMessage; import forge.gui.match.views.VAntes; import forge.item.CardPrinted; import forge.item.IPaperCard; +import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; import forge.util.Aggregates; import forge.util.MyRandom; @@ -50,6 +52,45 @@ import forge.util.MyRandom; * All of these methods can and should be static. */ public class GameNew { + + /** + * TODO: Write javadoc for this type. + * + */ + public static final class GameInputUpdatesThread extends Thread { + private final MatchController match; + private final GameState game; + private boolean wasChangedRecently; + + /** + * TODO: Write javadoc for Constructor. + * @param match + * @param game + */ + public GameInputUpdatesThread(MatchController match, GameState game) { + this.match = match; + this.game = game; + } + + public void run(){ + while(!game.isGameOver()) { + boolean needsNewInput = CMessage.SINGLETON_INSTANCE.getInputControl().isValid() == false; + if ( needsNewInput ) { + match.getInput().setNewInput(game); + wasChangedRecently = true; + } + try { + Thread.sleep(wasChangedRecently ? 2 : 40); + wasChangedRecently = false; + } catch (InterruptedException e) { + BugReporter.reportException(e); + break; + } + } + } + } + + public static final ForgePreferences preferences = Singletons.getModel().getPreferences(); private static void preparePlayerLibrary(Player player, final ZoneType zoneType, CardPool secion, boolean canRandomFoil, Random generator) { PlayerZone library = player.getZone(zoneType); @@ -60,7 +101,7 @@ public class GameNew { final Card card = cardPrinted.toForgeCard(player); // apply random pictures for cards - if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_CARD_ART)) { + if (preferences.getPrefBoolean(FPref.UI_RANDOM_CARD_ART)) { final int cntVariants = cardPrinted.getRules().getEditionInfo(cardPrinted.getEdition()).getCopiesCount(); if (cntVariants > 1) { card.setRandomPicture(generator.nextInt(cntVariants - 1) + 1); @@ -86,8 +127,8 @@ public class GameNew { * TODO: Accept something like match state as parameter. Match should be aware of players, * their decks and other special starting conditions. */ - public static void newGame(final Map playersConditions, final GameState game, final boolean canRandomFoil) { - Singletons.getModel().getMatch().getInput().clearInput(); + public static void newGame(final MatchController match, final Map playersConditions, final GameState game, final boolean canRandomFoil) { + match.getInput().clearInput(); Card.resetUniqueNumber(); // need this code here, otherwise observables fail @@ -97,7 +138,7 @@ public class GameNew { trigHandler.clearDelayedTrigger(); // friendliness - boolean useAnte = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE); + boolean useAnte = preferences.getPrefBoolean(FPref.UI_ANTE); final Set rAICards = new HashSet(); Map> removedAnteCards = new HashMap>(); @@ -112,8 +153,8 @@ public class GameNew { initVariantsZones(player, psc); - GameType gameType = Singletons.getModel().getMatch().getGameType(); - boolean isFirstGame = Singletons.getModel().getMatch().getPlayedGames().isEmpty(); + GameType gameType = match.getGameType(); + boolean isFirstGame = match.getPlayedGames().isEmpty(); boolean hasSideboard = psc.getOriginalDeck().has(DeckSection.Sideboard); boolean canSideBoard = !isFirstGame && gameType.isSideboardingAllowed() && hasSideboard; @@ -132,7 +173,7 @@ public class GameNew { preparePlayerLibrary(player, ZoneType.Sideboard, myDeck.get(DeckSection.Sideboard), canRandomFoil, generator); // Shuffling - if (player instanceof AIPlayer && Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_SMOOTH_LAND)) { + if (player instanceof AIPlayer && preferences.getPrefBoolean(FPref.UI_SMOOTH_LAND)) { // AI may do this instead of shuffling its deck final Iterable c1 = GameNew.smoothComputerManaCurve(player.getCardsIn(ZoneType.Library)); player.getZone(ZoneType.Library).setCards(c1); @@ -174,7 +215,7 @@ public class GameNew { JOptionPane.showMessageDialog(null, ante.toString(), "", JOptionPane.INFORMATION_MESSAGE); } - GameNew.actuateGame(game, false); + GameNew.actuateGame(match, game, false); } private static void initVariantsZones(final Player player, final PlayerStartConditions psc) { @@ -243,8 +284,7 @@ public class GameNew { } // ultimate of Karn the Liberated - public static void restartGame(final GameState game, final Player startingTurn, Map> playerLibraries) { - MatchController match = Singletons.getModel().getMatch(); + public static void restartGame(final MatchController match, final GameState game, final Player startingTurn, Map> playerLibraries) { Map players = match.getPlayers(); Map playersConditions = new HashMap(); @@ -291,7 +331,7 @@ public class GameNew { PhaseHandler phaseHandler = game.getPhaseHandler(); phaseHandler.setPlayerTurn(startingTurn); - GameNew.actuateGame(game, true); + GameNew.actuateGame(match, game, true); } /** @@ -302,10 +342,10 @@ public class GameNew { * newGame, then when all is ready, call this function. * @param isRestartedGame Whether the actuated game is the first start or a restart */ - private static void actuateGame(final GameState game, boolean isRestartedGame) { + private static void actuateGame(final MatchController match, final GameState game, boolean isRestartedGame) { if (!isRestartedGame) { // Deciding which cards go to ante - if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE)) { + if (preferences.getPrefBoolean(FPref.UI_ANTE)) { final String nl = System.getProperty("line.separator"); final StringBuilder msg = new StringBuilder(); for (final Player p : game.getPlayers()) { @@ -324,15 +364,24 @@ public class GameNew { JOptionPane.showMessageDialog(null, msg, "Ante", JOptionPane.INFORMATION_MESSAGE); } - GameOutcome lastGameOutcome = Singletons.getModel().getMatch().getLastGameOutcome(); + GameOutcome lastGameOutcome = match.getLastGameOutcome(); // Only cut/coin toss if it's the first game of the match - if (lastGameOutcome == null) { - GameNew.seeWhoPlaysFirstDice(); + Player goesFirst; + Player humanPlayer = Singletons.getControl().getPlayer(); + boolean isFirstGame = lastGameOutcome == null; + if (isFirstGame) { + goesFirst = GameNew.seeWhoPlaysFirstDice(game); } else { - Player human = Singletons.getControl().getPlayer(); - Player goesFirst = lastGameOutcome.isWinner(human.getLobbyPlayer()) ? human.getOpponent() : human; - setPlayersFirstTurn(goesFirst, false); + + goesFirst = lastGameOutcome.isWinner(humanPlayer.getLobbyPlayer()) ? humanPlayer.getOpponent() : humanPlayer; } + String message = goesFirst + ( isFirstGame ? " has won the coin toss." : " lost the last game."); + boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(message); + if ( goesFirst != humanPlayer ) { + JOptionPane.showMessageDialog(null, message + "\nComputer Going First", "You are drawing", JOptionPane.INFORMATION_MESSAGE); + } + goesFirst = willPlay ? goesFirst : goesFirst.getOpponent(); + game.getPhaseHandler().setPlayerTurn(goesFirst); } // Draw cards @@ -340,9 +389,20 @@ public class GameNew { p.drawCards(p.getMaxHandSize()); } + + game.getPhaseHandler().setPhaseState(PhaseType.MULLIGAN); - InputControl control = Singletons.getModel().getMatch().getInput(); - control.setInput(new InputMulligan()); + + InputControl control = match.getInput(); + Input tmp = new InputMulligan(); + control.setInput(tmp); + + + Thread thGame = new GameInputUpdatesThread(match, game); + + match.getInput().getInput().showMessage(); + thGame.setName("Game input updater"); + thGame.start(); } // newGame() private static String buildFourColumnList(String firstLine, Iterable cAnteRemoved) { @@ -418,49 +478,14 @@ public class GameNew { *

* seeWhoPlaysFirstCoinToss. *

+ * @return */ - private static void seeWhoPlaysFirstDice() { - int playerDie = 0; - int computerDie = 0; - - while (playerDie == computerDie) { - playerDie = MyRandom.getRandom().nextInt(20); - computerDie = MyRandom.getRandom().nextInt(20); - } - + private static Player seeWhoPlaysFirstDice(final GameState game) { // Play the Flip Coin sound - Singletons.getModel().getGame().getEvents().post(new FlipCoinEvent()); + game.getEvents().post(new FlipCoinEvent()); - List allPlayers = Singletons.getModel().getGame().getPlayers(); - setPlayersFirstTurn(allPlayers.get(MyRandom.getRandom().nextInt(allPlayers.size())), true); + List allPlayers = game.getPlayers(); + return allPlayers.get(MyRandom.getRandom().nextInt(allPlayers.size())); } - private static void setPlayersFirstTurn(Player goesFirst, boolean firstGame) { - StringBuilder sb = new StringBuilder(goesFirst.toString()); - if (firstGame) { - sb.append(" has won the coin toss."); - } - else { - sb.append(" lost the last game."); - } - if (goesFirst.isHuman()) { - if (!humanPlayOrDraw(sb.toString())) { - goesFirst = goesFirst.getOpponent(); - } - } else { - sb.append("\nComputer Going First"); - JOptionPane.showMessageDialog(null, sb.toString(), "Play or Draw?", JOptionPane.INFORMATION_MESSAGE); - } - Singletons.getModel().getGame().getPhaseHandler().setPlayerTurn(goesFirst); - } // seeWhoPlaysFirstDice() - - private static boolean humanPlayOrDraw(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/game/MatchController.java b/src/main/java/forge/game/MatchController.java index c3c87c78df6..4f0e9455979 100644 --- a/src/main/java/forge/game/MatchController.java +++ b/src/main/java/forge/game/MatchController.java @@ -19,7 +19,7 @@ import forge.game.player.Player; import forge.game.player.PlayerStatistics; import forge.game.player.PlayerType; import forge.game.zone.ZoneType; -import forge.gui.GuiInput; +import forge.gui.InputProxy; import forge.gui.framework.EDocID; import forge.gui.framework.SDisplayUtil; import forge.gui.match.CMatchUI; @@ -149,8 +149,7 @@ public class MatchController { Singletons.getControl().changeState(FControl.Screens.MATCH_SCREEN); SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc()); - // set all observers - GuiInput inputControl = CMessage.SINGLETON_INSTANCE.getInputControl(); + InputProxy inputControl = CMessage.SINGLETON_INSTANCE.getInputControl(); input.addObserver(inputControl); currentGame.getStack().addObserver(inputControl); currentGame.getPhaseHandler().addObserver(inputControl); @@ -159,7 +158,7 @@ public class MatchController { // some observers are set in CMatchUI.initMatch final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed; - GameNew.newGame(startConditions, currentGame, canRandomFoil); + GameNew.newGame(this, startConditions, currentGame, canRandomFoil); // TODO restore this functionality!!! //VMatchUI.SINGLETON_INSTANCE.getViewDevMode().getDocument().setVisible(Preferences.DEV_MODE); diff --git a/src/main/java/forge/game/ai/AiInputCommon.java b/src/main/java/forge/game/ai/AiInputCommon.java index 391ebad1b06..5276194e4f0 100644 --- a/src/main/java/forge/game/ai/AiInputCommon.java +++ b/src/main/java/forge/game/ai/AiInputCommon.java @@ -108,7 +108,6 @@ public class AiInputCommon extends Input { } } player.getController().passPriority(); - stop(); } // getMessage(); /** @@ -162,7 +161,9 @@ public class AiInputCommon extends Input { sa = computer.getSpellAbilityToPlay(); if ( sa == null ) break; //System.out.println("Playing sa: " + sa); - ComputerUtil.handlePlayingSpellAbility(player, sa, game); + if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) { + break; + } } while ( sa != null ); } diff --git a/src/main/java/forge/game/ai/ComputerUtil.java b/src/main/java/forge/game/ai/ComputerUtil.java index ed0074b056a..4820a5e2c87 100644 --- a/src/main/java/forge/game/ai/ComputerUtil.java +++ b/src/main/java/forge/game/ai/ComputerUtil.java @@ -425,14 +425,16 @@ public class ComputerUtil { int count = 0; while (count < amount) { - final Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList); - if (prefCard != null) { - sacList.add(prefCard); - typeList.remove(prefCard); - count++; - } else { + Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList); + if (prefCard == null) { + prefCard = ComputerUtilCard.getWorstAI(typeList); + } + if (prefCard == null) { return null; } + sacList.add(prefCard); + typeList.remove(prefCard); + count++; } return sacList; } diff --git a/src/main/java/forge/game/ai/ComputerUtilCombat.java b/src/main/java/forge/game/ai/ComputerUtilCombat.java index 557f8102d9c..f9ac54c0223 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCombat.java +++ b/src/main/java/forge/game/ai/ComputerUtilCombat.java @@ -1648,13 +1648,13 @@ public class ComputerUtilCombat { public static Map distributeAIDamage(final Card attacker, final List block, int dmgCanDeal, GameEntity defender) { Map damageMap = new HashMap(); - if (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.") - || attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked.")) { + boolean isAttacking = defender != null; + + if (isAttacking && (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.") + || attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked."))) { damageMap.put(null, dmgCanDeal); return damageMap; } - - boolean isAttacking = defender != null; final boolean hasTrample = attacker.hasKeyword("Trample"); diff --git a/src/main/java/forge/gui/GuiInput.java b/src/main/java/forge/gui/InputProxy.java similarity index 69% rename from src/main/java/forge/gui/GuiInput.java rename to src/main/java/forge/gui/InputProxy.java index 787f195b2c1..d767040375b 100644 --- a/src/main/java/forge/gui/GuiInput.java +++ b/src/main/java/forge/gui/InputProxy.java @@ -21,9 +21,7 @@ import java.util.Observable; import java.util.Observer; import forge.Card; -import forge.Singletons; import forge.control.input.Input; -import forge.game.phase.PhaseHandler; import forge.game.player.Player; import forge.game.zone.PlayerZone; import forge.util.MyObservable; @@ -36,28 +34,16 @@ import forge.util.MyObservable; * @author Forge * @version $Id$ */ -public class GuiInput extends MyObservable implements Observer { +public class InputProxy extends MyObservable implements Observer { /** The input. */ private Input input; + private volatile boolean valid = false; - - /** {@inheritDoc} */ @Override public final void update(final Observable observable, final Object obj) { - PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler(); - - final Input tmp = Singletons.getModel().getMatch().getInput().getActualInput(); - // System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ actual input is " + ( tmp == null ? "null" : tmp.getClass().getName()) + "; MHP = " + ph.mayPlayerHavePriority() ); - if (tmp != null) { - // System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ input is " + tmp.getClass().getName() ); - this.setInput(tmp); - } else if (!ph.isPlayerPriorityAllowed()) { - //System.out.println("cannot have priority, forced to pass"); - ph.passPriority(); - } + valid = false; } - /** *

* Setter for the field input. @@ -66,9 +52,10 @@ public class GuiInput extends MyObservable implements Observer { * @param in * a {@link forge.control.input.Input} object. */ - private void setInput(final Input in) { + public void setInput(final Input in) { + valid = true; this.input = in; - this.input.showMessage(); + this.input.showMessage(); // this call may invalidate the input by the time it returns } /** @@ -121,10 +108,13 @@ public class GuiInput extends MyObservable implements Observer { return this.getInput().toString(); } - /** @return {@link forge.gui.GuiInput.Input} */ + /** @return {@link forge.gui.InputProxy.Input} */ public Input getInput() { return this.input; } + public boolean isValid() { + return valid; + } } diff --git a/src/main/java/forge/gui/match/controllers/CMessage.java b/src/main/java/forge/gui/match/controllers/CMessage.java index 0e4573e6d5c..ba32802f0b8 100644 --- a/src/main/java/forge/gui/match/controllers/CMessage.java +++ b/src/main/java/forge/gui/match/controllers/CMessage.java @@ -28,7 +28,7 @@ import javax.swing.JButton; import forge.Command; import forge.game.MatchController; -import forge.gui.GuiInput; +import forge.gui.InputProxy; import forge.gui.framework.ICDoc; import forge.gui.framework.SDisplayUtil; import forge.gui.match.views.VMessage; @@ -42,7 +42,7 @@ public enum CMessage implements ICDoc { /** */ SINGLETON_INSTANCE; - private GuiInput inputControl = new GuiInput(); + private InputProxy inputControl = new InputProxy(); private Component lastFocusedButton = null; private final ActionListener actCancel = new ActionListener() { @@ -87,7 +87,7 @@ public enum CMessage implements ICDoc { * * @return GuiInput */ - public GuiInput getInputControl() { + public InputProxy getInputControl() { return this.inputControl; } diff --git a/src/main/java/forge/view/arcane/PlayArea.java b/src/main/java/forge/view/arcane/PlayArea.java index b15772cdd1b..62acf479920 100644 --- a/src/main/java/forge/view/arcane/PlayArea.java +++ b/src/main/java/forge/view/arcane/PlayArea.java @@ -214,6 +214,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen while (deltaCardWidth > 0) { List template = tryArrangePilesOfWidth(lands, tokens, creatures, others); + //System.out.println(template == null ? "won't fit" : "Fits @ " + cardWidth + " !!! " + template.toString()); deltaCardWidth = (getCardWidth() - lastGoodCardWidth) / 2; if (template != null) { @@ -258,6 +259,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen int x = 0; int y = PlayArea.GUTTER_Y; + //System.out.println("-------- " + (mirror ? "^" : "_") + " (Positioning ) Card width = " + cardWidth + ". Playarea = " + playAreaWidth + " x " + playAreaHeight ); for (final CardStackRow row : template) { int rowBottom = 0; x = PlayArea.GUTTER_X; @@ -277,10 +279,11 @@ 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); + //System.out.println("... placinng " + panel.getCard() + " @ (" + panelX + ", " + panelY + ")" ); panel.setCardBounds(panelX, panelY, this.getCardWidth(), this.cardHeight); } rowBottom = Math.max(rowBottom, y + stack.getHeight()); - x += stack.getWidth() + cardSpacingX; + x += stack.getWidth(); } y = rowBottom; } @@ -291,6 +294,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen int afterFirstRow; + //System.out.println( "======== " + ( mirror ? "^" : "_" ) + " (try arrange) Card width = " + cardWidth + ". PlayArea = " + playAreaWidth + " x " + playAreaHeight + " ========"); boolean landsFit, tokensFit, creaturesFit; if (this.mirror) { // Wrap all creatures and lands. @@ -354,13 +358,13 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen // card width. 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 (final CardStack stack : sourceRow) { final int rowWidth = currentRow.getWidth(); + final int stackWidth = stack.getWidth(); + //System.out.printf("Adding %s (+%dpx), current row is %dpx and has %s \n", stack, stackWidth, rowWidth, currentRow ); // If the row is not empty and this stack doesn't fit, add the row. - if (rowWidth + stack.getWidth() > this.playAreaWidth && !currentRow.isEmpty() ) { + if (rowWidth + stackWidth > this.playAreaWidth && !currentRow.isEmpty() ) { // Stop processing if the row is too wide or tall. if (rowWidth > this.playAreaWidth || this.getRowsHeight(template) + sourceRow.getHeight() > this.playAreaHeight) { @@ -388,6 +392,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen template.add(insertIndex, currentRow); } else return false; } + //System.out.println("... row complete! " + currentRow.getWidth() + "px"); return true; } @@ -409,6 +414,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen private int planOthersRow(final List sourceRow, final int firstPile, final List template, final CardStackRow rowToFill) { int rowWidth = rowToFill.getWidth(); + // System.out.println("This row has:" + rowToFill + "; want to add:" + sourceRow ); for (int i = firstPile; i < sourceRow.size(); i++ ) { CardStack stack = sourceRow.get(i);