diff --git a/.gitattributes b/.gitattributes index 97ec8d93792..c5d9ffa0afa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -705,6 +705,7 @@ res/cardsfolder/b/bala_ged_scorpion.txt svneol=native#text/plain res/cardsfolder/b/bala_ged_thief.txt -text res/cardsfolder/b/balance.txt svneol=native#text/plain res/cardsfolder/b/balance_of_power.txt -text +res/cardsfolder/b/balancing_act.txt -text res/cardsfolder/b/balduvian_barbarians.txt svneol=native#text/plain res/cardsfolder/b/balduvian_bears.txt svneol=native#text/plain res/cardsfolder/b/balduvian_conjurer.txt svneol=native#text/plain @@ -7036,6 +7037,7 @@ res/cardsfolder/n/nath_of_the_gilt_leaf.txt svneol=native#text/plain res/cardsfolder/n/naths_buffoon.txt svneol=native#text/plain res/cardsfolder/n/naths_elite.txt svneol=native#text/plain res/cardsfolder/n/natural_affinity.txt svneol=native#text/plain +res/cardsfolder/n/natural_balance.txt -text res/cardsfolder/n/natural_emergence.txt -text svneol=unset#text/plain res/cardsfolder/n/natural_end.txt -text res/cardsfolder/n/natural_order.txt svneol=native#text/plain @@ -10639,6 +10641,7 @@ res/cardsfolder/t/tanglebloom.txt svneol=native#text/plain res/cardsfolder/t/tangleroot.txt svneol=native#text/plain res/cardsfolder/t/tanglesap.txt -text res/cardsfolder/t/tanglewalker.txt svneol=native#text/plain +res/cardsfolder/t/taniwha.txt -text res/cardsfolder/t/taoist_hermit.txt svneol=native#text/plain res/cardsfolder/t/taoist_mystic.txt svneol=native#text/plain res/cardsfolder/t/tar_fiend.txt svneol=native#text/plain @@ -13793,6 +13796,7 @@ src/main/java/forge/game/GameActionPlay.java -text src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain src/main/java/forge/game/GameEndReason.java -text src/main/java/forge/game/GameFormat.java -text +src/main/java/forge/game/GameInputUpdatesThread.java -text src/main/java/forge/game/GameLossReason.java -text src/main/java/forge/game/GameNew.java -text src/main/java/forge/game/GameOutcome.java -text diff --git a/CHANGES.txt b/CHANGES.txt index daa1fd0e820..092bb1a5221 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -48,6 +48,11 @@ Order // Chaos Pain // Suffering Spite // Malice Stand // Deliver +Boom // Bust +Crime // Punishment +Rise // Fall +Bound // Determined +Hit // Run New Phenomenons: diff --git a/res/cardsfolder/b/balancing_act.txt b/res/cardsfolder/b/balancing_act.txt new file mode 100644 index 00000000000..550ae088aa3 --- /dev/null +++ b/res/cardsfolder/b/balancing_act.txt @@ -0,0 +1,26 @@ +Name:Balancing Act +ManaCost:2 W W +Types:Sorcery +A:SP$ RepeatEach | Cost$ 2 W W | RepeatPlayers$ Player | RepeatSubAbility$ FindFewestPermanent | StackDescription$ SpellDescription | SubAbility$ DBChooseRepeat | SpellDescription$ Each player chooses a number of permanents he or she controls equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way. +SVar:FindFewestPermanent:DB$ StoreSVar | SVar$ MinPermanent | Type$ CountSVar | Expression$ NumPermanent | ConditionCheckSVar$ NumPermanent | ConditionSVarCompare$ LTMinPermanent +SVar:NumPermanent:Count$Valid Permanent.RememberedPlayerCtrl +SVar:MinPermanent:Number$9999 +SVar:DBChooseRepeat:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose | StackDescription$ None | SubAbility$ SacAll +SVar:DBChoose:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Permanent.RememberedPlayerCtrl | Amount$ MinPermanent | References$ MinPermanent | ChoiceTitle$ Choose permanents you control | RememberChosen$ True +SVar:SacAll:DB$ SacrificeAll | ValidCards$ Permanent.IsNotRemembered | SubAbility$ DBCleanup1 | StackDescription$ None +SVar:DBCleanup1:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBFindFewestHand +SVar:DBFindFewestHand:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ FindFewestHand | StackDescription$ None | SubAbility$ DBChooseRepeat2 +SVar:FindFewestHand:DB$ StoreSVar | SVar$ MinHand | Type$ CountSVar | Expression$ NumHand | ConditionCheckSVar$ NumHand | ConditionSVarCompare$ LTMinHand +SVar:MinHand:Number$9999 +SVar:NumHand:Count$ValidHand Card.RememberedPlayerCtrl +SVar:DBChooseRepeat2:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChooseHand | StackDescription$ None | SubAbility$ DisCardAll +SVar:DBChooseHand:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Card.RememberedPlayerCtrl | ChoiceZone$ Hand | Amount$ MinHand | References$ MinHand | ChoiceTitle$ Choose cards in your hand | RememberChosen$ True +SVar:DisCardAll:DB$ Discard | Mode$ Defined | DefinedCards$ ValidHand Card.IsNotRemembered | Defined$ Each | SubAbility$ DBCleanup2 +SVar:DBCleanup2:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBReset1 +SVar:DBReset1:DB$ StoreSVar | SVar$ MinPermanent | Type$ Number | Expression$ 9999 | SubAbility$ DBReset2 +SVar:DBReset2:DB$ StoreSVar | SVar$ MinHand | Type$ Number | Expression$ 9999 +SVar:Picture:http://www.wizards.com/global/images/magic/general/balancing_act.jpg +SVar:RemAIDeck:True +SVar:RemRandomDeck:True +SetInfo:ODY Rare +Oracle:Each player chooses a number of permanents he or she controls equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way. diff --git a/res/cardsfolder/c/crashing_boars.txt b/res/cardsfolder/c/crashing_boars.txt index 9ee56a0035b..a1a8ea3687c 100644 --- a/res/cardsfolder/c/crashing_boars.txt +++ b/res/cardsfolder/c/crashing_boars.txt @@ -1,7 +1,7 @@ Name:Crashing Boars -ManaCost:2 G -Types:Creature Snake Warrior -PT:1/3 +ManaCost:3 G G +Types:Creature Boar +PT:4/4 T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigChoose | TriggerDescription$ Whenever CARDNAME attacks, defending player chooses an untapped creature he or she controls. That creature blocks CARDNAME this turn if able. SVar:TrigChoose:AB$ ChooseCard | Cost$ 0 | Defined$ DefendingPlayer | Amount$ 1 | Choices$ Creature.untapped+DefenderCtrl | SubAbility$ DBMustBlock SVar:DBMustBlock:DB$ MustBlock | Defined$ ChosenCard | SpellDescription$ Target creature blocks CARDNAME this turn if able. diff --git a/res/cardsfolder/n/natural_balance.txt b/res/cardsfolder/n/natural_balance.txt new file mode 100644 index 00000000000..42c0bd771bf --- /dev/null +++ b/res/cardsfolder/n/natural_balance.txt @@ -0,0 +1,12 @@ +Name:Natural Balance +ManaCost:2 G G +Types:Sorcery +A:SP$ RepeatEach | Cost$ 2 G G | RepeatPlayers$ Player | RepeatSubAbility$ BalanceLands | AILogic$ BalanceLands | SpellDescription$ Each player who controls six or more lands chooses five lands he or she controls and sacrifices the rest. Each player who controls four or fewer lands may search his or her library for up to X basic land cards and put them onto the battlefield, where X is five minus the number of lands he or she controls. Then each player who searched his or her library this way shuffles it. +SVar:BalanceLands:DB$ Sacrifice | SacValid$ Land | Amount$ SacX | Defined$ Remembered | ConditionCheckSVar$ SacX | ConditionSVarCompare$ GT0 | SubAbility$ FetchLands +SVar:FetchLands:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Land.Basic | ChangeNum$ FetchX | DefinedPlayer$ Remembered | ConditionCheckSVar$ FetchX | ConditionSVarCompare$ GT0 +SVar:LandsControlled:Count$Valid Land.RememberedPlayerCtrl +SVar:SacX:SVar$LandsControlled/Minus.5 +SVar:FetchX:Number$5/Minus.LandsControlled +SVar:Picture:http://www.wizards.com/global/images/magic/general/natural_balance.jpg +SetInfo:MIR Rare +Oracle:Each player who controls six or more lands chooses five lands he or she controls and sacrifices the rest. Each player who controls four or fewer lands may search his or her library for up to X basic land cards and put them onto the battlefield, where X is five minus the number of lands he or she controls. Then each player who searched his or her library this way shuffles it. diff --git a/res/cardsfolder/t/taniwha.txt b/res/cardsfolder/t/taniwha.txt new file mode 100644 index 00000000000..90dab68bbb9 --- /dev/null +++ b/res/cardsfolder/t/taniwha.txt @@ -0,0 +1,11 @@ +Name:Taniwha +ManaCost:3 U U +Types:Legendary Creature Serpent +PT:7/7 +K:Trample +K:Phasing +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPhaseLands | TriggerDescription$ At the beginning of your upkeep, all lands you control phase out. +SVar:TrigPhaseLands:DB$ Phases | Defined$ Valid Land.YouCtrl +SVar:Picture:http://www.wizards.com/global/images/magic/general/taniwha.jpg +SetInfo:MIR Rare +Oracle:Trample\nPhasing (This phases in or out before you untap during each of your untap steps. While it's phased out, it's treated as though it doesn't exist.)\nAt the beginning of your upkeep, all lands you control phase out. (They phase in before you untap during your next untap step.) diff --git a/src/main/java/forge/card/ability/ai/RepeatEachAi.java b/src/main/java/forge/card/ability/ai/RepeatEachAi.java index 04dd1e76c78..578482c3703 100644 --- a/src/main/java/forge/card/ability/ai/RepeatEachAi.java +++ b/src/main/java/forge/card/ability/ai/RepeatEachAi.java @@ -86,6 +86,17 @@ public class RepeatEachAi extends SpellAbilityAi { } tgt.addTarget(list.get(0)); + } else if ("BalanceLands".equals(logic)) { + if (CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() >= 5) { + return false; + } + + List opponents = aiPlayer.getOpponents(); + for(Player opp : opponents) { + if (CardLists.filter(opp.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() < 4) { + return false; + } + } } // TODO Add some normal AI variability here diff --git a/src/main/java/forge/game/GameInputUpdatesThread.java b/src/main/java/forge/game/GameInputUpdatesThread.java new file mode 100644 index 00000000000..ae3fb61336e --- /dev/null +++ b/src/main/java/forge/game/GameInputUpdatesThread.java @@ -0,0 +1,41 @@ +package forge.game; + +import forge.error.BugReporter; +import forge.gui.match.controllers.CMessage; + +/** + * TODO: Write javadoc for this type. + * + */ +public 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; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/forge/game/GameNew.java b/src/main/java/forge/game/GameNew.java index 0ad10e62fea..686fe654d35 100644 --- a/src/main/java/forge/game/GameNew.java +++ b/src/main/java/forge/game/GameNew.java @@ -25,7 +25,6 @@ import forge.card.trigger.TriggerType; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; -import forge.error.BugReporter; import forge.game.event.FlipCoinEvent; import forge.game.phase.PhaseHandler; import forge.game.player.AIPlayer; @@ -33,7 +32,6 @@ 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; @@ -48,43 +46,6 @@ import forge.util.MyRandom; */ 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) { @@ -233,10 +194,6 @@ public class GameNew { for (final Player p1 : game.getPlayers()) { p1.drawCards(p1.getMaxHandSize()); } - - Thread thGame = new GameInputUpdatesThread(match, game); - thGame.setName("Game input updater"); - thGame.start(); } // ultimate of Karn the Liberated diff --git a/src/main/java/forge/game/GameState.java b/src/main/java/forge/game/GameState.java index 88a1c9903f5..1cf4df89fff 100644 --- a/src/main/java/forge/game/GameState.java +++ b/src/main/java/forge/game/GameState.java @@ -287,7 +287,7 @@ public class GameState { /** * @return the gameOver */ - public boolean isGameOver() { + public synchronized boolean isGameOver() { return gameOver; } @@ -295,7 +295,7 @@ public class GameState { * @param reason * @param go the gameOver to set */ - public void setGameOver(GameEndReason reason) { + public synchronized void setGameOver(GameEndReason reason) { this.gameOver = true; for (Player p : roIngamePlayers) { p.onGameOver(); diff --git a/src/main/java/forge/game/MatchController.java b/src/main/java/forge/game/MatchController.java index 1bb9120c175..486afe4759f 100644 --- a/src/main/java/forge/game/MatchController.java +++ b/src/main/java/forge/game/MatchController.java @@ -157,6 +157,10 @@ public class MatchController { final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed; GameNew.newGame(this, startConditions, currentGame, canRandomFoil); + Thread thGame = new GameInputUpdatesThread(this, currentGame); + thGame.setName("Game input updater"); + thGame.start(); + // TODO restore this functionality!!! //VMatchUI.SINGLETON_INSTANCE.getViewDevMode().getDocument().setVisible(Preferences.DEV_MODE); for (final VField field : VMatchUI.SINGLETON_INSTANCE.getFieldViews()) { diff --git a/src/main/java/forge/gui/InputProxy.java b/src/main/java/forge/gui/InputProxy.java index 8219cd50199..0d33017066e 100644 --- a/src/main/java/forge/gui/InputProxy.java +++ b/src/main/java/forge/gui/InputProxy.java @@ -38,10 +38,10 @@ public class InputProxy extends MyObservable implements Observer { /** The input. */ private Input input; - private volatile boolean valid = false; + private boolean valid = false; @Override - public final void update(final Observable observable, final Object obj) { + public final synchronized void update(final Observable observable, final Object obj) { valid = false; } /** @@ -52,7 +52,7 @@ public class InputProxy extends MyObservable implements Observer { * @param in * a {@link forge.control.input.Input} object. */ - public final void setInput(final Input in) { + public final synchronized void setInput(final Input in) { valid = true; this.input = in; this.input.showMessage(); // this call may invalidate the input by the time it returns @@ -114,7 +114,7 @@ public class InputProxy extends MyObservable implements Observer { } - public boolean isValid() { + public synchronized boolean isValid() { return valid; } }