From b8b433586dcd2084e7651d9a0601fc8b00d78dd9 Mon Sep 17 00:00:00 2001 From: drdev Date: Sun, 7 Dec 2014 01:20:52 +0000 Subject: [PATCH] Support playing conquest games --- .gitattributes | 1 + .../src/main/java/forge/deck/DeckFormat.java | 18 +-- .../src/main/java/forge/game/GameType.java | 1 + .../match/winlose/ConquestWinLose.java | 39 ++++++ .../screens/match/winlose/QuestWinLose.java | 6 +- .../screens/match/winlose/ViewWinLose.java | 3 + .../planarconquest/CommandCenterScreen.java | 12 ++ .../planarconquest/ConquestPrefsScreen.java | 9 +- .../planarconquest/ConquestController.java | 117 ++++++++++++++++-- .../forge/planarconquest/ConquestPlane.java | 3 + .../planarconquest/ConquestPlaneData.java | 4 + .../planarconquest/ConquestPreferences.java | 14 ++- .../forge/planarconquest/IVCommandCenter.java | 3 + .../main/java/forge/util/WaitRunnable.java | 2 +- 14 files changed, 202 insertions(+), 30 deletions(-) create mode 100644 forge-gui-mobile/src/forge/screens/match/winlose/ConquestWinLose.java diff --git a/.gitattributes b/.gitattributes index 7d93bca772d..79eea31cf01 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1279,6 +1279,7 @@ forge-gui-mobile/src/forge/screens/match/views/VPlayers.java -text forge-gui-mobile/src/forge/screens/match/views/VPrompt.java -text forge-gui-mobile/src/forge/screens/match/views/VStack.java -text forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java -text +forge-gui-mobile/src/forge/screens/match/winlose/ConquestWinLose.java -text forge-gui-mobile/src/forge/screens/match/winlose/ControlWinLose.java -text forge-gui-mobile/src/forge/screens/match/winlose/GauntletWinLose.java -text forge-gui-mobile/src/forge/screens/match/winlose/LimitedWinLose.java -text diff --git a/forge-core/src/main/java/forge/deck/DeckFormat.java b/forge-core/src/main/java/forge/deck/DeckFormat.java index ee7bdd9aa1d..54bc5fac72d 100644 --- a/forge-core/src/main/java/forge/deck/DeckFormat.java +++ b/forge-core/src/main/java/forge/deck/DeckFormat.java @@ -34,15 +34,15 @@ import java.util.Map.Entry; * GameType is an enum to determine the type of current game. :) */ public enum DeckFormat { - - // Main board: allowed size SB: restriction Max distinct non basic cards - Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4), - QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4), - Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE), - Commander ( Range.is(99), Range.between(0, 10), 1), - Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4), - Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4), - Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4); + // Main board: allowed size SB: restriction Max distinct non basic cards + Constructed ( Range.between(60, Integer.MAX_VALUE), Range.between(0, 15), 4), + QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4), + Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE), + Commander ( Range.is(99), Range.between(0, 10), 1), + PlanarConquest ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 1), + Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4), + Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4), + Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4); private final Range mainRange; private final Range sideRange; // null => no check diff --git a/forge-game/src/main/java/forge/game/GameType.java b/forge-game/src/main/java/forge/game/GameType.java index a30d361b1f6..f3f57fac2a0 100644 --- a/forge-game/src/main/java/forge/game/GameType.java +++ b/forge-game/src/main/java/forge/game/GameType.java @@ -19,6 +19,7 @@ public enum GameType { Gauntlet (DeckFormat.Limited, true, true, true, "Gauntlet", "", null), Quest (DeckFormat.QuestDeck, true, true, false, "Quest", "", null), QuestDraft (DeckFormat.Limited, true, true, true, "Quest Draft", "", null), + PlanarConquest (DeckFormat.PlanarConquest, true, false, false, "Planar Conquest", "", null), Constructed (DeckFormat.Constructed, false, true, true, "Constructed", "", null), Vanguard (DeckFormat.Vanguard, true, true, true, "Vanguard", "Each player has a special \"Avatar\" card that affects the game.", null), Commander (DeckFormat.Commander, false, false, false, "Commander", "Each player has a legendary \"General\" card which can be cast at any time and determines deck colors.", null), diff --git a/forge-gui-mobile/src/forge/screens/match/winlose/ConquestWinLose.java b/forge-gui-mobile/src/forge/screens/match/winlose/ConquestWinLose.java new file mode 100644 index 00000000000..3bbbe2af416 --- /dev/null +++ b/forge-gui-mobile/src/forge/screens/match/winlose/ConquestWinLose.java @@ -0,0 +1,39 @@ +/** Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.screens.match.winlose; + +import forge.game.GameView; +import forge.model.FModel; + +public class ConquestWinLose extends ControlWinLose { + public ConquestWinLose(final ViewWinLose view0, GameView lastGame0) { + super(view0, lastGame0); + view0.getBtnContinue().setVisible(false); + view0.getBtnRestart().setVisible(false); + } + + @Override + public final void showRewards() { + FModel.getConquest().showGameRewards(lastGame, getView()); + } + + @Override + public final void actionOnQuit() { + super.actionOnQuit(); + FModel.getConquest().onGameFinished(lastGame); + } +} diff --git a/forge-gui-mobile/src/forge/screens/match/winlose/QuestWinLose.java b/forge-gui-mobile/src/forge/screens/match/winlose/QuestWinLose.java index 92c5c4448ab..21036a445a8 100644 --- a/forge-gui-mobile/src/forge/screens/match/winlose/QuestWinLose.java +++ b/forge-gui-mobile/src/forge/screens/match/winlose/QuestWinLose.java @@ -37,9 +37,9 @@ public class QuestWinLose extends ControlWinLose { * @param view0 ViewWinLose object * @param match2 */ - public QuestWinLose(final ViewWinLose view0, GameView lastGame) { - super(view0, lastGame); - controller = new QuestWinLoseController(lastGame, view0); + public QuestWinLose(final ViewWinLose view0, GameView lastGame0) { + super(view0, lastGame0); + controller = new QuestWinLoseController(lastGame0, view0); } @Override diff --git a/forge-gui-mobile/src/forge/screens/match/winlose/ViewWinLose.java b/forge-gui-mobile/src/forge/screens/match/winlose/ViewWinLose.java index db0d9ab481e..fb6d2975d8e 100644 --- a/forge-gui-mobile/src/forge/screens/match/winlose/ViewWinLose.java +++ b/forge-gui-mobile/src/forge/screens/match/winlose/ViewWinLose.java @@ -66,6 +66,9 @@ public class ViewWinLose extends FOverlay implements IWinLoseView { case QuestDraft: //control = new QuestDraftWinLose(this, game0); break; + case PlanarConquest: + control = new ConquestWinLose(this, game0); + break; case Draft: if (!FModel.getGauntletMini().isGauntletDraft()) { break; diff --git a/forge-gui-mobile/src/forge/screens/planarconquest/CommandCenterScreen.java b/forge-gui-mobile/src/forge/screens/planarconquest/CommandCenterScreen.java index fd97de6853c..56480126341 100644 --- a/forge-gui-mobile/src/forge/screens/planarconquest/CommandCenterScreen.java +++ b/forge-gui-mobile/src/forge/screens/planarconquest/CommandCenterScreen.java @@ -25,12 +25,14 @@ import forge.model.FModel; import forge.planarconquest.ConquestAction; import forge.planarconquest.ConquestCommander; import forge.planarconquest.ConquestData; +import forge.planarconquest.ConquestController.GameRunner; import forge.planarconquest.ConquestPlane.Region; import forge.planarconquest.ConquestPlaneData; import forge.planarconquest.ConquestPlaneData.RegionData; import forge.planarconquest.ConquestPreferences.CQPref; import forge.planarconquest.IVCommandCenter; import forge.screens.FScreen; +import forge.screens.LoadingOverlay; import forge.screens.match.TargetingOverlay; import forge.toolbox.FCardPanel; import forge.toolbox.FContainer; @@ -86,6 +88,16 @@ public class CommandCenterScreen extends FScreen implements IVCommandCenter { regionDisplay.onRegionChanged(); //simulate region change when day changes to ensure everything is updated } + @Override + public void startGame(final GameRunner gameRunner) { + LoadingOverlay.show("Loading new game...", new Runnable() { + @Override + public void run() { + gameRunner.finishStartingGame(); + } + }); + } + @Override public void drawBackground(Graphics g) { FImage background = FSkinTexture.BG_PLANAR_MAP; diff --git a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestPrefsScreen.java b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestPrefsScreen.java index 75f42525a4b..48942f4f3f2 100644 --- a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestPrefsScreen.java +++ b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestPrefsScreen.java @@ -25,7 +25,7 @@ public class ConquestPrefsScreen extends FScreen { WINS_TO_UNLOCK, VARIANT_FREQUENCY, BOOSTER, - REWARDS + ACTIONS } private FScrollPane scroller = add(new FScrollPane() { @@ -67,9 +67,10 @@ public class ConquestPrefsScreen extends FScreen { scroller.add(new PrefsOption("Uncommon", CQPref.BOOSTER_UNCOMMONS, PrefsGroup.BOOSTER)); scroller.add(new PrefsOption("Rare", CQPref.BOOSTER_RARES, PrefsGroup.BOOSTER)); - scroller.add(new PrefsHeader("Rewards", FSkinImage.QUEST_COIN, PrefsGroup.REWARDS)); - scroller.add(new PrefsOption("Recruit Bonus Card Odds (%)", CQPref.RECRUIT_BONUS_CARD_ODDS, PrefsGroup.REWARDS)); - scroller.add(new PrefsOption("Study Bonus Card Odds (%)", CQPref.STUDY_BONUS_CARD_ODDS, PrefsGroup.REWARDS)); + scroller.add(new PrefsHeader("Actions", FSkinImage.ALPHASTRIKE, PrefsGroup.ACTIONS)); + scroller.add(new PrefsOption("Defend - Bonus Life", CQPref.DEFEND_BONUS_LIFE, PrefsGroup.ACTIONS)); + scroller.add(new PrefsOption("Recruit - Bonus Card Odds (%)", CQPref.RECRUIT_BONUS_CARD_ODDS, PrefsGroup.ACTIONS)); + scroller.add(new PrefsOption("Study - Bonus Card Odds (%)", CQPref.STUDY_BONUS_CARD_ODDS, PrefsGroup.ACTIONS)); } @Override diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestController.java b/forge-gui/src/main/java/forge/planarconquest/ConquestController.java index 8772cec37f5..c79efba0f30 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestController.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestController.java @@ -24,20 +24,27 @@ import com.google.common.base.Predicate; import com.google.common.eventbus.Subscribe; import forge.FThreads; +import forge.LobbyPlayer; import forge.card.CardRarity; import forge.card.CardRules; import forge.card.CardRulesPredicates; import forge.deck.CardPool; import forge.deck.Deck; +import forge.game.GameRules; +import forge.game.GameType; import forge.game.GameView; +import forge.game.Match; import forge.game.event.GameEvent; +import forge.game.player.RegisteredPlayer; import forge.interfaces.IButton; import forge.interfaces.IWinLoseView; import forge.item.PaperCard; +import forge.match.MatchUtil; import forge.model.FModel; import forge.planarconquest.ConquestPlaneData.RegionData; import forge.planarconquest.ConquestPreferences.CQPref; import forge.player.GamePlayerUtil; +import forge.properties.ForgePreferences.FPref; import forge.util.Aggregates; import forge.util.Lang; import forge.util.gui.SGuiChoose; @@ -48,6 +55,7 @@ public class ConquestController { private ConquestData model; private CardPool cardPool; private transient IStorage decks; + private transient GameRunner gameRunner; public ConquestController() { } @@ -102,16 +110,16 @@ public class ConquestController { for (ConquestCommander commander : commanders) { switch (commander.getCurrentDayAction()) { case Attack1: - playGame(commander, 0, false); + playGame(commander, 0, false, commandCenter); break; case Attack2: - playGame(commander, 1, false); + playGame(commander, 1, false, commandCenter); break; case Attack3: - playGame(commander, 2, false); + playGame(commander, 2, false, commandCenter); break; case Defend: - playGame(commander, Aggregates.randomInt(0, 2), true); //defend against random opponent + playGame(commander, Aggregates.randomInt(0, 2), true, commandCenter); //defend against random opponent break; case Recruit: if (!recruit(commander)) { return; } @@ -141,10 +149,90 @@ public class ConquestController { }); } - private void playGame(ConquestCommander commander, int opponentIndex, boolean isHumanDefending) { - RegionData regionData = model.getCurrentPlaneData().getRegionData(commander.getDeployedRegion()); - ConquestCommander opponent = regionData.getOpponent(opponentIndex); - //TODO + private void playGame(final ConquestCommander commander, final int opponentIndex, final boolean isHumanDefending, final IVCommandCenter commandCenter) { + gameRunner = new GameRunner(commander, opponentIndex, isHumanDefending, commandCenter); + gameRunner.invokeAndWait(); + + //after game finished + if (gameRunner.wonGame) { + RegionData regionData = model.getCurrentPlaneData().getRegionData(commander.getDeployedRegion()); + regionData.replaceOpponent(opponentIndex); + } + gameRunner = null; + } + + public class GameRunner { + private class Lock { + } + private final Lock lock = new Lock(); + + public final ConquestCommander commander; + public final ConquestCommander opponent; + public final boolean isHumanDefending; + private final IVCommandCenter commandCenter; + private boolean wonGame; + + private GameRunner(final ConquestCommander commander0, final int opponentIndex, final boolean isHumanDefending0, final IVCommandCenter commandCenter0) { + commander = commander0; + RegionData regionData = model.getCurrentPlaneData().getRegionData(commander.getDeployedRegion()); + opponent = regionData.getOpponent(opponentIndex); + isHumanDefending = isHumanDefending0; + commandCenter = commandCenter0; + } + + public final void invokeAndWait() { + FThreads.assertExecutedByEdt(false); //not supported if on UI thread + FThreads.invokeInEdtLater(new Runnable() { + @Override + public void run() { + commandCenter.startGame(GameRunner.this); + } + }); + try { + synchronized(lock) { + lock.wait(); + } + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void finishStartingGame() { + RegisteredPlayer humanStart = new RegisteredPlayer(commander.getDeck()); + RegisteredPlayer aiStart = new RegisteredPlayer(opponent.getDeck()); + + humanStart.setStartingLife(30 + (isHumanDefending ? FModel.getConquestPreferences().getPrefInt(CQPref.DEFEND_BONUS_LIFE) : 0)); + aiStart.setStartingLife(30); + + List starter = new ArrayList(); + starter.add(humanStart.setPlayer(getConquestPlayer())); + + LobbyPlayer aiPlayer = GamePlayerUtil.createAiPlayer(); + starter.add(aiStart.setPlayer(aiPlayer)); + + boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL); + for(RegisteredPlayer rp : starter) { + rp.setRandomFoil(useRandomFoil); + } + GameRules rules = new GameRules(GameType.PlanarConquest); + rules.setGamesPerMatch(1); //only play one game at a time + rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); + rules.canCloneUseTargetsImage = FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE); + final Match mc = new Match(rules, starter); + FThreads.invokeInEdtNowOrLater(new Runnable(){ + @Override + public void run() { + MatchUtil.startGame(mc); + } + }); + } + + private void finish() { + synchronized(lock) { //release game lock once game finished + lock.notify(); + } + } } private boolean recruit(ConquestCommander commander) { @@ -161,10 +249,12 @@ public class ConquestController { CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, bonusCard ? 2 : 1); } + private LobbyPlayer getConquestPlayer() { + return GamePlayerUtil.getGuiPlayer(); //TODO: Should this be a separate player? + } + public void showGameRewards(final GameView game, final IWinLoseView view) { - view.getBtnRestart().setVisible(false); - view.getBtnContinue().setVisible(false); - if (game.isMatchWonBy(GamePlayerUtil.getGuiPlayer())) { //TODO: Should this be smarter + if (game.isMatchWonBy(getConquestPlayer())) { view.getBtnQuit().setText("Great!"); //give controller a chance to run remaining logic on a separate thread @@ -182,8 +272,9 @@ public class ConquestController { } public void onGameFinished(final GameView game) { - if (game.isMatchWonBy(GamePlayerUtil.getGuiPlayer())) { + if (game.isMatchWonBy(getConquestPlayer())) { model.addWin(); + gameRunner.wonGame = true; } else { model.addLoss(); @@ -191,6 +282,8 @@ public class ConquestController { FModel.getConquest().save(); FModel.getConquestPreferences().save(); + + gameRunner.finish(); } private void awardWinStreakBonus(final IWinLoseView view) { diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestPlane.java b/forge-gui/src/main/java/forge/planarconquest/ConquestPlane.java index 41f81061cce..349c444147b 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestPlane.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestPlane.java @@ -396,6 +396,9 @@ public enum ConquestPlane { cards.remove(used[i].getCard()); } } + if (cards.isEmpty()) { //if all commanders are used, we can't prevent duplicates + cards.addAll(commanders); + } return new ConquestCommander(Aggregates.random(cards), cardPool, true); } diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java b/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java index 4b682031efe..8d33b4d2857 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java @@ -76,6 +76,10 @@ public class ConquestPlaneData { return opponents[index]; } + public void replaceOpponent(int index) { + opponents[index] = region.getRandomOpponent(opponents); + } + public ConquestCommander getDeployedCommander() { return deployedCommander; } diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestPreferences.java b/forge-gui/src/main/java/forge/planarconquest/ConquestPreferences.java index 6d1732d5bb1..bffc2cfba65 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestPreferences.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestPreferences.java @@ -44,7 +44,8 @@ public class ConquestPreferences extends PreferencesStore 50) { + return "Bonus card odds must be between 0% and 50%."; + } + break; + case DEFEND_BONUS_LIFE: + if (val > 10) { + return "Bonus life must be between 0 and 10."; + } + break; default: break; } diff --git a/forge-gui/src/main/java/forge/planarconquest/IVCommandCenter.java b/forge-gui/src/main/java/forge/planarconquest/IVCommandCenter.java index 677a203fe60..97739b0001a 100644 --- a/forge-gui/src/main/java/forge/planarconquest/IVCommandCenter.java +++ b/forge-gui/src/main/java/forge/planarconquest/IVCommandCenter.java @@ -1,5 +1,8 @@ package forge.planarconquest; +import forge.planarconquest.ConquestController.GameRunner; + public interface IVCommandCenter { void updateCurrentDay(); + void startGame(final GameRunner gameRunner); } diff --git a/forge-gui/src/main/java/forge/util/WaitRunnable.java b/forge-gui/src/main/java/forge/util/WaitRunnable.java index 24235896053..6c21f9f17a1 100644 --- a/forge-gui/src/main/java/forge/util/WaitRunnable.java +++ b/forge-gui/src/main/java/forge/util/WaitRunnable.java @@ -8,7 +8,7 @@ public abstract class WaitRunnable implements Runnable { private final Lock lock = new Lock(); - public void invokeAndWait() { + public final void invokeAndWait() { FThreads.assertExecutedByEdt(false); //not supported if on UI thread FThreads.invokeInEdtLater(new Runnable() { @Override