mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
- Started working on the full split card face creation in CardFactory (mostly works, but some parts need modification to work correctly).
- Merge: merge the latest trunk into Splitcards.
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -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
|
||||
|
||||
219
CHANGES.txt
219
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)
|
||||
|
||||
21
README.txt
21
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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Card> tgtCards = ai.getOpponent().getCardsIn(ZoneType.Battlefield);
|
||||
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
|
||||
if (tgtCards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
tgt.resetTargets();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<CardColor> combinedCardColorArr = new ArrayList<CardColor>();
|
||||
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;
|
||||
|
||||
@@ -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 **************************
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -106,7 +106,6 @@ public class InputBlock extends Input {
|
||||
currentAttacker = null;
|
||||
allBlocking.clear();
|
||||
|
||||
stop();
|
||||
FControl.SINGLETON_INSTANCE.getPlayer().getController().passPriority();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -75,7 +75,6 @@ public class InputPassPriority extends Input {
|
||||
@Override
|
||||
public final void selectButtonOK() {
|
||||
FControl.SINGLETON_INSTANCE.getPlayer().getController().passPriority();
|
||||
stop();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
||||
@@ -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<Player, PlayerStartConditions> playersConditions, final GameState game, final boolean canRandomFoil) {
|
||||
Singletons.getModel().getMatch().getInput().clearInput();
|
||||
public static void newGame(final MatchController match, final Map<Player, PlayerStartConditions> 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<CardPrinted> rAICards = new HashSet<CardPrinted>();
|
||||
|
||||
Map<Player, Set<CardPrinted>> removedAnteCards = new HashMap<Player, Set<CardPrinted>>();
|
||||
@@ -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<Card> 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<Player, List<Card>> playerLibraries) {
|
||||
MatchController match = Singletons.getModel().getMatch();
|
||||
public static void restartGame(final MatchController match, final GameState game, final Player startingTurn, Map<Player, List<Card>> playerLibraries) {
|
||||
|
||||
Map<LobbyPlayer, PlayerStartConditions> players = match.getPlayers();
|
||||
Map<Player, PlayerStartConditions> playersConditions = new HashMap<Player, PlayerStartConditions>();
|
||||
@@ -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 <handsize> 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<CardPrinted> cAnteRemoved) {
|
||||
@@ -418,49 +478,14 @@ public class GameNew {
|
||||
* <p>
|
||||
* seeWhoPlaysFirstCoinToss.
|
||||
* </p>
|
||||
* @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<Player> allPlayers = Singletons.getModel().getGame().getPlayers();
|
||||
setPlayersFirstTurn(allPlayers.get(MyRandom.getRandom().nextInt(allPlayers.size())), true);
|
||||
List<Player> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1648,13 +1648,13 @@ public class ComputerUtilCombat {
|
||||
public static Map<Card, Integer> distributeAIDamage(final Card attacker, final List<Card> block, int dmgCanDeal, GameEntity defender) {
|
||||
Map<Card, Integer> damageMap = new HashMap<Card, Integer>();
|
||||
|
||||
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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Setter for the field <code>input</code>.
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
|
||||
|
||||
while (deltaCardWidth > 0) {
|
||||
List<CardStackRow> 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<CardStack> sourceRow, final int firstPile, final List<CardStackRow> 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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user