From 03816aa47a90f6c3fce2255c4e9f07a7068e79bb Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Tue, 4 Jun 2013 20:25:25 +0000 Subject: [PATCH] GameLog: quest listener is registered in MatchClass, Mulligan logs from events PhaseHandler - half-measures to handle close window during turn-based actions (like declare combatants) getAllPossibleAbilites method moved from game to card removed 5 calls to isHuman/Computer --- src/main/java/forge/Card.java | 39 ++++++++++ src/main/java/forge/GameLogFormatter.java | 8 ++ .../forge/card/ability/ai/ChangeZoneAi.java | 10 +++ .../ability/effects/ChangeZoneEffect.java | 19 ++--- .../cardfactory/CardFactorySorceries.java | 78 +++++-------------- .../control/FControlGameEventHandler.java | 1 - src/main/java/forge/game/Game.java | 42 ---------- src/main/java/forge/game/Match.java | 5 +- src/main/java/forge/game/ai/AiController.java | 3 + .../java/forge/game/phase/PhaseHandler.java | 8 +- src/main/java/forge/game/player/Player.java | 2 - .../forge/game/player/PlayerControllerAi.java | 3 + .../game/player/PlayerControllerHuman.java | 4 +- .../forge/gui/input/InputPassPriority.java | 2 +- .../forge/gui/match/nonsingleton/CField.java | 2 +- 15 files changed, 102 insertions(+), 124 deletions(-) diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index bee39fad39e..d78e7d6589e 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -63,6 +63,7 @@ import forge.card.trigger.Trigger; import forge.card.trigger.TriggerType; import forge.card.trigger.ZCTrigger; import forge.game.Game; +import forge.game.GameActionUtil; import forge.game.GlobalRuleChange; import forge.game.event.GameEventCardDamaged; import forge.game.event.GameEventCardDamaged.DamageType; @@ -72,6 +73,7 @@ import forge.game.event.GameEventCounterRemoved; import forge.game.event.GameEventCardTapped; import forge.game.phase.Combat; import forge.game.player.Player; +import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.item.CardDb; import forge.util.Expressions; @@ -8287,4 +8289,41 @@ public class Card extends GameEntity implements Comparable { throw new IllegalStateException("Card " + toString() + " has no means to determine the game it belongs to!"); } + /** + * TODO: Write javadoc for this method. + * @param card + * @param game TODO + * @param player + * @return + */ + public List getAllPossibleAbilites(Player player) { + // this can only be called by the Human + final Zone zone = player.getGame().getZoneOf(this); + + final List abilities = new ArrayList(); + for (SpellAbility sa : getSpellAbilities()) { + //add alternative costs as additional spell abilities + abilities.add(sa); + abilities.addAll(GameActionUtil.getAlternativeCosts(sa)); + } + + for (int iSa = 0; iSa < abilities.size();) { + SpellAbility sa = abilities.get(iSa); + sa.setActivatingPlayer(player); + if (!sa.canPlay()) + abilities.remove(iSa); + else + iSa++; + } + + if (isLand() && player.canPlayLand(this)) { + if (zone.is(ZoneType.Hand) || (!zone.is(ZoneType.Battlefield) && hasStartOfKeyword("May be played"))) { + Ability.PLAY_LAND_SURROGATE.setSourceCard(this); + abilities.add(Ability.PLAY_LAND_SURROGATE); + } + } + + return abilities; + } + } // end Card class diff --git a/src/main/java/forge/GameLogFormatter.java b/src/main/java/forge/GameLogFormatter.java index fb50464d536..4e9919ae4cb 100644 --- a/src/main/java/forge/GameLogFormatter.java +++ b/src/main/java/forge/GameLogFormatter.java @@ -14,6 +14,7 @@ import forge.game.event.GameEventBlockersDeclared; import forge.game.event.GameEventCardDamaged; import forge.game.event.GameEventCardDamaged.DamageType; import forge.game.event.GameEventLandPlayed; +import forge.game.event.GameEventMulligan; import forge.game.event.GameEventPlayerDamaged; import forge.game.event.GameEventPlayerPoisoned; import forge.game.event.GameEventSpellAbilityCast; @@ -27,6 +28,7 @@ import forge.game.event.GameEventPlayerControl; import forge.game.player.LobbyPlayer; import forge.game.player.Player; import forge.game.player.PlayerStatistics; +import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.maps.MapOfLists; @@ -231,6 +233,12 @@ public class GameLogFormatter extends IGameEventVisitor.Base { return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString()); } + + @Override + public GameLogEntry visit(GameEventMulligan ev) { + String message = String.format( "%s has mulliganed down to %d cards.", ev.player, ev.player.getZone(ZoneType.Hand).size()); + return new GameLogEntry(GameLogEntryType.MULLIGAN, message); + } @Subscribe diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 37a8a668d70..5e90ab155e2 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -38,6 +38,7 @@ import forge.game.ai.ComputerUtilMana; import forge.game.phase.Combat; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.player.PlayerActionConfirmMode; import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.MyRandom; @@ -1372,4 +1373,13 @@ public class ChangeZoneAi extends SpellAbilityAi { } } // end changeHiddenOriginResolveAI + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String) + */ + @Override + public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { + // AI was never asked + return true; + } + } diff --git a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java index c3b8b664257..1ec18cad7a2 100644 --- a/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java +++ b/src/main/java/forge/card/ability/effects/ChangeZoneEffect.java @@ -21,7 +21,6 @@ import forge.card.spellability.SpellAbilityStackInstance; import forge.card.spellability.Target; import forge.card.trigger.TriggerType; import forge.game.Game; -import forge.game.ai.ComputerUtilCard; import forge.game.player.Player; import forge.game.zone.Zone; import forge.game.zone.ZoneType; @@ -428,10 +427,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect { if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) { continue; } - final String prompt = "Do you want to move " + tgtC + " from " + origin + " to " + destination + "?"; - if (player.isHuman() && optional && !GuiDialog.confirm(hostCard, prompt)) { + + final String prompt = String.format("Do you want to move %s from %s to %s?", tgtC, origin, destination); + if (optional && false == player.getController().confirmAction(sa, null, prompt) ) continue; - } + final Zone originZone = game.getZoneOf(tgtC); // if Target isn't in the expected Zone, continue @@ -472,16 +472,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { list = CardLists.getValidCards(list, sa.getParam("AttachedTo"), tgtC.getController(), tgtC); } if (!list.isEmpty()) { - Card attachedTo = null; - if (player.isHuman()) { - if (list.size() > 1) { - attachedTo = GuiChoose.one(tgtC + " - Select a card to attach to.", list); - } else { - attachedTo = list.get(0); - } - } else { // AI player - attachedTo = ComputerUtilCard.getBestAI(list); - } + Card attachedTo = player.getController().chooseSingleCardForEffect(list, sa, tgtC + " - Select a card to attach to."); if (tgtC.isAura()) { if (tgtC.isEnchanting()) { // If this Card is already Enchanting something, need diff --git a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java index 2269319866c..b23c0a800bb 100644 --- a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java +++ b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java @@ -18,7 +18,6 @@ package forge.card.cardfactory; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; import forge.Card; @@ -31,14 +30,10 @@ import forge.card.mana.ManaCost; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.game.Game; -import forge.game.ai.AiController; import forge.game.player.Player; -import forge.game.player.PlayerControllerAi; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.input.InputPayManaExecuteCommands; -import forge.gui.input.InputSelectCards; -import forge.gui.input.InputSelectCardsFromList; /** *

@@ -52,39 +47,24 @@ public class CardFactorySorceries { private static final void balanceLands(Game game, Spell card) { - List> lands = new ArrayList>(); + int minLands = Integer.MAX_VALUE; for (Player p : game.getPlayers()) { - - lands.add(p.getLandsInPlay()); + int pL = p.getLandsInPlay().size(); + if( pL < minLands ) + minLands = pL; } - int min = Integer.MAX_VALUE; - for (List l : lands) { - int s = l.size(); - min = Math.min(min, s); - } - Iterator> ll = lands.iterator(); for (Player p : game.getPlayers()) { - List l = ll.next(); - int sac = l.size() - min; + List l = p.getLandsInPlay(); + int sac = l.size() - minLands; if (sac == 0) { continue; } - if (p.isComputer()) { - CardLists.shuffle(l); - for (int i = 0; i < sac; i++) { - game.getAction().sacrifice(l.get(i), card); - } - } else { - final List list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Land"); - - InputSelectCards inp = new InputSelectCardsFromList(sac, sac, list); - inp.setMessage("Select %d more land(s) to sacrifice"); - Singletons.getControl().getInputQueue().setInputAndWait(inp); - for( Card crd : inp.getSelected() ) - p.getGame().getAction().sacrifice(crd, card); - } + + List toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, l, "Select %d more land(s) to sacrifice"); + for( Card crd : toSac ) + p.getGame().getAction().sacrifice(crd, card); } } @@ -100,26 +80,16 @@ public class CardFactorySorceries { if (sac == 0) { continue; } - if (p.isHuman()) { - InputSelectCards sc = new InputSelectCardsFromList(sac, sac, hand); - sc.setMessage("Select %d more card(s) to discard"); - Singletons.getControl().getInputQueue().setInputAndWait(sc); - for( Card c : sc.getSelected()) - p.discard(c, spell); - } else { - final AiController ai = ((PlayerControllerAi)p.getController()).getAi(); - final List toDiscard = ai.getCardsToDiscard(sac, (String[])null, spell); - for (int i = 0; i < toDiscard.size(); i++) { - p.discard(toDiscard.get(i), spell); - } - } + + List toDiscard = p.getController().chooseCardsToDiscardFrom(p, spell, hand, sac, sac); // "Select %d more card(s) to discard" + for (Card c : toDiscard) + p.discard(c, spell); } } private static final void balanceCreatures(Game game, Spell card) { List> creats = new ArrayList>(); for (Player p : game.getPlayers()) { - creats.add(p.getCreaturesInPlay()); } int min = Integer.MAX_VALUE; @@ -135,22 +105,10 @@ public class CardFactorySorceries { if (sac == 0) { continue; } - if (p.isComputer()) { - CardLists.sortByPowerAsc(c); - CardLists.sortByCmcDesc(c); - Collections.reverse(c); - for (int i = 0; i < sac; i++) { - p.getGame().getAction().sacrifice(c.get(i), card); - } - } else { - final List list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Creature"); - InputSelectCards inp = new InputSelectCardsFromList(sac, sac, list); - inp.setMessage("Select %d more creature(s) to sacrifice"); - Singletons.getControl().getInputQueue().setInputAndWait(inp); - for( Card crd : inp.getSelected() ) - p.getGame().getAction().sacrifice(crd, card); - - } + List toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, c, "Select %d more creature(s) to sacrifice"); + + for( Card crd : toSac ) + p.getGame().getAction().sacrifice(crd, card); } } diff --git a/src/main/java/forge/control/FControlGameEventHandler.java b/src/main/java/forge/control/FControlGameEventHandler.java index e559d15ffc8..dc7f38d8f5c 100644 --- a/src/main/java/forge/control/FControlGameEventHandler.java +++ b/src/main/java/forge/control/FControlGameEventHandler.java @@ -10,7 +10,6 @@ import forge.Card; import forge.FThreads; import forge.game.event.GameEvent; import forge.game.event.GameEventAnteCardsSelected; -import forge.game.event.GameEventAttackersDeclared; import forge.game.event.GameEventGameFinished; import forge.game.event.GameEventGameOutcome; import forge.game.event.GameEventPlayerControl; diff --git a/src/main/java/forge/game/Game.java b/src/main/java/forge/game/Game.java index c8bcf28f64e..f5ea50fe2ad 100644 --- a/src/main/java/forge/game/Game.java +++ b/src/main/java/forge/game/Game.java @@ -30,11 +30,8 @@ import forge.Card; import forge.CardLists; import forge.ColorChanger; import forge.GameLog; -import forge.Singletons; import forge.StaticEffects; import forge.card.replacement.ReplacementHandler; -import forge.card.spellability.Ability; -import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbilityStackInstance; import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerType; @@ -122,9 +119,6 @@ public class Game { endOfTurn = new EndOfTurn(this); endOfCombat = new EndOfCombat(this); - if ( match0.getGameType() == GameType.Quest) - events.register(Singletons.getModel().getQuest()); // this one listens to player's mulligans ATM - subscribeToEvents(gameLog.getEventVisitor()); } @@ -549,42 +543,6 @@ public class Game { return type; } - /** - * TODO: Write javadoc for this method. - * @param card - * @param player - * @return - */ - public List getAbilitesOfCard(Card c, Player player) { - // this can only be called by the Human - final Zone zone = this.getZoneOf(c); - - final List abilities = new ArrayList(); - for (SpellAbility sa : c.getSpellAbilities()) { - //add alternative costs as additional spell abilities - abilities.add(sa); - abilities.addAll(GameActionUtil.getAlternativeCosts(sa)); - } - - for (int iSa = 0; iSa < abilities.size();) { - SpellAbility sa = abilities.get(iSa); - sa.setActivatingPlayer(player); - if (!sa.canPlay()) - abilities.remove(iSa); - else - iSa++; - } - - if (c.isLand() && player.canPlayLand(c)) { - if (zone.is(ZoneType.Hand) || (!zone.is(ZoneType.Battlefield) && c.hasStartOfKeyword("May be played"))) { - Ability.PLAY_LAND_SURROGATE.setSourceCard(c); - abilities.add(Ability.PLAY_LAND_SURROGATE); - } - } - - return abilities; - } - /** * @return the activePlane */ diff --git a/src/main/java/forge/game/Match.java b/src/main/java/forge/game/Match.java index b3dcce4f4f7..c810f9bd3d3 100644 --- a/src/main/java/forge/game/Match.java +++ b/src/main/java/forge/game/Match.java @@ -82,7 +82,10 @@ public class Match { public void startRound() { currentGame = new Game(players, gameType, this); - + + if ( getGameType() == GameType.Quest) + currentGame.subscribeToEvents(Singletons.getModel().getQuest()); // this one listens to player's mulligans ATM + Singletons.getControl().attachToGame(currentGame); final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed; diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index aabee1cce52..a7ae13a4ee5 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -695,6 +695,9 @@ public class AiController { } return choice; + case ChangeZone: // called when permanent ETB 'AttachedTo' something + return ComputerUtilCard.getBestAI(options); + default: throw new InvalidParameterException("AI chooseSingleCard does not know how to choose card for " + api); } } diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 0c43b9e4e01..0a141396b5c 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -465,6 +465,9 @@ public class PhaseHandler implements java.io.Serializable { private void declareAttackersTurnBasedActions() { Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers; whoDeclares.getController().declareAttackers(playerTurn); + + if ( game.isGameOver() ) // they just like to close window at any moment + return; game.getCombat().removeAbsentCombatants(); CombatUtil.checkAttackOrBlockAlone(game.getCombat()); @@ -518,7 +521,10 @@ public class PhaseHandler implements java.io.Serializable { // Apply Odric's effect here Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers; if ( combat.isPlayerAttacked(p) ) - whoDeclaresBlockers.getController().declareBlockers(p); + whoDeclaresBlockers.getController().declareBlockers(p); + + if ( game.isGameOver() ) // they just like to close window at any moment + return; } while(p != playerTurn); combat.removeAbsentCombatants(); diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 1375dd824e2..34505d92d01 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -39,7 +39,6 @@ import forge.Constant.Preferences; import forge.CounterType; import forge.FThreads; import forge.GameEntity; -import forge.GameLogEntryType; import forge.Singletons; import forge.card.MagicColor; import forge.card.ability.AbilityFactory; @@ -3137,7 +3136,6 @@ public class Player extends GameEntity implements Comparable { public void onMulliganned() { game.fireEvent(new GameEventMulligan(this)); // quest listener may interfere here final int newHand = getCardsIn(ZoneType.Hand).size(); - game.getGameLog().add(GameLogEntryType.MULLIGAN, this + " has mulliganed down to " + newHand + " cards."); stats.notifyHasMulliganed(); stats.notifyOpeningHandSize(newHand); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 0bfdee1e112..3a1ab9cd90f 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -200,6 +200,9 @@ public class PlayerControllerAi extends PlayerController { @Override public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List validCards, int min, int max) { + if ( p == player ) + return brains.getCardsToDiscard(min, max, validCards, sa); + boolean isTargetFriendly = !p.isOpponentOf(player); return isTargetFriendly diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 2385937dd68..3ae3600a2b8 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -263,8 +263,10 @@ public class PlayerControllerHuman extends PlayerController { // Human is supposed to read the message and understand from it what to choose if ( isOptional ) return GuiChoose.oneOrNone(title, options); - else + else if ( options.size() > 2 ) return GuiChoose.one(title, options); + else + return options.get(0); } /* (non-Javadoc) diff --git a/src/main/java/forge/gui/input/InputPassPriority.java b/src/main/java/forge/gui/input/InputPassPriority.java index 3152dbef240..d927b2ae193 100644 --- a/src/main/java/forge/gui/input/InputPassPriority.java +++ b/src/main/java/forge/gui/input/InputPassPriority.java @@ -64,7 +64,7 @@ public class InputPassPriority extends InputSyncronizedBase { @Override protected void onCardSelected(Card card, boolean isRmb) { - final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player)); + final SpellAbility ab = player.getController().getAbilityToPlay(card.getAllPossibleAbilites(player)); if ( null != ab) { chosenSa = ab; stop(); diff --git a/src/main/java/forge/gui/match/nonsingleton/CField.java b/src/main/java/forge/gui/match/nonsingleton/CField.java index 1351a099776..6b6253b7624 100644 --- a/src/main/java/forge/gui/match/nonsingleton/CField.java +++ b/src/main/java/forge/gui/match/nonsingleton/CField.java @@ -154,7 +154,7 @@ public class CField implements ICDoc { // TODO: "can play" check needed! // should I check for who owns these cards? Are there any abilities to be played from opponent's graveyard? - final SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(c, player)); + final SpellAbility ab = player.getController().getAbilityToPlay(c.getAllPossibleAbilites(player)); if ( null != ab) { game.getAction().invoke(new Runnable(){ @Override public void run(){ HumanPlay.playSpellAbility(player, ab);