diff --git a/.gitattributes b/.gitattributes index 14104d0b044..2414ed74565 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15187,6 +15187,7 @@ src/main/java/forge/gui/home/settings/CSubmenuAvatars.java -text src/main/java/forge/gui/home/settings/CSubmenuDownloaders.java -text src/main/java/forge/gui/home/settings/CSubmenuPreferences.java -text src/main/java/forge/gui/home/settings/CSubmenuReleaseNotes.java -text +src/main/java/forge/gui/home/settings/GamePlayerUtil.java -text src/main/java/forge/gui/home/settings/VSubmenuAvatars.java -text src/main/java/forge/gui/home/settings/VSubmenuDownloaders.java -text src/main/java/forge/gui/home/settings/VSubmenuPreferences.java -text diff --git a/src/main/java/forge/GameLogFormatter.java b/src/main/java/forge/GameLogFormatter.java index a9c3ae0ef5d..6e7cc52fc77 100644 --- a/src/main/java/forge/GameLogFormatter.java +++ b/src/main/java/forge/GameLogFormatter.java @@ -1,253 +1,248 @@ -package forge; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map.Entry; - -import com.google.common.eventbus.Subscribe; - -import forge.card.spellability.TargetChoices; -import forge.game.GameOutcome; -import forge.game.event.GameEventAttackersDeclared; -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; -import forge.game.event.GameEventSpellResolved; -import forge.game.event.GameEventTurnBegan; -import forge.game.event.IGameEventVisitor; -import forge.game.event.GameEventGameOutcome; -import forge.game.event.GameEvent; -import forge.game.event.GameEventTurnPhase; -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.net.FServer; -import forge.util.Lang; -import forge.util.maps.MapOfLists; - -public class GameLogFormatter extends IGameEventVisitor.Base { - private final GameLog log; - - public GameLogFormatter(GameLog gameLog) { - log = gameLog; - } - - @Override - public GameLogEntry visit(GameEventGameOutcome ev) { - // add result entries to the game log - final LobbyPlayer human = FServer.instance.getLobby().getGuiPlayer(); - - // This adds some extra entries to log - final List outcomes = new ArrayList(); - for (Entry p : ev.result) { - String whoHas = p.getKey().equals(human) ? "You have" : p.getKey().getName() + " has"; - String outcome = String.format("%s %s", whoHas, p.getValue().getOutcome().toString()); - outcomes.add(outcome); - log.add(GameLogEntryType.GAME_OUTCOME, outcome); - } - - return generateSummary(ev.history); - } - - - /* (non-Javadoc) - * @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventSpellResolved) - */ - @Override - public GameLogEntry visit(GameEventSpellResolved ev) { - String messageForLog = ev.hasFizzled ? ev.spell.getSourceCard().getName() + " ability fizzles." : ev.spell.getStackDescription(); - return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, messageForLog); - } - - - @Override - public GameLogEntry visit(GameEventSpellAbilityCast event) { - String who = event.sa.getActivatingPlayer().getName(); - String action = event.sa.isSpell() ? " cast " : " activated "; - String what = event.sa.getStackDescription().startsWith("Morph ") ? "Morph" : event.sa.getSourceCard().toString(); - - StringBuilder sb = new StringBuilder(); - sb.append(who).append(action).append(what); - - if (event.sa.getTargetRestrictions() != null) { - sb.append(" targeting "); - for (TargetChoices ch : event.sa.getAllTargetChoices()) { - if (null != ch) { - sb.append(ch.getTargetedString()); - } - } - } - sb.append("."); - - return new GameLogEntry(GameLogEntryType.STACK_ADD, sb.toString()); - } - - private GameLogEntry generateSummary(List gamesPlayed) { - GameOutcome outcome1 = gamesPlayed.get(0); - int[] wins = new int[outcome1.getNumPlayers()]; - LobbyPlayer[] players = new LobbyPlayer[outcome1.getNumPlayers()]; - for(int i = 0; i < wins.length; wins[i++] = 0); - - for (GameOutcome go : gamesPlayed) { - int i = 0; - for(Entry ps : go) { - players[i] = ps.getKey(); - if( ps.getValue().getOutcome().hasWon() ) - wins[i]++; - i++; - } - } - - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < wins.length; i++) { - sb.append(players[i].getName()).append(": ").append(wins[i]).append(" "); - } - return new GameLogEntry(GameLogEntryType.MATCH_RESULTS, sb.toString()); - } - - @Override - public GameLogEntry visit(GameEventPlayerControl event) { - // TODO Auto-generated method stub - LobbyPlayer newController = event.newController; - Player p = event.player; - - final String message; - if( newController == null ) - message = p.getName() + " has restored control over themself"; - else - message = String.format("%s is controlled by %s", p.getName(), newController.getName()); - - return new GameLogEntry(GameLogEntryType.PLAYER_CONROL, message); - } - - @Override - public GameLogEntry visit(GameEventTurnPhase ev) { - Player p = ev.playerTurn; - return new GameLogEntry(GameLogEntryType.PHASE, ev.phaseDesc + Lang.getPossesive(p.getName()) + " " + ev.phase.nameForUi); - } - - - @Override - public GameLogEntry visit(GameEventCardDamaged event) { - String additionalLog = ""; - if( event.type == DamageType.Deathtouch ) additionalLog = "(Deathtouch)"; - if( event.type == DamageType.M1M1Counters ) additionalLog = "(As -1/-1 Counters)"; - if( event.type == DamageType.LoyaltyLoss ) additionalLog = "(Removing " + Lang.nounWithAmount(event.amount, "loyalty counter") + ")"; - - String message = String.format("%s deals %d damage %s to %s.", event.source, event.amount, additionalLog, event.card); - return new GameLogEntry(GameLogEntryType.DAMAGE, message); - } - - /* (non-Javadoc) - * @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventLandPlayed) - */ - @Override - public GameLogEntry visit(GameEventLandPlayed ev) { - String message = String.format("%s played %s", ev.player, ev.land); - return new GameLogEntry(GameLogEntryType.LAND, message); - } - - @Override - public GameLogEntry visit(GameEventTurnBegan event) { - String message = String.format( "Turn %d (%s)", event.turnNumber, event.turnOwner); - return new GameLogEntry(GameLogEntryType.TURN, message); - } - - @Override - public GameLogEntry visit(GameEventPlayerDamaged ev) { - String extra = ev.infect ? " (as poison counters)" : ""; - String message = String.format("%s deals %d %s damage to %s%s.", ev.source, ev.amount, ev.combat ? "combat" : "non-combat", ev.target, extra ); - return new GameLogEntry(GameLogEntryType.DAMAGE, message); - } - - @Override - public GameLogEntry visit(GameEventPlayerPoisoned ev) { - String message = String.format("%s receives %s from %s", ev.receiver, Lang.nounWithAmount(ev.amount, "posion counter"), ev.source); - return new GameLogEntry(GameLogEntryType.DAMAGE, message); - } - - @Override - public GameLogEntry visit(final GameEventAttackersDeclared ev) { - final StringBuilder sb = new StringBuilder(); - - // Loop through Defenders - // Append Defending Player/Planeswalker - - // Not a big fan of the triple nested loop here - for (GameEntity k : ev.attackersMap.keySet()) { - - Collection attackers = ev.attackersMap.get(k); - if (attackers == null || attackers.isEmpty()) { - continue; - } - if ( sb.length() > 0 ) sb.append("\n"); - sb.append(ev.player + " assigned " + Lang.joinHomogenous(attackers)); - sb.append(" to attack " + k + "."); - } - if ( sb.length() == 0 ) - sb.append(ev.player + " didn't attack this turn."); - - return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString()); - } - - - @Override - public GameLogEntry visit(final GameEventBlockersDeclared ev) { - final StringBuilder sb = new StringBuilder(); - - // Loop through Defenders - // Append Defending Player/Planeswalker - - Collection blockers = null; - - for (Entry> kv : ev.blockers.entrySet()) { - GameEntity defender = kv.getKey(); - MapOfLists attackers = kv.getValue(); - if (attackers == null || attackers.isEmpty()) { - continue; - } - if ( sb.length() > 0 ) sb.append("\n"); - - String controllerName = defender instanceof Card ? ((Card)defender).getController().getName() : defender.getName(); - boolean firstAttacker = true; - for (final Entry> att : attackers.entrySet()) { - if ( !firstAttacker ) sb.append("\n"); - - blockers = att.getValue(); - if ( blockers.isEmpty() ) { - sb.append(controllerName + " didn't block "); - } else { - sb.append(controllerName + " assigned " + Lang.joinHomogenous(blockers) + " to block "); - } - - sb.append(att.getKey()).append("."); - firstAttacker = false; - } - } - - 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 - public void recieve(GameEvent ev) { - GameLogEntry le = ev.visit(this); - if ( le != null ) - log.add(le); - } - +package forge; + +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; + +import com.google.common.eventbus.Subscribe; + +import forge.card.spellability.TargetChoices; +import forge.game.GameOutcome; +import forge.game.event.GameEvent; +import forge.game.event.GameEventAttackersDeclared; +import forge.game.event.GameEventBlockersDeclared; +import forge.game.event.GameEventCardDamaged; +import forge.game.event.GameEventCardDamaged.DamageType; +import forge.game.event.GameEventGameOutcome; +import forge.game.event.GameEventLandPlayed; +import forge.game.event.GameEventMulligan; +import forge.game.event.GameEventPlayerControl; +import forge.game.event.GameEventPlayerDamaged; +import forge.game.event.GameEventPlayerPoisoned; +import forge.game.event.GameEventSpellAbilityCast; +import forge.game.event.GameEventSpellResolved; +import forge.game.event.GameEventTurnBegan; +import forge.game.event.GameEventTurnPhase; +import forge.game.event.IGameEventVisitor; +import forge.game.player.LobbyPlayer; +import forge.game.player.Player; +import forge.game.zone.ZoneType; +import forge.util.Lang; +import forge.util.maps.MapOfLists; + +public class GameLogFormatter extends IGameEventVisitor.Base { + private final GameLog log; + + public GameLogFormatter(GameLog gameLog) { + log = gameLog; + } + + @Override + public GameLogEntry visit(GameEventGameOutcome ev) { + for (Player p : ev.result.getPlayers()) { + String outcome = String.format("%s has %s", p.getName(), p.getOutcome().toString()); + log.add(GameLogEntryType.GAME_OUTCOME, outcome); + } + return generateSummary(ev.history); + } + + + /* (non-Javadoc) + * @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventSpellResolved) + */ + @Override + public GameLogEntry visit(GameEventSpellResolved ev) { + String messageForLog = ev.hasFizzled ? ev.spell.getSourceCard().getName() + " ability fizzles." : ev.spell.getStackDescription(); + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, messageForLog); + } + + + @Override + public GameLogEntry visit(GameEventSpellAbilityCast event) { + String who = event.sa.getActivatingPlayer().getName(); + String action = event.sa.isSpell() ? " cast " : " activated "; + String what = event.sa.getStackDescription().startsWith("Morph ") ? "Morph" : event.sa.getSourceCard().toString(); + + StringBuilder sb = new StringBuilder(); + sb.append(who).append(action).append(what); + + if (event.sa.getTargetRestrictions() != null) { + sb.append(" targeting "); + for (TargetChoices ch : event.sa.getAllTargetChoices()) { + if (null != ch) { + sb.append(ch.getTargetedString()); + } + } + } + sb.append("."); + + return new GameLogEntry(GameLogEntryType.STACK_ADD, sb.toString()); + } + + private GameLogEntry generateSummary(List gamesPlayed) { + + GameOutcome outcome1 = gamesPlayed.get(0); + List players = outcome1.getPlayers(); + + final int[] wins = new int[players.size()]; + + // Calculate total games each player has won. + for (GameOutcome game : gamesPlayed) { + int i = 0; + for (Player p : game.getPlayers()) { + if (p.getOutcome().hasWon()) { + wins[i]++; + } + i++; + } + } + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < wins.length; i++) { + Player player = players.get(i); + String playerName = player.getName(); + playerName += " [" + player.getOriginalLobbyPlayer().getType() + "]"; + sb.append(playerName).append(": ").append(wins[i]).append(" "); + } + + return new GameLogEntry(GameLogEntryType.MATCH_RESULTS, sb.toString()); + } + + @Override + public GameLogEntry visit(GameEventPlayerControl event) { + // TODO Auto-generated method stub + LobbyPlayer newController = event.newController; + Player p = event.player; + + final String message; + if( newController == null ) + message = p.getName() + " has restored control over themself"; + else + message = String.format("%s is controlled by %s", p.getName(), newController.getName()); + + return new GameLogEntry(GameLogEntryType.PLAYER_CONROL, message); + } + + @Override + public GameLogEntry visit(GameEventTurnPhase ev) { + Player p = ev.playerTurn; + return new GameLogEntry(GameLogEntryType.PHASE, ev.phaseDesc + Lang.getPossesive(p.getName()) + " " + ev.phase.nameForUi); + } + + + @Override + public GameLogEntry visit(GameEventCardDamaged event) { + String additionalLog = ""; + if( event.type == DamageType.Deathtouch ) additionalLog = "(Deathtouch)"; + if( event.type == DamageType.M1M1Counters ) additionalLog = "(As -1/-1 Counters)"; + if( event.type == DamageType.LoyaltyLoss ) additionalLog = "(Removing " + Lang.nounWithAmount(event.amount, "loyalty counter") + ")"; + + String message = String.format("%s deals %d damage %s to %s.", event.source, event.amount, additionalLog, event.card); + return new GameLogEntry(GameLogEntryType.DAMAGE, message); + } + + /* (non-Javadoc) + * @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventLandPlayed) + */ + @Override + public GameLogEntry visit(GameEventLandPlayed ev) { + String message = String.format("%s played %s", ev.player, ev.land); + return new GameLogEntry(GameLogEntryType.LAND, message); + } + + @Override + public GameLogEntry visit(GameEventTurnBegan event) { + String message = String.format( "Turn %d (%s)", event.turnNumber, event.turnOwner); + return new GameLogEntry(GameLogEntryType.TURN, message); + } + + @Override + public GameLogEntry visit(GameEventPlayerDamaged ev) { + String extra = ev.infect ? " (as poison counters)" : ""; + String message = String.format("%s deals %d %s damage to %s%s.", ev.source, ev.amount, ev.combat ? "combat" : "non-combat", ev.target, extra ); + return new GameLogEntry(GameLogEntryType.DAMAGE, message); + } + + @Override + public GameLogEntry visit(GameEventPlayerPoisoned ev) { + String message = String.format("%s receives %s from %s", ev.receiver, Lang.nounWithAmount(ev.amount, "posion counter"), ev.source); + return new GameLogEntry(GameLogEntryType.DAMAGE, message); + } + + @Override + public GameLogEntry visit(final GameEventAttackersDeclared ev) { + final StringBuilder sb = new StringBuilder(); + + // Loop through Defenders + // Append Defending Player/Planeswalker + + // Not a big fan of the triple nested loop here + for (GameEntity k : ev.attackersMap.keySet()) { + + Collection attackers = ev.attackersMap.get(k); + if (attackers == null || attackers.isEmpty()) { + continue; + } + if ( sb.length() > 0 ) sb.append("\n"); + sb.append(ev.player + " assigned " + Lang.joinHomogenous(attackers)); + sb.append(" to attack " + k + "."); + } + if ( sb.length() == 0 ) + sb.append(ev.player + " didn't attack this turn."); + + return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString()); + } + + + @Override + public GameLogEntry visit(final GameEventBlockersDeclared ev) { + final StringBuilder sb = new StringBuilder(); + + // Loop through Defenders + // Append Defending Player/Planeswalker + + Collection blockers = null; + + for (Entry> kv : ev.blockers.entrySet()) { + GameEntity defender = kv.getKey(); + MapOfLists attackers = kv.getValue(); + if (attackers == null || attackers.isEmpty()) { + continue; + } + if ( sb.length() > 0 ) sb.append("\n"); + + String controllerName = defender instanceof Card ? ((Card)defender).getController().getName() : defender.getName(); + boolean firstAttacker = true; + for (final Entry> att : attackers.entrySet()) { + if ( !firstAttacker ) sb.append("\n"); + + blockers = att.getValue(); + if ( blockers.isEmpty() ) { + sb.append(controllerName + " didn't block "); + } else { + sb.append(controllerName + " assigned " + Lang.joinHomogenous(blockers) + " to block "); + } + + sb.append(att.getKey()).append("."); + firstAttacker = false; + } + } + + 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 + public void recieve(GameEvent ev) { + GameLogEntry le = ev.visit(this); + if ( le != null ) + log.add(le); + } + } // end class GameLog \ No newline at end of file diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index 402754a7f68..d78d7b49e3b 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -35,6 +35,8 @@ import javax.swing.JLayeredPane; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; +import org.apache.commons.lang.StringUtils; + import forge.Card; import forge.Constant.Preferences; import forge.Singletons; @@ -42,7 +44,10 @@ import forge.control.KeyboardShortcuts.Shortcut; import forge.game.Game; import forge.game.GameType; import forge.game.Match; +import forge.game.RegisteredPlayer; import forge.game.player.LobbyPlayer; +import forge.game.player.LobbyPlayerAi; +import forge.game.player.LobbyPlayerHuman; import forge.game.player.Player; import forge.gui.GuiDialog; import forge.gui.SOverlayUtils; @@ -56,6 +61,7 @@ import forge.gui.framework.SOverflowUtil; import forge.gui.framework.SResizingUtil; import forge.gui.home.CHomeUI; import forge.gui.home.VHomeUI; +import forge.gui.home.settings.GamePlayerUtil; import forge.gui.match.CMatchUI; import forge.gui.match.VMatchUI; import forge.gui.match.controllers.CDock; @@ -67,6 +73,7 @@ import forge.gui.match.views.VAntes; import forge.gui.menus.ForgeMenu; import forge.gui.toolbox.FSkin; import forge.net.FServer; +import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; import forge.properties.NewConstants; import forge.quest.QuestController; @@ -400,7 +407,7 @@ public enum FControl implements KeyEventDispatcher { game.getAction().checkGameOverCondition(); else { game.isGameOver(); // this is synchronized method - it's used to make Game-0 thread see changes made here - inputQueue.onGameOver(false); // release any waiting input, effectively passing priority + inputQueue.onGameOver(false); // release any waiting input, effectively passing priority } playbackControl.onGameStopRequested(); @@ -413,6 +420,7 @@ public enum FControl implements KeyEventDispatcher { public final void startGameWithUi(Match match) { + setPlayerName(match.getPlayers()); Game newGame = match.createGame(); attachToGame(newGame); match.startGame(newGame, null); @@ -512,5 +520,23 @@ public enum FControl implements KeyEventDispatcher { //Allow the event to be redispatched return false; } + + /** + * Prompts user for a name that will be used instead of "Human" during gameplay. + *

+ * This is a one time only event that is triggered when starting a game and the + * PLAYER_NAME setting is blank. Does not apply to a hotseat game. + */ + private void setPlayerName(List players) { + final ForgePreferences prefs = Singletons.getModel().getPreferences(); + if (StringUtils.isBlank(prefs.getPref(FPref.PLAYER_NAME))) { + boolean isPlayerOneHuman = players.get(0).getPlayer() instanceof LobbyPlayerHuman; + boolean isPlayerTwoComputer = players.get(1).getPlayer() instanceof LobbyPlayerAi; + if (isPlayerOneHuman && isPlayerTwoComputer) { + GamePlayerUtil.setPlayerName(); + } + } + } + } diff --git a/src/main/java/forge/game/GameOutcome.java b/src/main/java/forge/game/GameOutcome.java index d259e579013..382900b8902 100644 --- a/src/main/java/forge/game/GameOutcome.java +++ b/src/main/java/forge/game/GameOutcome.java @@ -1,162 +1,184 @@ -/* - * 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.game; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - -import org.apache.commons.lang3.tuple.Pair; - -import forge.game.player.LobbyPlayer; -import forge.game.player.Player; -import forge.game.player.PlayerOutcome; -import forge.game.player.PlayerStatistics; - -/** - *

- * GameInfo class. - *

- * - * @author Forge - * @version $Id: GameOutcome.java 17608 2012-10-20 22:27:27Z Max mtg $ - */ - -// This class might be divided in two parts: the very summary (immutable with -// only getters) and -// GameObserver class - who should be notified of any considerable ingame event -public final class GameOutcome implements Iterable> { - - - /** The player got first turn. */ - // private String playerGotFirstTurn = "Nobody"; - - /** The last turn number. */ - private int lastTurnNumber = 0; - - /** The player rating. */ - private final List> playerRating = new ArrayList>(2); - - private GameEndReason winCondition; - - public GameOutcome(GameEndReason reason, final Iterable list) { - winCondition = reason; - for (final Player n : list) { - this.playerRating.add(Pair.of(n.getLobbyPlayer(), n.getStats())); - } - } - - public boolean isDraw() { - for (Pair pv : playerRating) { - if (pv.getValue().getOutcome().hasWon()) { - return false; - } - } - return true; - } - - - public boolean isWinner(final LobbyPlayer who) { - for (Pair pv : playerRating) - if (pv.getValue().getOutcome().hasWon() && pv.getKey() == who ) - return true; - return false; - } - - /** - * Gets the winner. - * - * @return the winner - */ - public LobbyPlayer getWinner() { - for (Entry ps : playerRating) { - if (ps.getValue().getOutcome().hasWon()) { - return ps.getKey(); - } - } - return null; - } - - /** - * Gets the win condition. - * - * @return the win condition - */ - public GameEndReason getWinCondition() { - return this.winCondition; - } - - /** - * Gets the turn game ended. - * - * @return the turn game ended - */ - public int getLastTurnNumber() { - return this.lastTurnNumber; - } - - /** - * Sets the player who got first turn. - * - */ - /* - * public void setPlayerWhoGotFirstTurn(final String playerName) { - * this.playerGotFirstTurn = playerName; } - */ - - /** - * Gets the win spell effect. - * - * @return the win spell effect - */ - public String getWinSpellEffect() { - for (Pair pv : playerRating) { - PlayerOutcome po = pv.getValue().getOutcome(); - if (po.hasWon()) { - return po.altWinSourceName; - } - } - return null; - } - - /* (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator> iterator() { - return playerRating.iterator(); - } - - /** - * TODO: Write javadoc for this method. - * @param turnNumber - */ - public void setTurnsPlayed(int turnNumber) { - lastTurnNumber = turnNumber; - } - - /** - * TODO: Write javadoc for this method. - * @return - */ - public int getNumPlayers() { - return playerRating.size(); - } - -} +/* + * 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.game; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +import org.apache.commons.lang3.tuple.Pair; + +import forge.game.player.LobbyPlayer; +import forge.game.player.Player; +import forge.game.player.PlayerOutcome; +import forge.game.player.PlayerStatistics; + +/** + *

+ * GameInfo class. + *

+ * + * @author Forge + * @version $Id: GameOutcome.java 17608 2012-10-20 22:27:27Z Max mtg $ + */ + +// This class might be divided in two parts: the very summary (immutable with +// only getters) and +// GameObserver class - who should be notified of any considerable ingame event +public final class GameOutcome implements Iterable> { + + + /** The player got first turn. */ + // private String playerGotFirstTurn = "Nobody"; + + /** The last turn number. */ + private int lastTurnNumber = 0; + + /** The player rating. */ + private final List> playerRating = new ArrayList>(2); + + private final Iterable players; + + private GameEndReason winCondition; + + public GameOutcome(GameEndReason reason, final Iterable list) { + winCondition = reason; + players = list; + for (final Player n : list) { + this.playerRating.add(Pair.of(n.getLobbyPlayer(), n.getStats())); + } + } + + public boolean isDraw() { + for (Pair pv : playerRating) { + if (pv.getValue().getOutcome().hasWon()) { + return false; + } + } + return true; + } + + + public boolean isWinner(final LobbyPlayer who) { + for (Pair pv : playerRating) + if (pv.getValue().getOutcome().hasWon() && pv.getKey() == who ) + return true; + return false; + } + + /** + * Gets the winner. + * + * @return the winner + */ + public LobbyPlayer getWinningLobbyPlayer() { + for (Entry ps : playerRating) { + if (ps.getValue().getOutcome().hasWon()) { + return ps.getKey(); + } + } + return null; + } + + /** + * Gets winning {@code Player}. + *

+ * Alternative to {@link getWinningLobbyPlayer()} which does not + * distinguish between human player names (a problem for hotseat games). + */ + public Player getWinningPlayer() { + for (Player p: players) { + if (p.getOutcome().hasWon()) { + return p; + } + } + return null; + } + + /** + * Gets the win condition. + * + * @return the win condition + */ + public GameEndReason getWinCondition() { + return this.winCondition; + } + + /** + * Gets the turn game ended. + * + * @return the turn game ended + */ + public int getLastTurnNumber() { + return this.lastTurnNumber; + } + + /** + * Sets the player who got first turn. + * + */ + /* + * public void setPlayerWhoGotFirstTurn(final String playerName) { + * this.playerGotFirstTurn = playerName; } + */ + + /** + * Gets the win spell effect. + * + * @return the win spell effect + */ + public String getWinSpellEffect() { + for (Pair pv : playerRating) { + PlayerOutcome po = pv.getValue().getOutcome(); + if (po.hasWon()) { + return po.altWinSourceName; + } + } + return null; + } + + /* (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator> iterator() { + return playerRating.iterator(); + } + + /** + * TODO: Write javadoc for this method. + * @param turnNumber + */ + public void setTurnsPlayed(int turnNumber) { + lastTurnNumber = turnNumber; + } + + /** + * TODO: Write javadoc for this method. + * @return + */ + public int getNumPlayers() { + return playerRating.size(); + } + + public List getPlayers() { + return (List)players; + } + +} diff --git a/src/main/java/forge/game/Match.java b/src/main/java/forge/game/Match.java index 590297b66fe..6ab57e9d281 100644 --- a/src/main/java/forge/game/Match.java +++ b/src/main/java/forge/game/Match.java @@ -1,197 +1,197 @@ -package forge.game; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import org.apache.commons.lang3.tuple.Pair; - -import com.google.common.collect.Lists; -import forge.Card; -import forge.Singletons; -import forge.game.event.GameEventAnteCardsSelected; -import forge.game.player.LobbyPlayer; -import forge.game.player.Player; -import forge.properties.ForgePreferences.FPref; - -/** - * TODO: Write javadoc for this type. - * - */ - -public class Match { - - private final List players; - private final GameType gameType; - - private int gamesPerMatch = 3; - private int gamesToWinMatch = 2; - - private boolean useAnte = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE); - - private final List gamesPlayed = new ArrayList(); - private final List gamesPlayedRo; - - /** - * This should become constructor once. - */ - public Match(GameType type, List players0) { - gamesPlayedRo = Collections.unmodifiableList(gamesPlayed); - players = Collections.unmodifiableList(Lists.newArrayList(players0)); - gameType = type; - } - - public Match(GameType type, List players0, Boolean overrideAnte) { - this(type, players0); - if( overrideAnte != null ) - this.useAnte = overrideAnte.booleanValue(); - } - - /** - * Gets the games played. - * - * @return the games played - */ - public final List getPlayedGames() { - return this.gamesPlayedRo; - } - - /** @return int */ - public int getGamesPerMatch() { - return gamesPerMatch; - } - - /** @return int */ - public int getGamesToWinMatch() { - return gamesToWinMatch; - } - - public void addGamePlayed(Game finished) { - if (!finished.isGameOver()) { - throw new IllegalStateException("Game is not over yet."); - } - gamesPlayed.add(finished.getOutcome()); - } - - - /** - * TODO: Write javadoc for this method. - */ - public Game createGame() { - return new Game(players, gameType, this); - } - - /** - * TODO: Write javadoc for this method. - */ - public void startGame(final Game game, final CountDownLatch latch) { - final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed; - GameNew.newGame(game, canRandomFoil, this.useAnte); - - // This code could be run run from EDT. - game.getAction().invoke(new Runnable() { - @Override - public void run() { - if (useAnte) { // Deciding which cards go to ante - List> list = GameNew.chooseCardsForAnte(game); - GameNew.moveCardsToAnte(list); - game.fireEvent(new GameEventAnteCardsSelected(list)); - } - - GameOutcome lastOutcome = gamesPlayed.isEmpty() ? null : gamesPlayed.get(gamesPlayed.size() - 1); - game.getAction().startGame(lastOutcome); - - if( null != latch ) - latch.countDown(); - } - }); - } - - public void clearGamesPlayed() { - gamesPlayed.clear(); - } - - public void clearLastGame() { - gamesPlayed.remove(gamesPlayed.size() - 1); - } - - /** - * TODO: Write javadoc for this method. - * - * @return - */ - public GameType getGameType() { - return gameType; - } - - public Iterable getOutcomes() { - return gamesPlayedRo; - } - - /** - * TODO: Write javadoc for this method. - * - * @return - */ - public boolean isMatchOver() { - int[] victories = new int[players.size()]; - for (GameOutcome go : gamesPlayed) { - LobbyPlayer winner = go.getWinner(); - int i = 0; - for (RegisteredPlayer p : players) { - if (p.getPlayer().equals(winner)) { - victories[i]++; - break; // can't have 2 winners per game - } - i++; - } - } - - for (int score : victories) { - if (score >= gamesToWinMatch) { - return true; - } - } - return gamesPlayed.size() >= gamesPerMatch; - } - - /** - * TODO: Write javadoc for this method. - * - * @param questPlayer - * @return - */ - public int getGamesWonBy(LobbyPlayer questPlayer) { - int sum = 0; - for (GameOutcome go : gamesPlayed) { - if (questPlayer.equals(go.getWinner())) { - sum++; - } - } - return sum; - } - - /** - * TODO: Write javadoc for this method. - * - * @param questPlayer - * @return - */ - public boolean isWonBy(LobbyPlayer questPlayer) { - return getGamesWonBy(questPlayer) >= gamesToWinMatch; - } - - public List getPlayers() { - return players; - } - - /** - * TODO: Write javadoc for this method. - * @return - */ - public static int getPoisonCountersAmountToLose() { - return 10; - } - -} +package forge.game; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.collect.Lists; +import forge.Card; +import forge.Singletons; +import forge.game.event.GameEventAnteCardsSelected; +import forge.game.player.LobbyPlayer; +import forge.game.player.Player; +import forge.properties.ForgePreferences.FPref; + +/** + * TODO: Write javadoc for this type. + * + */ + +public class Match { + + private final List players; + private final GameType gameType; + + private int gamesPerMatch = 3; + private int gamesToWinMatch = 2; + + private boolean useAnte = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ANTE); + + private final List gamesPlayed = new ArrayList(); + private final List gamesPlayedRo; + + /** + * This should become constructor once. + */ + public Match(GameType type, List players0) { + gamesPlayedRo = Collections.unmodifiableList(gamesPlayed); + players = Collections.unmodifiableList(Lists.newArrayList(players0)); + gameType = type; + } + + public Match(GameType type, List players0, Boolean overrideAnte) { + this(type, players0); + if( overrideAnte != null ) + this.useAnte = overrideAnte.booleanValue(); + } + + /** + * Gets the games played. + * + * @return the games played + */ + public final List getPlayedGames() { + return this.gamesPlayedRo; + } + + /** @return int */ + public int getGamesPerMatch() { + return gamesPerMatch; + } + + /** @return int */ + public int getGamesToWinMatch() { + return gamesToWinMatch; + } + + public void addGamePlayed(Game finished) { + if (!finished.isGameOver()) { + throw new IllegalStateException("Game is not over yet."); + } + gamesPlayed.add(finished.getOutcome()); + } + + + /** + * TODO: Write javadoc for this method. + */ + public Game createGame() { + return new Game(players, gameType, this); + } + + /** + * TODO: Write javadoc for this method. + */ + public void startGame(final Game game, final CountDownLatch latch) { + final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed; + GameNew.newGame(game, canRandomFoil, this.useAnte); + + // This code could be run run from EDT. + game.getAction().invoke(new Runnable() { + @Override + public void run() { + if (useAnte) { // Deciding which cards go to ante + List> list = GameNew.chooseCardsForAnte(game); + GameNew.moveCardsToAnte(list); + game.fireEvent(new GameEventAnteCardsSelected(list)); + } + + GameOutcome lastOutcome = gamesPlayed.isEmpty() ? null : gamesPlayed.get(gamesPlayed.size() - 1); + game.getAction().startGame(lastOutcome); + + if( null != latch ) + latch.countDown(); + } + }); + } + + public void clearGamesPlayed() { + gamesPlayed.clear(); + } + + public void clearLastGame() { + gamesPlayed.remove(gamesPlayed.size() - 1); + } + + /** + * TODO: Write javadoc for this method. + * + * @return + */ + public GameType getGameType() { + return gameType; + } + + public Iterable getOutcomes() { + return gamesPlayedRo; + } + + /** + * TODO: Write javadoc for this method. + * + * @return + */ + public boolean isMatchOver() { + int[] victories = new int[players.size()]; + for (GameOutcome go : gamesPlayed) { + LobbyPlayer winner = go.getWinningLobbyPlayer(); + int i = 0; + for (RegisteredPlayer p : players) { + if (p.getPlayer().equals(winner)) { + victories[i]++; + break; // can't have 2 winners per game + } + i++; + } + } + + for (int score : victories) { + if (score >= gamesToWinMatch) { + return true; + } + } + return gamesPlayed.size() >= gamesPerMatch; + } + + /** + * TODO: Write javadoc for this method. + * + * @param questPlayer + * @return + */ + public int getGamesWonBy(LobbyPlayer questPlayer) { + int sum = 0; + for (GameOutcome go : gamesPlayed) { + if (questPlayer.equals(go.getWinningLobbyPlayer())) { + sum++; + } + } + return sum; + } + + /** + * TODO: Write javadoc for this method. + * + * @param questPlayer + * @return + */ + public boolean isWonBy(LobbyPlayer questPlayer) { + return getGamesWonBy(questPlayer) >= gamesToWinMatch; + } + + public List getPlayers() { + return players; + } + + /** + * TODO: Write javadoc for this method. + * @return + */ + public static int getPoisonCountersAmountToLose() { + return 10; + } + +} diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 7242ce36b9e..46f67fce161 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -52,16 +52,17 @@ import forge.card.spellability.Ability; import forge.card.spellability.SpellAbility; import forge.card.staticability.StaticAbility; import forge.card.trigger.TriggerType; -import forge.game.GameActionUtil; import forge.game.Game; +import forge.game.GameActionUtil; import forge.game.GameAge; import forge.game.GameType; import forge.game.GlobalRuleChange; +import forge.game.RegisteredPlayer; import forge.game.event.GameEventLandPlayed; -import forge.game.event.GameEventPlayerLivesChanged; import forge.game.event.GameEventMulligan; import forge.game.event.GameEventPlayerControl; import forge.game.event.GameEventPlayerDamaged; +import forge.game.event.GameEventPlayerLivesChanged; import forge.game.event.GameEventPlayerPoisoned; import forge.game.event.GameEventShuffle; import forge.game.phase.PhaseHandler; @@ -72,6 +73,7 @@ import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; +import forge.gui.GuiDisplayUtil; import forge.properties.ForgePreferences.FPref; import forge.util.Lang; import forge.util.MyRandom; @@ -85,9 +87,9 @@ import forge.util.MyRandom; * @version $Id$ */ public class Player extends GameEntity implements Comparable { - + private final Map commanderDamage = new HashMap(); - + /** The poison counters. */ private int poisonCounters = 0; @@ -108,7 +110,7 @@ public class Player extends GameEntity implements Comparable { /** The num power surge lands. */ private int numPowerSurgeLands; - + /** The number of times this player has searched his library. */ private int numLibrarySearchedOwn = 0; @@ -136,10 +138,10 @@ public class Player extends GameEntity implements Comparable { /** The num drawn this turn. */ private int numDrawnThisTurn = 0; private int numDrawnThisDrawStep = 0; - + /** The num discarded this turn. */ private int numDiscardedThisTurn = 0; - + /** A list of tokens not in play, but on their way. * This list is kept in order to not break ETB-replacement * on tokens. */ @@ -170,9 +172,9 @@ public class Player extends GameEntity implements Comparable { protected PlayerController controllerCreator = null; private int teamNumber = -1; - + private Card activeScheme = null; - + private Card commander = null; /** The Constant ALL_ZONES. */ @@ -207,10 +209,19 @@ public class Player extends GameEntity implements Comparable { for (final ZoneType z : Player.ALL_ZONES) { final PlayerZone toPut = z == ZoneType.Battlefield ? new PlayerZoneBattlefield(z, this) - : new PlayerZone(z, this); - this.zones.put(z, toPut); + : new PlayerZone(z, this); + this.zones.put(z, toPut); + } + + if (isHotSeatGame(game)) { + if (game.getPlayers().size() == 1) { + Player player1 = game.getPlayers().get(0); + player1.setName("Player 1"); + this.setName("Player 2"); + } + } else { + this.setName(chooseName(GuiDisplayUtil.personalizeHuman(name))); } - this.setName(chooseName(name)); } private String chooseName(String originalName) { @@ -231,14 +242,14 @@ public class Player extends GameEntity implements Comparable { } @Override - public Game getGame() { // I'll probably regret about this + public Game getGame() { // I'll probably regret about this return game; } - + public final PlayerStatistics getStats() { return stats; } - + public final void setTeam(int iTeam) { teamNumber = iTeam; } @@ -275,7 +286,7 @@ public class Player extends GameEntity implements Comparable { game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); activeScheme = getZone(ZoneType.SchemeDeck).get(0); - // gameAction moveTo ? + // gameAction moveTo ? getZone(ZoneType.SchemeDeck).remove(activeScheme); this.getZone(ZoneType.Command).add(activeScheme); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); @@ -361,7 +372,7 @@ public class Player extends GameEntity implements Comparable { return other != this && other != null && ( other.teamNumber < 0 || other.teamNumber != this.teamNumber ); } - + // //////////////////////// @@ -469,7 +480,7 @@ public class Player extends GameEntity implements Comparable { runParams.put("Player", this); runParams.put("LifeAmount", lifeGain); game.getTriggerHandler().runTrigger(TriggerType.LifeGained, runParams, false); - + game.fireEvent(new GameEventPlayerLivesChanged(this, oldLife, life)); } else { System.out.println("Player - trying to gain negative or 0 life"); @@ -645,7 +656,7 @@ public class Player extends GameEntity implements Comparable { commanderDamage.put(source,commanderDamage.get(source) + amount); } } - + this.assignedDamage.put(source, amount); if (source.hasKeyword("Lifelink")) { source.getController().gainLife(amount, source); @@ -803,7 +814,7 @@ public class Player extends GameEntity implements Comparable { restDamage = restDamage / 2; } else if (c.getName().equals("Benevolent Unicorn")) { if (source.isSpell()) { - restDamage -= 1; + restDamage -= 1; } } else if (c.getName().equals("Divine Presence")) { if (restDamage > 3) { @@ -942,7 +953,7 @@ public class Player extends GameEntity implements Comparable { if (DEBUGShieldsWithEffects) { System.out.println("Remaining shields: " - + (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used")); + + (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used")); System.out.println("Remaining damage: " + restDamage); } } @@ -995,8 +1006,8 @@ public class Player extends GameEntity implements Comparable { } return num; } - - public final Iterable getAssignedDamageSources() { + + public final Iterable getAssignedDamageSources() { return assignedDamage.keySet(); } @@ -1042,7 +1053,7 @@ public class Player extends GameEntity implements Comparable { damageToDo = this.preventDamage(damageToDo, source, true); this.addDamageAfterPrevention(damageToDo, source, true); // damage - // prevention + // prevention // is already // checked @@ -1082,7 +1093,7 @@ public class Player extends GameEntity implements Comparable { * * @param num * a int. - * @param source + * @param source */ public final void setPoisonCounters(final int num, Card source) { int oldPoison = poisonCounters; @@ -1261,8 +1272,8 @@ public class Player extends GameEntity implements Comparable { return this.drawCards(1); } - - public boolean canMulligan() { + + public boolean canMulligan() { return !getZone(ZoneType.Hand).isEmpty(); } @@ -1277,13 +1288,13 @@ public class Player extends GameEntity implements Comparable { */ public final List drawCards(final int n) { final List drawn = new ArrayList(); - + if (!this.canDraw()) { return drawn; } - + for (int i = 0; i < n; i++) { - + // TODO: multiple replacements need to be selected by the controller List dredgers = this.getDredge(); if (!dredgers.isEmpty()) { @@ -1299,7 +1310,7 @@ public class Player extends GameEntity implements Comparable { continue; } } - + drawn.addAll(this.doDraw()); } return drawn; @@ -1325,14 +1336,14 @@ public class Player extends GameEntity implements Comparable { return drawn; } - // ======== Chains of Mephistopheles hardcode. ========= + // ======== Chains of Mephistopheles hardcode. ========= // This card requires player to either discard a card, and then he may proceed drawing, or mill 1 - then no draw will happen // It's oracle-worded as a replacement effect ("If a player would draw a card ... discards a card instead") (rule 419.1a) // Yet, gatherer's rulings read: The effect is cumulative. If there are two of these on the battlefield, each of them will modify each draw // That means player isn't supposed to choose one replacement effect out of two (generated by Chains Of Mephistopheles), but both happen. // So it's not a common replacement effect and has to be handled by special code. - // This is why the code is placed after any other replacement effects could affect the draw event. + // This is why the code is placed after any other replacement effects could affect the draw event. List chainsList = null; for(Card c : game.getCardsIn(ZoneType.Battlefield)) { if ( c.getName().equals("Chains of Mephistopheles") ) { @@ -1353,7 +1364,7 @@ public class Player extends GameEntity implements Comparable { AbilityUtils.resolve(saMill); return drawn; // Draw is cancelled - } else { + } else { SpellAbility saDiscard = AbilityFactory.getAbility(c.getSVar("DiscardOne"), c); saDiscard.setActivatingPlayer(c.getController()); saDiscard.getTargets().add(this); @@ -1361,7 +1372,7 @@ public class Player extends GameEntity implements Comparable { } } } - // End of = Chains of Mephistopheles hardcode. ========= + // End of = Chains of Mephistopheles hardcode. ========= if (!library.isEmpty()) { @@ -1511,7 +1522,7 @@ public class Player extends GameEntity implements Comparable { return CardLists.filter(this.getCardsIn(zone), CardPredicates.nameEquals(cardName)); } - + public List getCardsActivableInExternalZones() { final List cl = new ArrayList(); @@ -1580,7 +1591,7 @@ public class Player extends GameEntity implements Comparable { } } return 0; -// throw new RuntimeException("Input_Draw : getDredgeNumber() card doesn't have dredge - " + c.getName()); + // throw new RuntimeException("Input_Draw : getDredgeNumber() card doesn't have dredge - " + c.getName()); } // getDredgeNumber() /** @@ -1690,7 +1701,7 @@ public class Player extends GameEntity implements Comparable { public final int getNumDiscardedThisTurn() { return this.numDiscardedThisTurn; } - + /** *

* Getter for the field numDiscardedThisTurn. @@ -1791,7 +1802,7 @@ public class Player extends GameEntity implements Comparable { // Play the shuffle sound game.fireEvent(new GameEventShuffle(this)); } // shuffle - // ////////////////////////////// + // ////////////////////////////// // ////////////////////////////// @@ -1860,7 +1871,7 @@ public class Player extends GameEntity implements Comparable { } } } - + if( land != null && !ignoreZoneAndTiming) { if (land.getOwner() != this && !land.hasKeyword("May be played by your opponent")) return false; @@ -1874,7 +1885,7 @@ public class Player extends GameEntity implements Comparable { // **** Check for land play limit per turn **** // Dev Mode if (this.getLobbyPlayer().getType() == PlayerType.HUMAN && Preferences.DEV_MODE && - Singletons.getModel().getPreferences().getPrefBoolean(FPref.DEV_UNLIMITED_LAND)) { + Singletons.getModel().getPreferences().getPrefBoolean(FPref.DEV_UNLIMITED_LAND)) { return true; } if (this.isCardInPlay("Fastbond") || this.isCardInCommand("Naya")) { @@ -1979,7 +1990,7 @@ public class Player extends GameEntity implements Comparable { * * @param s * a {@link forge.Card} object. - * @return + * @return * @return a {@link forge.Card} object. */ public final void setNamedCard(final String s) { @@ -2176,14 +2187,14 @@ public class Player extends GameEntity implements Comparable { return this.getOutcome().lossState != null; } - // Rule 704.5a - If a player has 0 or less life, he or she loses the game. + // Rule 704.5a - If a player has 0 or less life, he or she loses the game. final boolean hasNoLife = this.getLife() <= 0; if (hasNoLife && !this.cantLoseForZeroOrLessLife()) { return this.loseConditionMet(GameLossReason.LifeReachedZero, null); } - + // Rule 704.5b - If a player attempted to draw a card from a library with no cards in it - // since the last time state-based actions were checked, he or she loses the game. + // since the last time state-based actions were checked, he or she loses the game. if (triedToDrawFromEmptyLibrary) { triedToDrawFromEmptyLibrary = false; // one-shot check return this.loseConditionMet(GameLossReason.Milled, null); @@ -2193,7 +2204,7 @@ public class Player extends GameEntity implements Comparable { if (this.poisonCounters >= 10) { return this.loseConditionMet(GameLossReason.Poisoned, null); } - + if(game.getType() == GameType.Commander) { Map cmdDmg = getCommanderDamage(); @@ -2201,13 +2212,13 @@ public class Player extends GameEntity implements Comparable { { if(cmdDmg.get(c) >= 21) return this.loseConditionMet(GameLossReason.CommanderDamage, null); - } + } } return false; } - public final boolean hasLost() { + public final boolean hasLost() { return this.getOutcome() != null && this.getOutcome().lossState != null; } @@ -2330,11 +2341,11 @@ public class Player extends GameEntity implements Comparable { public final void resetProwl() { this.prowl = new ArrayList(); } - + public final void setLibrarySearched(final int l) { this.numLibrarySearchedOwn = l; } - + public final int getLibrarySearched() { return this.numLibrarySearchedOwn; } @@ -2378,7 +2389,7 @@ public class Player extends GameEntity implements Comparable { if (incR.length > 1) { final String excR = incR[1]; final String[] exR = excR.split("\\+"); // Exclusive Restrictions - // are ... + // are ... for (int j = 0; j < exR.length; j++) { if (!this.hasProperty(exR[j], sourceController, source)) { return false; @@ -2466,7 +2477,7 @@ public class Player extends GameEntity implements Comparable { final Player controller = "Active".equals(property.split("Than")[1]) ? game.getPhaseHandler().getPlayerTurn() : sourceController; if (property.substring(7).startsWith("Life") && this.getLife() <= controller.getLife()) { return false; - } else if (property.substring(7).startsWith("CardsInHand") + } else if (property.substring(7).startsWith("CardsInHand") && this.getCardsIn(ZoneType.Hand).size() <= controller.getCardsIn(ZoneType.Hand).size()) { return false; } @@ -2624,7 +2635,7 @@ public class Player extends GameEntity implements Comparable { public final int getLifeGainedThisTurn() { return this.lifeGainedThisTurn; } - + /** *

* Setter for the field lifeGainedThisTurn. @@ -2766,11 +2777,11 @@ public class Player extends GameEntity implements Comparable { public final LobbyPlayer getOriginalLobbyPlayer() { return controllerCreator.getLobbyPlayer(); } - + public final boolean isMindSlaved() { return controller.getLobbyPlayer() != controllerCreator.getLobbyPlayer(); } - + public final void releaseControl() { if ( controller == controllerCreator ) return; @@ -2835,7 +2846,7 @@ public class Player extends GameEntity implements Comparable { public int getCounterDoublersMagnitude(final CounterType type) { int counterDoublers = getCardsIn(ZoneType.Battlefield, "Doubling Season").size() - + CardLists.filter(getGame().getCardsIn(ZoneType.Command), + + CardLists.filter(getGame().getCardsIn(ZoneType.Command), CardPredicates.nameEquals("Selesnya Loft Gardens")).size(); if (type == CounterType.P1P1) { counterDoublers += getCardsIn(ZoneType.Battlefield, "Corpsejack Menace").size(); @@ -2846,9 +2857,9 @@ public class Player extends GameEntity implements Comparable { public int getTokenDoublersMagnitude() { final int tokenDoublers = getCardsIn(ZoneType.Battlefield, "Parallel Lives").size() + getCardsIn(ZoneType.Battlefield, "Doubling Season").size() - + CardLists.filter(getGame().getCardsIn(ZoneType.Command), + + CardLists.filter(getGame().getCardsIn(ZoneType.Command), CardPredicates.nameEquals("Selesnya Loft Gardens")).size();; - return 1 << tokenDoublers; // pow(a,0) = 1; pow(a,1) = a + return 1 << tokenDoublers; // pow(a,0) = 1; pow(a,1) = a } public void onCleanupPhase() { @@ -2911,7 +2922,7 @@ public class Player extends GameEntity implements Comparable { } public final void setFirstController(PlayerController ctrlr) { if( null != controllerCreator ) throw new IllegalStateException("Controller creator already assigned"); - controllerCreator = ctrlr; + controllerCreator = ctrlr; controller = ctrlr; } /** @@ -2981,13 +2992,13 @@ public class Player extends GameEntity implements Comparable { /** * * Takes the top plane of the planar deck and put it face up in the command zone. - * Then runs triggers. + * Then runs triggers. */ public void planeswalk() { planeswalkTo(Arrays.asList(getZone(ZoneType.PlanarDeck).get(0))); } - + /** * Puts the planes in the argument and puts them face up in the command zone. * Then runs triggers. @@ -3003,19 +3014,19 @@ public class Player extends GameEntity implements Comparable { getZone(ZoneType.PlanarDeck).remove(c); getZone(ZoneType.Command).add(c); } - + //DBG //System.out.println("CurrentPlanes: " + currentPlanes); //System.out.println("ActivePlanes: " + game.getActivePlanes()); //System.out.println("CommandPlanes: " + getZone(ZoneType.Command).getCards()); - + game.setActivePlanes(currentPlanes); //Run PlaneswalkedTo triggers here. HashMap runParams = new HashMap(); runParams.put("Card", currentPlanes); game.getTriggerHandler().runTrigger(TriggerType.PlaneswalkedTo, runParams,false); } - + /** * * Puts my currently active planes, if any, at the bottom of my planar deck. @@ -3024,11 +3035,11 @@ public class Player extends GameEntity implements Comparable { { if(!currentPlanes.isEmpty()) { - //Run PlaneswalkedFrom triggers here. + //Run PlaneswalkedFrom triggers here. HashMap runParams = new HashMap(); runParams.put("Card", new ArrayList(currentPlanes)); game.getTriggerHandler().runTrigger(TriggerType.PlaneswalkedFrom, runParams,false); - + for(Card c : currentPlanes) { game.getZoneOf(c).remove(c); c.clearControllers(); @@ -3042,7 +3053,7 @@ public class Player extends GameEntity implements Comparable { //System.out.println("ActivePlanes: " + game.getActivePlanes()); //System.out.println("CommandPlanes: " + getZone(ZoneType.Command).getCards()); } - + /** * * Sets up the first plane of a round. @@ -3065,7 +3076,7 @@ public class Player extends GameEntity implements Comparable { break; } } - + game.setActivePlanes(currentPlanes); } @@ -3080,7 +3091,7 @@ public class Player extends GameEntity implements Comparable { public final void resetAttackedThisCombat() { // resets the status of attacked/blocked this phase List list = CardLists.filter(getCardsIn(ZoneType.Battlefield), Presets.CREATURES); - + for (int i = 0; i < list.size(); i++) { final Card c = list.get(i); if (c.getDamageHistory().getCreatureAttackedThisCombat()) { @@ -3089,7 +3100,7 @@ public class Player extends GameEntity implements Comparable { if (c.getDamageHistory().getCreatureBlockedThisCombat()) { c.getDamageHistory().setCreatureBlockedThisCombat(false); } - + if (c.getDamageHistory().getCreatureGotBlockedThisCombat()) { c.getDamageHistory().setCreatureGotBlockedThisCombat(false); } @@ -3120,41 +3131,41 @@ public class Player extends GameEntity implements Comparable { miracleTrigger.setStackDescription(card.getName() + " - Miracle."); miracleTrigger.setActivatingPlayer(card.getOwner()); miracleTrigger.setTrigger(true); - + game.getStack().add(miracleTrigger); } public boolean isSkippingDraw() { - + if (hasKeyword("Skip your next draw step.")) { removeKeyword("Skip your next draw step."); return true; } - + if (hasKeyword("Skip your draw step.")) { return true; } - + return false; } - + public void addInboundToken(Card c) { inboundTokens.add(c); } - + public void removeInboundToken(Card c) { inboundTokens.remove(c); } - /** + /** * TODO: Write javadoc for this type. * */ private final class MiracleTrigger extends Ability { private final SpellAbility miracle; - + /** * TODO: Write javadoc for Constructor. * @param sourceCard @@ -3165,7 +3176,7 @@ public class Player extends GameEntity implements Comparable { super(sourceCard, manaCost); this.miracle = miracle; } - + @Override public void resolve() { miracle.setActivatingPlayer(getSourceCard().getOwner()); @@ -3206,11 +3217,11 @@ public class Player extends GameEntity implements Comparable { } /** - * @param b isPlayingExtraTurn to set + * @param b isPlayingExtraTurn to set */ public void setExtraTurn(boolean b) { this.isPlayingExtraTrun = b; - + } /** @@ -3219,4 +3230,15 @@ public class Player extends GameEntity implements Comparable { public boolean isPlayingExtraTurn() { return isPlayingExtraTrun; } + + private boolean isHotSeatGame(Game game0) { + boolean isHotSeat = false; + List players = game0.getMatch().getPlayers(); + if (players.size() == 2) { + boolean isPlayer1Human = players.get(0).getPlayer().getType() == PlayerType.HUMAN; + boolean isPlayer2Human = players.get(1).getPlayer().getType() == PlayerType.HUMAN; + isHotSeat = (isPlayer1Human && isPlayer2Human); + } + return isHotSeat; + } } diff --git a/src/main/java/forge/gui/GuiDisplayUtil.java b/src/main/java/forge/gui/GuiDisplayUtil.java index aef90933e7e..ab38b4444c5 100644 --- a/src/main/java/forge/gui/GuiDisplayUtil.java +++ b/src/main/java/forge/gui/GuiDisplayUtil.java @@ -32,6 +32,7 @@ import java.util.Map.Entry; import javax.swing.JFileChooser; import javax.swing.JOptionPane; + import com.google.common.base.Predicates; import com.google.common.collect.Lists; @@ -53,8 +54,9 @@ import forge.game.player.HumanPlay; import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.input.InputSelectCardsFromList; -import forge.item.PaperCard; import forge.item.IPaperCard; +import forge.item.PaperCard; +import forge.properties.ForgePreferences.FPref; public final class GuiDisplayUtil { private GuiDisplayUtil() { @@ -122,7 +124,7 @@ public final class GuiDisplayUtil { else if (categoryName.equals("aicardsinlibrary")) aiCardTexts.put(ZoneType.Library, categoryValue); else if (categoryName.equals("humancardsinexile")) humanCardTexts.put(ZoneType.Exile, categoryValue); else if (categoryName.equals("aicardsinexile")) aiCardTexts.put(ZoneType.Exile, categoryValue); - + } in.close(); @@ -138,7 +140,7 @@ public final class GuiDisplayUtil { private static void setupGameState(final int humanLife, final int computerLife, final Map humanCardTexts, final Map aiCardTexts, final String tChangePlayer, final String tChangePhase) { - + final Game game = getGame(); game.getAction().invoke(new Runnable() { @Override @@ -148,17 +150,17 @@ public final class GuiDisplayUtil { Player newPlayerTurn = tChangePlayer.equals("human") ? newPlayerTurn = human : tChangePlayer.equals("ai") ? newPlayerTurn = ai : null; PhaseType newPhase = tChangePhase.trim().equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase); - + game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn); - - + + game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - + devSetupPlayerState(humanLife, humanCardTexts, human); devSetupPlayerState(computerLife, aiCardTexts, ai); - + game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); - + game.getAction().checkStaticAbilities(); } }); @@ -242,7 +244,7 @@ public final class GuiDisplayUtil { final Card c = GuiChoose.oneOrNone("Choose a card", lib); if (null == c) return; - + getGame().getAction().invoke(new Runnable() { @Override public void run() { getGame().getAction().moveToHand(c); }}); } @@ -311,8 +313,8 @@ public final class GuiDisplayUtil { */ public static void devModeUntapPerm() { final Game game = getGame(); - - + + game.getAction().invoke(new Runnable() { @Override @@ -380,7 +382,7 @@ public final class GuiDisplayUtil { return; } - getGame().getAction().invoke(new Runnable() { @Override public void run() { + getGame().getAction().invoke(new Runnable() { @Override public void run() { Card forgeCard = c.toForgeCard(p); getGame().getAction().moveToHand(forgeCard); }}); @@ -410,20 +412,20 @@ public final class GuiDisplayUtil { if (c.getRules().getType().isLand()) { forgeCard.setOwner(p); game.getAction().moveToPlay(forgeCard); - + } else { final List choices = forgeCard.getBasicSpells(); if (choices.isEmpty()) { return; // when would it happen? } - + final SpellAbility sa = choices.size() == 1 ? choices.get(0) : GuiChoose.oneOrNone("Choose", choices); if (sa == null) { return; // happens if cancelled } - - game.getAction().moveToHand(forgeCard); // this is really needed (for rollbacks at least) - // Human player is choosing targets for an ability controlled by chosen player. + + game.getAction().moveToHand(forgeCard); // this is really needed (for rollbacks at least) + // Human player is choosing targets for an ability controlled by chosen player. sa.setActivatingPlayer(p); HumanPlay.playSaWithoutPayingManaCost(game, sa, true); } @@ -440,26 +442,26 @@ public final class GuiDisplayUtil { if (null == p) { return; } - + PlanarDice res = GuiChoose.oneOrNone("Choose result", PlanarDice.values()); if(res == null) return; - + System.out.println("Rigging planar dice roll: " + res.toString()); //DBG //System.out.println("ActivePlanes: " + getGame().getActivePlanes()); //System.out.println("CommandPlanes: " + getGame().getCardsIn(ZoneType.Command)); - + PlanarDice.roll(p, res); - + getGame().getAction().invoke(new Runnable() { @Override public void run() { p.getGame().getStack().chooseOrderOfSimultaneousStackEntryAll(); } }); - + } public static void devModePlaneswalkTo() { @@ -496,5 +498,14 @@ public final class GuiDisplayUtil { return Singletons.getControl().getObservedGame(); } + public static String getPlayerName() { + return Singletons.getModel().getPreferences().getPref(FPref.PLAYER_NAME); + } + + public static String personalizeHuman(String text) { + String playerName = Singletons.getModel().getPreferences().getPref(FPref.PLAYER_NAME); + return text.replaceAll("(?i)human", playerName); + } + } // end class GuiDisplayUtil diff --git a/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java b/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java index 8275cc89463..3d89bc9fd0c 100644 --- a/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java +++ b/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java @@ -3,12 +3,16 @@ package forge.gui.home.settings; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.Pair; import forge.Command; @@ -21,15 +25,12 @@ import forge.game.ai.AiProfileUtil; import forge.gui.framework.ICDoc; import forge.gui.framework.SLayoutIO; import forge.gui.toolbox.FComboBoxPanel; +import forge.gui.toolbox.FLabel; import forge.gui.toolbox.FSkin; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** +/** * Controls the preferences submenu in the home UI. * *

(C at beginning of class name denotes a control class.) @@ -38,10 +39,10 @@ import java.util.List; public enum CSubmenuPreferences implements ICDoc { /** */ SINGLETON_INSTANCE; - + private VSubmenuPreferences view; private ForgePreferences prefs; - + private final List> lstControls = new ArrayList>(); /* (non-Javadoc) @@ -50,11 +51,11 @@ public enum CSubmenuPreferences implements ICDoc { @SuppressWarnings("serial") @Override public void initialize() { - + this.view = VSubmenuPreferences.SINGLETON_INSTANCE; this.prefs = Singletons.getModel().getPreferences(); - - // This updates variable right now and is not standard + + // This updates variable right now and is not standard view.getCbDevMode().addItemListener(new ItemListener() { @Override public void itemStateChanged(final ItemEvent arg0) { @@ -64,8 +65,8 @@ public enum CSubmenuPreferences implements ICDoc { prefs.save(); } }); - - lstControls.clear(); // just in case + + lstControls.clear(); // just in case lstControls.add(Pair.of(view.getCbAnte(), FPref.UI_ANTE)); lstControls.add(Pair.of(view.getCbManaBurn(), FPref.UI_MANABURN)); lstControls.add(Pair.of(view.getCbScaleLarger(), FPref.UI_SCALE_LARGER)); @@ -76,15 +77,15 @@ public enum CSubmenuPreferences implements ICDoc { lstControls.add(Pair.of(view.getCbSingletons(), FPref.DECKGEN_SINGLETONS)); lstControls.add(Pair.of(view.getCbUploadDraft(), FPref.UI_UPLOAD_DRAFT)); lstControls.add(Pair.of(view.getCbStackLand(), FPref.UI_SMOOTH_LAND)); - lstControls.add(Pair.of(view.getCbRandomFoil(), FPref.UI_RANDOM_FOIL)); + lstControls.add(Pair.of(view.getCbRandomFoil(), FPref.UI_RANDOM_FOIL)); lstControls.add(Pair.of(view.getCbRandomizeArt(), FPref.UI_RANDOM_CARD_ART)); lstControls.add(Pair.of(view.getCbEnableSounds(), FPref.UI_ENABLE_SOUNDS)); lstControls.add(Pair.of(view.getCbAltSoundSystem(), FPref.UI_ALT_SOUND_SYSTEM)); lstControls.add(Pair.of(view.getCbUiForTouchScreen(), FPref.UI_FOR_TOUCHSCREN)); - lstControls.add(Pair.of(view.getCbCompactMainMenu(), FPref.UI_COMPACT_MAIN_MENU)); + lstControls.add(Pair.of(view.getCbCompactMainMenu(), FPref.UI_COMPACT_MAIN_MENU)); lstControls.add(Pair.of(view.getCbOverlayCardName(), FPref.UI_OVERLAY_CARD_NAME)); lstControls.add(Pair.of(view.getCbOverlayCardPower(), FPref.UI_OVERLAY_CARD_POWER)); - lstControls.add(Pair.of(view.getCbOverlayCardManaCost(), FPref.UI_OVERLAY_CARD_MANA_COST)); + lstControls.add(Pair.of(view.getCbOverlayCardManaCost(), FPref.UI_OVERLAY_CARD_MANA_COST)); lstControls.add(Pair.of(view.getCbShowMatchBackgroundImage(), FPref.UI_MATCH_IMAGE_VISIBLE)); lstControls.add(Pair.of(view.getCbUseThemedComboBox(), FPref.UI_THEMED_COMBOBOX)); lstControls.add(Pair.of(view.getCbPromptFreeBlocks(), FPref.MATCHPREF_PROMPT_FREE_BLOCKS)); @@ -105,39 +106,41 @@ public enum CSubmenuPreferences implements ICDoc { CSubmenuPreferences.this.resetForgeSettingsToDefault(); } }); - + view.getBtnDeleteEditorUI().setCommand(new Command() { @Override public void run() { CSubmenuPreferences.this.resetDeckEditorLayout(); } }); - + view.getBtnDeleteMatchUI().setCommand(new Command() { @Override public void run() { CSubmenuPreferences.this.resetMatchScreenLayout(); } }); - + initializeGameLogVerbosityComboBox(); initializeAiProfilesComboBox(); - initializeSkinsComboBox(); - + initializeSkinsComboBox(); + initializePlayerNameButton(); + } - - + + /* (non-Javadoc) * @see forge.control.home.IControlSubmenu#update() */ @Override public void update() { - + this.view = VSubmenuPreferences.SINGLETON_INSTANCE; this.prefs = Singletons.getModel().getPreferences(); - + + setPlayerNameButtonText(); view.getCbDevMode().setSelected(prefs.getPrefBoolean(FPref.DEV_MODE_ENABLED)); - + for(Pair kv: lstControls) { kv.getKey().setSelected(prefs.getPrefBoolean(kv.getValue())); } @@ -147,7 +150,7 @@ public enum CSubmenuPreferences implements ICDoc { @Override public void run() { view.getCbRemoveSmall().requestFocusInWindow(); } }); } - + /* (non-Javadoc) * @see forge.gui.framework.ICDoc#getCommandOnSelect() */ @@ -155,50 +158,50 @@ public enum CSubmenuPreferences implements ICDoc { public Command getCommandOnSelect() { return null; } - + private void resetForgeSettingsToDefault() { - String userPrompt = + String userPrompt = "This will reset all preferences to their defaults and restart Forge.\n\n" + - "Reset and restart Forge?"; + "Reset and restart Forge?"; int reply = JOptionPane.showConfirmDialog(null, userPrompt, "Reset Settings", JOptionPane.YES_NO_OPTION); if (reply == JOptionPane.YES_OPTION) { ForgePreferences prefs = Singletons.getModel().getPreferences(); prefs.reset(); prefs.save(); - update(); - RestartUtil.restartApplication(null); - } + update(); + RestartUtil.restartApplication(null); + } } - + private void resetDeckEditorLayout() { - String userPrompt = + String userPrompt = "This will reset the Deck Editor screen layout.\n" + - "All tabbed views will be restored to their default positions.\n\n" + - "Reset layout?"; + "All tabbed views will be restored to their default positions.\n\n" + + "Reset layout?"; int reply = JOptionPane.showConfirmDialog(null, userPrompt, "Reset Deck Editor Layout", JOptionPane.YES_NO_OPTION); if (reply == JOptionPane.YES_OPTION) { deleteScreenLayoutFile(Screens.DECK_EDITOR_CONSTRUCTED); JOptionPane.showMessageDialog(null, "Deck Editor layout has been reset."); - } + } } - + private void resetMatchScreenLayout() { - String userPrompt = + String userPrompt = "This will reset the layout of the Match screen.\n" + - "If you want to save the current layout first, please use " + - "the Dock tab -> Save Layout option in the Match screen.\n\n" + - "Reset layout?"; + "If you want to save the current layout first, please use " + + "the Dock tab -> Save Layout option in the Match screen.\n\n" + + "Reset layout?"; int reply = JOptionPane.showConfirmDialog(null, userPrompt, "Reset Match Screen Layout", JOptionPane.YES_NO_OPTION); if (reply == JOptionPane.YES_OPTION) { deleteScreenLayoutFile(Screens.MATCH_SCREEN); - JOptionPane.showMessageDialog(null, "Match Screen layout has been reset."); - } + JOptionPane.showMessageDialog(null, "Match Screen layout has been reset."); + } } - + private void deleteScreenLayoutFile(Screens screen) { String fd = SLayoutIO.getFilePreferred(screen); File f = new File(fd); - f.delete(); + f.delete(); } private void initializeGameLogVerbosityComboBox() { @@ -207,16 +210,16 @@ public enum CSubmenuPreferences implements ICDoc { JComboBox comboBox = createComboBox(GameLogEntryType.values(), userSetting); GameLogEntryType selectedItem = GameLogEntryType.valueOf(this.prefs.getPref(userSetting)); panel.setComboBox(comboBox, selectedItem); - } - + } + private void initializeAiProfilesComboBox() { - FPref userSetting = FPref.UI_CURRENT_AI_PROFILE; + FPref userSetting = FPref.UI_CURRENT_AI_PROFILE; FComboBoxPanel panel = this.view.getAiProfilesComboBoxPanel(); JComboBox comboBox = createComboBox(AiProfileUtil.getProfilesArray(), userSetting); String selectedItem = this.prefs.getPref(userSetting); - panel.setComboBox(comboBox, selectedItem); + panel.setComboBox(comboBox, selectedItem); } - + private void initializeSkinsComboBox() { final FComboBoxPanel panel = this.view.getSkinsComboBoxPanel(); String[] installedSkins = FSkin.getSkinNamesArray(true); @@ -242,13 +245,13 @@ public enum CSubmenuPreferences implements ICDoc { this.prefs.setPref(FPref.UI_SKIN, "Default"); } } - + private JComboBox createComboBox(E[] items, final ForgePreferences.FPref setting) { final JComboBox comboBox = new JComboBox(items); addComboBoxListener(comboBox, setting); return comboBox; } - + private void addComboBoxListener(final JComboBox comboBox, final ForgePreferences.FPref setting) { comboBox.addItemListener(new ItemListener() { @SuppressWarnings("unchecked") @@ -260,4 +263,29 @@ public enum CSubmenuPreferences implements ICDoc { } }); } + + + private void initializePlayerNameButton() { + FLabel btn = view.getBtnPlayerName(); + setPlayerNameButtonText(); + btn.setCommand(getPlayerNameButtonCommand()); + } + + private void setPlayerNameButtonText() { + FLabel btn = view.getBtnPlayerName(); + String name = prefs.getPref(FPref.PLAYER_NAME); + btn.setText(StringUtils.isBlank(name) ? "Human" : name); + } + + @SuppressWarnings("serial") + private Command getPlayerNameButtonCommand() { + return new Command() { + @Override + public void run() { + GamePlayerUtil.setPlayerName(); + setPlayerNameButtonText(); + } + }; + } + } diff --git a/src/main/java/forge/gui/home/settings/GamePlayerUtil.java b/src/main/java/forge/gui/home/settings/GamePlayerUtil.java new file mode 100644 index 00000000000..f4361bfd1b5 --- /dev/null +++ b/src/main/java/forge/gui/home/settings/GamePlayerUtil.java @@ -0,0 +1,70 @@ +package forge.gui.home.settings; + +import javax.swing.JOptionPane; + +import org.apache.commons.lang.StringUtils; + +import forge.Singletons; +import forge.properties.ForgePreferences; +import forge.properties.ForgePreferences.FPref; + +public final class GamePlayerUtil { + private GamePlayerUtil() { }; + + private final static ForgePreferences prefs = Singletons.getModel().getPreferences(); + + public static void setPlayerName() { + + String playerName = prefs.getPref(FPref.PLAYER_NAME); + String newName = null; + + if (StringUtils.isBlank(playerName)) { + newName = (String)JOptionPane.showInputDialog( + Singletons.getView().getFrame(), + "By default, Forge will refer to you as the \"Human\" during gameplay.\n" + + "If you would prefer a different name please enter it now.\n", + "Personalize Forge Gameplay", + JOptionPane.QUESTION_MESSAGE, + null, null, null); + } else { + newName = getNewPlayerNameFromInputDialog(playerName); + } + + if (newName == null || !StringUtils.isAlphanumericSpace(newName)) { + newName = (StringUtils.isBlank(playerName) ? "Human" : playerName); + } else if (StringUtils.isWhitespace(newName)) { + newName = "Human"; + } else { + newName = newName.trim(); + } + + prefs.setPref(FPref.PLAYER_NAME, newName); + prefs.save(); + + if (StringUtils.isBlank(playerName) && newName != "Human") { + JOptionPane.showMessageDialog( + Singletons.getView().getFrame(), + "Thank you, " + newName + ". " + + "You will not be prompted again but you can change\nyour name at any time using the \"Player Name\" setting in Preferences.\n\n"); + } + + } + + private static String getNewPlayerNameFromInputDialog(String playerName) { + String newName = + (String)JOptionPane.showInputDialog( + Singletons.getView().getFrame(), + "Please enter a new name (alpha-numeric only)\n", + "Personalize Forge Gameplay", + JOptionPane.PLAIN_MESSAGE, + null, null, playerName); + if (newName == null || !StringUtils.isAlphanumericSpace(newName)) { + return playerName; + } else if (StringUtils.isWhitespace(newName)) { + return "Human"; + } else { + return newName.trim(); + } + } + +} diff --git a/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java b/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java index 0176ac41b5c..e48faf66406 100644 --- a/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java +++ b/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java @@ -1,6 +1,7 @@ package forge.gui.home.settings; import java.awt.Color; +import java.awt.Font; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.KeyAdapter; @@ -17,6 +18,7 @@ import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; + import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.StringUtils; @@ -38,7 +40,7 @@ import forge.gui.toolbox.FScrollPane; import forge.gui.toolbox.FSkin; import forge.properties.ForgePreferences.FPref; -/** +/** * Assembles Swing components of preferences submenu singleton. * *

(V at beginning of class name denotes a view class.) @@ -59,7 +61,8 @@ public enum VSubmenuPreferences implements IVSubmenu { private final FLabel btnReset = new FLabel.Builder().opaque(true).hoverable(true).text("Reset to Default Settings").build(); private final FLabel btnDeleteMatchUI = new FLabel.Builder().opaque(true).hoverable(true).text("Reset Match Layout").build(); private final FLabel btnDeleteEditorUI = new FLabel.Builder().opaque(true).hoverable(true).text("Reset Editor Layout").build(); - + private final FLabel btnPlayerName = new FLabel.Builder().opaque(true).hoverable(true).text("").build(); + private final JCheckBox cbRemoveSmall = new OptionsCheckBox("Remove Small Creatures"); private final JCheckBox cbSingletons = new OptionsCheckBox("Singleton Mode"); private final JCheckBox cbRemoveArtifacts = new OptionsCheckBox("Remove Artifacts"); @@ -87,47 +90,50 @@ public enum VSubmenuPreferences implements IVSubmenu { private final Map shortcutFields = new HashMap(); // ComboBox items are added in CSubmenuPreferences since this is just the View. - private final FComboBoxPanel cbpSkin = new FComboBoxPanel("Choose Skin:"); + private final FComboBoxPanel cbpSkin = new FComboBoxPanel("Choose Skin:"); private final FComboBoxPanel cbpGameLogEntryType = new FComboBoxPanel("Game Log Verbosity:"); private final FComboBoxPanel cbpAiProfiles = new FComboBoxPanel("AI Personality:"); - + /** * Constructor. */ private VSubmenuPreferences() { - + pnlPrefs.setOpaque(false); pnlPrefs.setLayout(new MigLayout("insets 0, gap 0, wrap 2")); // Spacing between components is defined here. final String sectionConstraints = "w 80%!, h 42px!, gap 10% 0 10px 10px, span 2 1"; final String regularConstraints = "w 80%!, h 22px!, gap 10% 0 0 10px, span 2 1"; - - + + // Troubleshooting pnlPrefs.add(new SectionLabel("Troubleshooting"), sectionConstraints); - + //pnlPrefs.add(new SectionLabel(" "), sectionConstraints); pnlPrefs.add(btnReset, regularConstraints + ", h 30px!"); - + final String twoButtonConstraints = "w 38%!, h 30px!, gap 10% 0 0 10px"; pnlPrefs.add(btnDeleteMatchUI, twoButtonConstraints); pnlPrefs.add(btnDeleteEditorUI, "w 38%!, h 30px!, gap 0 0 0 10px"); // Reset button - + // General Configuration pnlPrefs.add(new SectionLabel("General Configuration"), sectionConstraints + ", gaptop 2%"); - + + pnlPrefs.add(getPlayerNamePanel(), regularConstraints + ", h 26px!"); + pnlPrefs.add(new NoteLabel("Sets the name that you will be referred to by Forge during gameplay."), regularConstraints); + pnlPrefs.add(cbCompactMainMenu, regularConstraints); pnlPrefs.add(new NoteLabel("Enable for a space efficient sidebar that displays only one menu group at a time (RESTART REQUIRED)."), regularConstraints); - - + + // Gameplay Options pnlPrefs.add(new SectionLabel("Gameplay"), sectionConstraints + ", gaptop 2%"); - + pnlPrefs.add(cbpAiProfiles, "w 80%!, gap 10% 0 0 10px, span 2 1"); - pnlPrefs.add(new NoteLabel("Choose your AI opponent."), regularConstraints); + pnlPrefs.add(new NoteLabel("Choose your AI opponent."), regularConstraints); pnlPrefs.add(cbAnte, regularConstraints); pnlPrefs.add(new NoteLabel("Determines whether or not the game is played for ante."), regularConstraints); @@ -161,7 +167,7 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbRemoveArtifacts, regularConstraints); pnlPrefs.add(new NoteLabel("Disables artifact cards in generated decks."), regularConstraints); - + // Advanced pnlPrefs.add(new SectionLabel("Advanced Settings"), sectionConstraints); @@ -174,10 +180,10 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbUseThemedComboBox, regularConstraints); pnlPrefs.add(new NoteLabel("Turn off if you are having combo-box color clash (RESTART REQUIRED)."), regularConstraints); - + // Themes pnlPrefs.add(new SectionLabel("Visual Themes"), sectionConstraints + ", gaptop 2%"); - + pnlPrefs.add(cbpSkin, "w 80%!, gap 10% 0 0 10px, span 2 1"); pnlPrefs.add(new NoteLabel("Change the overall look and feel of Forge (RESTART REQUIRED)."), regularConstraints); @@ -207,7 +213,7 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbOverlayCardPower, regularConstraints); pnlPrefs.add(cbOverlayCardManaCost, regularConstraints); - + // Sound options pnlPrefs.add(new SectionLabel("Sound Options"), sectionConstraints + ", gaptop 2%"); @@ -216,8 +222,8 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbAltSoundSystem, regularConstraints); pnlPrefs.add(new NoteLabel("Use the alternate sound system (only use in case your have issues with sound not playing or disappearing)"), regularConstraints); - - + + // Keyboard shortcuts final JLabel lblShortcuts = new SectionLabel("Keyboard Shortcuts"); pnlPrefs.add(lblShortcuts, sectionConstraints + ", gaptop 2%"); @@ -234,7 +240,7 @@ public enum VSubmenuPreferences implements IVSubmenu { scrContent.setBorder(null); } - + public void reloadShortcuts() { for (Map.Entry e : shortcutFields.entrySet()) { e.getValue().reload(e.getKey()); @@ -396,11 +402,11 @@ public enum VSubmenuPreferences implements IVSubmenu { this.setText(StringUtils.join(displayText, ' ')); } } - + /** @return {@link javax.swing.JCheckBox} */ public final JCheckBox getCbCompactMainMenu() { return cbCompactMainMenu; - } + } /** @return {@link javax.swing.JCheckBox} */ public final JCheckBox getCbRemoveSmall() { @@ -441,7 +447,7 @@ public enum VSubmenuPreferences implements IVSubmenu { public JCheckBox getCbOverlayCardManaCost() { return cbOverlayCardManaCost; } - + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbRandomFoil() { return cbRandomFoil; @@ -471,7 +477,7 @@ public enum VSubmenuPreferences implements IVSubmenu { public JCheckBox getCbDevMode() { return cbDevMode; } - + public FComboBoxPanel getAiProfilesComboBoxPanel() { return cbpAiProfiles; } @@ -479,11 +485,11 @@ public enum VSubmenuPreferences implements IVSubmenu { public FComboBoxPanel getGameLogVerbosityComboBoxPanel() { return cbpGameLogEntryType; } - + public FComboBoxPanel getSkinsComboBoxPanel() { return cbpSkin; } - + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbEnforceDeckLegality() { return cbEnforceDeckLegality; @@ -506,7 +512,7 @@ public enum VSubmenuPreferences implements IVSubmenu { /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbAltSoundSystem() { - return cbAltSoundSystem; + return cbAltSoundSystem; } public final JCheckBox getCbUiForTouchScreen() { @@ -517,17 +523,21 @@ public enum VSubmenuPreferences implements IVSubmenu { public FLabel getBtnReset() { return btnReset; } - + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbShowMatchBackgroundImage() { return cbShowMatchBackgroundImage; } - + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbUseThemedComboBox() { return cbUseThemedComboBox; - } - + } + + public FLabel getBtnPlayerName() { + return btnPlayerName; + } + //========== Overridden from IVDoc public final FLabel getBtnDeleteMatchUI() { @@ -577,4 +587,13 @@ public enum VSubmenuPreferences implements IVSubmenu { public DragCell getParentCell() { return parentCell; } + + private JPanel getPlayerNamePanel() { + JPanel p = new JPanel(new MigLayout("insets 0, gap 0!")); + p.setOpaque(false); + FLabel lbl = new FLabel.Builder().text("Player Name: ").fontSize(12).fontStyle(Font.BOLD).build(); + p.add(lbl, "aligny top, h 100%"); + p.add(btnPlayerName, "aligny top, h 100%, w 200px!"); + return p; + } } diff --git a/src/main/java/forge/gui/match/QuestWinLose.java b/src/main/java/forge/gui/match/QuestWinLose.java index 1bf0a61291a..82ff4f0c15a 100644 --- a/src/main/java/forge/gui/match/QuestWinLose.java +++ b/src/main/java/forge/gui/match/QuestWinLose.java @@ -134,7 +134,7 @@ public class QuestWinLose extends ControlWinLose { // Ante returns to owners in a draw if (!outcome.isDraw()) { - boolean isHumanWinner = outcome.getWinner().equals(questPlayer); + boolean isHumanWinner = outcome.getWinningLobbyPlayer().equals(questPlayer); final List anteCards = new ArrayList(); for (Player p : lastGame.getRegisteredPlayers()) { if (p.getLobbyPlayer().equals(questPlayer) == isHumanWinner) { diff --git a/src/main/java/forge/gui/match/ViewWinLose.java b/src/main/java/forge/gui/match/ViewWinLose.java index addcca37796..4227c30a9ee 100644 --- a/src/main/java/forge/gui/match/ViewWinLose.java +++ b/src/main/java/forge/gui/match/ViewWinLose.java @@ -1,228 +1,238 @@ -package forge.gui.match; - -import java.awt.Color; -import java.awt.Font; -import java.awt.Point; -import java.awt.Toolkit; -import java.awt.datatransfer.StringSelection; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; - -import org.apache.commons.lang3.tuple.Pair; - -import net.miginfocom.swing.MigLayout; -import forge.Command; -import forge.GameLog; -import forge.GameLogEntry; -import forge.GameLogEntryType; -import forge.Singletons; -import forge.game.Game; -import forge.game.GameOutcome; -import forge.game.player.LobbyPlayer; -import forge.game.player.PlayerStatistics; -import forge.gui.toolbox.FButton; -import forge.gui.toolbox.FLabel; -import forge.gui.toolbox.FOverlay; -import forge.gui.toolbox.FScrollPane; -import forge.gui.toolbox.FSkin; -import forge.gui.toolbox.FTextArea; -import forge.net.FServer; - -/** - * TODO: Write javadoc for this type. - * - */ -public class ViewWinLose { - private final FButton btnContinue, btnRestart, btnQuit; - private final JPanel pnlCustom; - - private final JLabel lblTitle = new JLabel("WinLoseFrame > lblTitle needs updating."); - private final JLabel lblStats = new JLabel("WinLoseFrame > lblStats needs updating."); - private final JPanel pnlOutcomes = new JPanel(new MigLayout("wrap, align center")); - - @SuppressWarnings("serial") - public ViewWinLose(final Game game) { - final JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel(); - - final JPanel pnlLeft = new JPanel(); - final JPanel pnlRight = new JPanel(); - final JScrollPane scrCustom = new JScrollPane(); - pnlCustom = new JPanel(); - - btnContinue = new FButton(); - btnRestart = new FButton(); - btnQuit = new FButton(); - - // Control of the win/lose is handled differently for various game modes. - ControlWinLose control = null; - switch (game.getType()) { - case Quest: - control = new QuestWinLose(this, game); - break; - case Draft: - if (!Singletons.getModel().getGauntletMini().isGauntletDraft()) { - break; - } - case Sealed: - control = new LimitedWinLose(this, game); - break; - case Gauntlet: - control = new GauntletWinLose(this, game); - break; - default: // will catch it after switch - break; - } - if (null == control) { - control = new ControlWinLose(this, game); - } - - - pnlLeft.setOpaque(false); - pnlRight.setOpaque(false); - pnlCustom.setOpaque(false); - scrCustom.setOpaque(false); - scrCustom.setBorder(null); - scrCustom.getVerticalScrollBar().setUnitIncrement(16); - scrCustom.getViewport().setOpaque(false); - scrCustom.getViewport().add(pnlCustom); - - lblTitle.setForeground(Color.white); - lblTitle.setHorizontalAlignment(SwingConstants.CENTER); - FSkin.get(lblTitle).setFont(FSkin.getBoldFont(30)); - - lblStats.setForeground(Color.white); - lblStats.setHorizontalAlignment(SwingConstants.CENTER); - FSkin.get(lblStats).setFont(FSkin.getFont(26)); - - btnContinue.setText("Continue"); - FSkin.get(btnContinue).setFont(FSkin.getFont(22)); - btnRestart.setText("Restart"); - FSkin.get(btnRestart).setFont(FSkin.getFont(22)); - btnQuit.setText("Quit"); - FSkin.get(btnQuit).setFont(FSkin.getFont(22)); - btnContinue.setEnabled(!game.getMatch().isMatchOver()); - - // Assemble game log scroller. - final FTextArea txtLog = new FTextArea(); - txtLog.setText(game.getGameLog().getLogText(null)); - FSkin.get(txtLog).setFont(FSkin.getFont(14)); - txtLog.setFocusable(true); // allow highlighting and copying of log - - FLabel btnCopyLog = new FLabel.ButtonBuilder().text("Copy to clipboard").build(); - btnCopyLog.setCommand(new Command() { - @Override - public void run() { - StringSelection ss = new StringSelection(txtLog.getText()); - try { - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); - } catch (IllegalStateException ex) { - // ignore; may be unavailable on some platforms - } - } - }); - - // Add all components accordingly. - overlay.setLayout(new MigLayout("insets 0, w 100%!, h 100%!")); - pnlLeft.setLayout(new MigLayout("insets 0, wrap, align center")); - pnlRight.setLayout(new MigLayout("insets 0, wrap")); - pnlCustom.setLayout(new MigLayout("insets 0, wrap, align center")); - - final boolean customIsPopulated = control.populateCustomPanel(); - if (customIsPopulated) { - overlay.add(pnlLeft, "w 40%!, h 100%!"); - overlay.add(pnlRight, "w 60%!, h 100%!"); - pnlRight.add(scrCustom, "w 100%!, h 100%!"); - } - else { - overlay.add(pnlLeft, "w 100%!, h 100%!"); - } - - pnlOutcomes.setOpaque(false); - pnlLeft.add(lblTitle, "h 60px!, center"); - pnlLeft.add(pnlOutcomes, "center"); - pnlLeft.add(lblStats, "h 60px!, center"); - - // A container must be made to ensure proper centering. - final JPanel pnlButtons = new JPanel(new MigLayout("insets 0, wrap, ax center")); - pnlButtons.setOpaque(false); - - final String constraints = "w 300px!, h 50px!, gap 0 0 20px 0"; - pnlButtons.add(btnContinue, constraints); - pnlButtons.add(btnRestart, constraints); - pnlButtons.add(btnQuit, constraints); - pnlLeft.add(pnlButtons, "w 100%!"); - - final JPanel pnlLog = new JPanel(new MigLayout("insets 0, wrap, ax center")); - final FScrollPane scrLog = new FScrollPane(txtLog); - scrLog.setBorder(null); - pnlLog.setOpaque(false); - - pnlLog.add(new FLabel.Builder().text("Game Log").fontAlign(SwingConstants.CENTER) - .fontSize(18).fontStyle(Font.BOLD).build(), - "w 300px!, h 28px!, gaptop 20px"); - - pnlLog.add(scrLog, "w 300px!, h 100px!, gap 0 0 10 10"); - pnlLog.add(btnCopyLog, "center, w pref+16, h pref+8"); - pnlLeft.add(pnlLog, "w 100%!"); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - scrLog.getViewport().setViewPosition(new Point(0, 0)); - // populateCustomPanel may have changed which buttons are enabled; focus on the 'best' one - if (btnContinue.isEnabled()) { - btnContinue.requestFocusInWindow(); - } else { - btnQuit.requestFocusInWindow(); - } - } - }); - - lblTitle.setText(composeTitle(game.getOutcome())); - - GameLog log = game.getGameLog(); - - for (GameLogEntry o : log.getLogEntriesExact(GameLogEntryType.GAME_OUTCOME)) - pnlOutcomes.add(new FLabel.Builder().text(o.message).fontSize(14).build(), "h 20!"); - - for (GameLogEntry o : log.getLogEntriesExact(GameLogEntryType.MATCH_RESULTS)) - lblStats.setText(o.message); - } - - private String composeTitle(GameOutcome outcome) { - LobbyPlayer guiPlayer = FServer.instance.getLobby().getGuiPlayer(); - int nHumansInGame = 0; - for(Pair pps : outcome) { - if( pps.getKey() == guiPlayer ) - nHumansInGame++; - } - LobbyPlayer winner = outcome.getWinner(); - if ( winner == null ) - return "It's a draw!"; - - return nHumansInGame == 1 ? "You " + (winner == guiPlayer ? "won!" : "lost!") : winner.getName() + " Won!"; - } - - /** @return {@link forge.gui.toolbox.FButton} */ - public FButton getBtnContinue() { - return this.btnContinue; - } - - /** @return {@link forge.gui.toolbox.FButton} */ - public FButton getBtnRestart() { - return this.btnRestart; - } - - /** @return {@link forge.gui.toolbox.FButton} */ - public FButton getBtnQuit() { - return this.btnQuit; - } - - /** @return {@link javax.swing.JPanel} */ - public JPanel getPnlCustom() { - return this.pnlCustom; - } -} +package forge.gui.match; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; + +import net.miginfocom.swing.MigLayout; +import forge.Command; +import forge.GameLog; +import forge.GameLogEntry; +import forge.GameLogEntryType; +import forge.Singletons; +import forge.game.Game; +import forge.game.GameOutcome; +import forge.game.player.Player; +import forge.gui.toolbox.FButton; +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.FOverlay; +import forge.gui.toolbox.FScrollPane; +import forge.gui.toolbox.FSkin; +import forge.gui.toolbox.FTextArea; + +/** + * TODO: Write javadoc for this type. + * + */ +public class ViewWinLose { + private final FButton btnContinue, btnRestart, btnQuit; + private final JPanel pnlCustom; + + private final JLabel lblTitle = new JLabel("WinLoseFrame > lblTitle needs updating."); + private final JLabel lblStats = new JLabel("WinLoseFrame > lblStats needs updating."); + private final JPanel pnlOutcomes = new JPanel(new MigLayout("wrap, align center")); + + private final Game game; + + @SuppressWarnings("serial") + public ViewWinLose(final Game game0) { + + game = game0; + + final JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel(); + + final JPanel pnlLeft = new JPanel(); + final JPanel pnlRight = new JPanel(); + final JScrollPane scrCustom = new JScrollPane(); + pnlCustom = new JPanel(); + + btnContinue = new FButton(); + btnRestart = new FButton(); + btnQuit = new FButton(); + + // Control of the win/lose is handled differently for various game + // modes. + ControlWinLose control = null; + switch (game0.getType()) { + case Quest: + control = new QuestWinLose(this, game0); + break; + case Draft: + if (!Singletons.getModel().getGauntletMini().isGauntletDraft()) { + break; + } + case Sealed: + control = new LimitedWinLose(this, game0); + break; + case Gauntlet: + control = new GauntletWinLose(this, game0); + break; + default: // will catch it after switch + break; + } + if (null == control) { + control = new ControlWinLose(this, game0); + } + + pnlLeft.setOpaque(false); + pnlRight.setOpaque(false); + pnlCustom.setOpaque(false); + scrCustom.setOpaque(false); + scrCustom.setBorder(null); + scrCustom.getVerticalScrollBar().setUnitIncrement(16); + scrCustom.getViewport().setOpaque(false); + scrCustom.getViewport().add(pnlCustom); + + lblTitle.setForeground(Color.white); + lblTitle.setHorizontalAlignment(SwingConstants.CENTER); + FSkin.get(lblTitle).setFont(FSkin.getBoldFont(30)); + + lblStats.setForeground(Color.white); + lblStats.setHorizontalAlignment(SwingConstants.CENTER); + FSkin.get(lblStats).setFont(FSkin.getFont(26)); + + btnContinue.setText("Next Game"); + FSkin.get(btnContinue).setFont(FSkin.getFont(22)); + btnRestart.setText("Start New Match"); + FSkin.get(btnRestart).setFont(FSkin.getFont(22)); + btnQuit.setText("Quit Match"); + FSkin.get(btnQuit).setFont(FSkin.getFont(22)); + btnContinue.setEnabled(!game0.getMatch().isMatchOver()); + + // Assemble game log scroller. + final FTextArea txtLog = new FTextArea(); + txtLog.setText(game.getGameLog().getLogText(null).replace("[COMPUTER]", "[AI]")); + FSkin.get(txtLog).setFont(FSkin.getFont(14)); + txtLog.setFocusable(true); // allow highlighting and copying of log + + FLabel btnCopyLog = new FLabel.ButtonBuilder().text("Copy to clipboard").build(); + btnCopyLog.setCommand(new Command() { + @Override + public void run() { + StringSelection ss = new StringSelection(txtLog.getText()); + try { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); + } catch (IllegalStateException ex) { + // ignore; may be unavailable on some platforms + } + } + }); + + // Add all components accordingly. + overlay.setLayout(new MigLayout("insets 0, w 100%!, h 100%!")); + pnlLeft.setLayout(new MigLayout("insets 0, wrap, align center")); + pnlRight.setLayout(new MigLayout("insets 0, wrap")); + pnlCustom.setLayout(new MigLayout("insets 0, wrap, align center")); + + final boolean customIsPopulated = control.populateCustomPanel(); + if (customIsPopulated) { + overlay.add(pnlLeft, "w 40%!, h 100%!"); + overlay.add(pnlRight, "w 60%!, h 100%!"); + pnlRight.add(scrCustom, "w 100%!, h 100%!"); + } else { + overlay.add(pnlLeft, "w 100%!, h 100%!"); + } + + pnlOutcomes.setOpaque(false); + pnlLeft.add(lblTitle, "h 60px!, center"); + pnlLeft.add(pnlOutcomes, "center"); + pnlLeft.add(lblStats, "h 60px!, center"); + + // A container must be made to ensure proper centering. + final JPanel pnlButtons = new JPanel(new MigLayout("insets 0, wrap, ax center")); + pnlButtons.setOpaque(false); + + final String constraints = "w 300px!, h 50px!, gap 0 0 20px 0"; + pnlButtons.add(btnContinue, constraints); + pnlButtons.add(btnRestart, constraints); + pnlButtons.add(btnQuit, constraints); + pnlLeft.add(pnlButtons, "w 100%!"); + + final JPanel pnlLog = new JPanel(new MigLayout("insets 0, wrap, ax center")); + final FScrollPane scrLog = new FScrollPane(txtLog); + scrLog.setBorder(null); + pnlLog.setOpaque(false); + + pnlLog.add( + new FLabel.Builder().text("Game Log").fontAlign(SwingConstants.CENTER).fontSize(18) + .fontStyle(Font.BOLD).build(), "w 300px!, h 28px!, gaptop 20px"); + + pnlLog.add(scrLog, "w 300px!, h 100px!, gap 0 0 10 10"); + pnlLog.add(btnCopyLog, "center, w pref+16, h pref+8"); + pnlLeft.add(pnlLog, "w 100%!"); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + scrLog.getViewport().setViewPosition(new Point(0, 0)); + // populateCustomPanel may have changed which buttons are + // enabled; focus on the 'best' one + if (btnContinue.isEnabled()) { + btnContinue.requestFocusInWindow(); + } else { + btnQuit.requestFocusInWindow(); + } + } + }); + + lblTitle.setText(composeTitle(game0.getOutcome())); + + showGameOutcomeSummary(); + showPlayerScores(); + + } + + private String composeTitle(GameOutcome outcome) { + Player winner = outcome.getWinningPlayer(); + if (winner == null) { + return "It's a draw!"; + } else { + return winner.getName() + " Won!"; + } + } + + /** @return {@link forge.gui.toolbox.FButton} */ + public FButton getBtnContinue() { + return this.btnContinue; + } + + /** @return {@link forge.gui.toolbox.FButton} */ + public FButton getBtnRestart() { + return this.btnRestart; + } + + /** @return {@link forge.gui.toolbox.FButton} */ + public FButton getBtnQuit() { + return this.btnQuit; + } + + /** @return {@link javax.swing.JPanel} */ + public JPanel getPnlCustom() { + return this.pnlCustom; + } + + private void showGameOutcomeSummary() { + GameLog log = game.getGameLog(); + for (GameLogEntry o : log.getLogEntriesExact(GameLogEntryType.GAME_OUTCOME)) + pnlOutcomes.add(new FLabel.Builder().text(o.message).fontSize(14).build(), "h 20!"); + } + + private void showPlayerScores() { + GameLog log = game.getGameLog(); + for (GameLogEntry o : log.getLogEntriesExact(GameLogEntryType.MATCH_RESULTS)) { + lblStats.setText(removePlayerTypeFromLogMessage(o.message)); + } + } + + private String removePlayerTypeFromLogMessage(String message) { + return message.replaceAll("\\[[^\\]]*\\]", ""); + } +} diff --git a/src/main/java/forge/net/FServer.java b/src/main/java/forge/net/FServer.java index dcb53118fe4..b3b203f31a8 100644 --- a/src/main/java/forge/net/FServer.java +++ b/src/main/java/forge/net/FServer.java @@ -1,129 +1,129 @@ -package forge.net; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import org.apache.commons.lang.time.StopWatch; - -import forge.GameLogEntry; -import forge.Singletons; -import forge.deck.Deck; -import forge.game.Game; -import forge.game.GameType; -import forge.game.Match; -import forge.game.RegisteredPlayer; -import forge.util.Lang; - - -/** - * TODO: Write javadoc for this type. - * - */ -public enum FServer { - instance(); - - private boolean interactiveMode = true; - private Lobby lobby = null; - - public Lobby getLobby() { - if (lobby == null) { - lobby = new Lobby(); - } - return lobby; - } - - /** - * TODO: Write javadoc for this method. - * @return - */ - private final NetServer server = new NetServer(); - public NetServer getServer() { - // TODO Auto-generated method stub - return server; - } - - /** - * TODO: Write javadoc for this method. - * @param args - */ - public void simulateMatches(String[] args) { - interactiveMode = false; - System.out.println("Simulation mode"); - if(args.length < 3 ) { - System.out.println("Syntax: forge.exe sim [N]"); - System.out.println("\tsim - stands for simulation mode"); - System.out.println("\tdeck1 (or deck2) - constructed deck name or filename (has to be quoted when contains multiple words)"); - System.out.println("\tdeck is treated as file if it ends with a dot followed by three numbers or letters"); - System.out.println("\tN - number of games, defaults to 1"); - return; - } - Deck d1 = deckFromCommandLineParameter(args[1]); - Deck d2 = deckFromCommandLineParameter(args[2]); - if(d1 == null || d2 == null) { - System.out.println("One of decks could not be loaded, match cannot start"); - return; - } - - int nGames = args.length >= 4 ? Integer.parseInt(args[3]) : 1; - - System.out.println(String.format("Ai-%s vs Ai_%s - %s", d1.getName(), d2.getName(), Lang.nounWithNumeral(nGames, "game"))); - - List pp = new ArrayList(); - pp.add(RegisteredPlayer.fromDeck(d1).setPlayer(FServer.instance.getLobby().getAiPlayer("Ai-" + d1.getName()))); - pp.add(RegisteredPlayer.fromDeck(d2).setPlayer(FServer.instance.getLobby().getAiPlayer("Ai_" + d2.getName()))); - - Match mc = new Match(GameType.Constructed, pp); - for(int iGame = 0; iGame < nGames; iGame++) - simulateSingleMatch(mc, iGame); - System.out.flush(); - } - /** - * TODO: Write javadoc for this method. - * @param sw - * @param pp - */ - private void simulateSingleMatch(Match mc, int iGame) { - StopWatch sw = new StopWatch(); - sw.start(); - - CountDownLatch cdl = new CountDownLatch(1); - - Game g1 = mc.createGame(); - mc.startGame(g1, cdl); - try { - cdl.await(); // wait until game ends (in other thread) - } catch (InterruptedException e) { - // TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log. - e.printStackTrace(); - } - sw.stop(); - - List log = g1.getGameLog().getLogEntries(null); - Collections.reverse(log); - - for(GameLogEntry l : log) - System.out.println(l); - - System.out.println(String.format("\nGame %d ended in %d ms. %s has won!\n", 1+iGame, sw.getTime(), g1.getOutcome().getWinner().getName())); - } - - - private Deck deckFromCommandLineParameter(String deckname) { - int dotpos = deckname.lastIndexOf('.'); - if(dotpos > 0 && dotpos == deckname.length()-4) - return Deck.fromFile(new File(deckname)); - return Singletons.getModel().getDecks().getConstructed().get(deckname); - } - /** - * TODO: Write javadoc for this method. - * @return - */ - public boolean isInteractiveMode() { - return interactiveMode; - } - - -} +package forge.net; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import org.apache.commons.lang.time.StopWatch; + +import forge.GameLogEntry; +import forge.Singletons; +import forge.deck.Deck; +import forge.game.Game; +import forge.game.GameType; +import forge.game.Match; +import forge.game.RegisteredPlayer; +import forge.util.Lang; + + +/** + * TODO: Write javadoc for this type. + * + */ +public enum FServer { + instance(); + + private boolean interactiveMode = true; + private Lobby lobby = null; + + public Lobby getLobby() { + if (lobby == null) { + lobby = new Lobby(); + } + return lobby; + } + + /** + * TODO: Write javadoc for this method. + * @return + */ + private final NetServer server = new NetServer(); + public NetServer getServer() { + // TODO Auto-generated method stub + return server; + } + + /** + * TODO: Write javadoc for this method. + * @param args + */ + public void simulateMatches(String[] args) { + interactiveMode = false; + System.out.println("Simulation mode"); + if(args.length < 3 ) { + System.out.println("Syntax: forge.exe sim [N]"); + System.out.println("\tsim - stands for simulation mode"); + System.out.println("\tdeck1 (or deck2) - constructed deck name or filename (has to be quoted when contains multiple words)"); + System.out.println("\tdeck is treated as file if it ends with a dot followed by three numbers or letters"); + System.out.println("\tN - number of games, defaults to 1"); + return; + } + Deck d1 = deckFromCommandLineParameter(args[1]); + Deck d2 = deckFromCommandLineParameter(args[2]); + if(d1 == null || d2 == null) { + System.out.println("One of decks could not be loaded, match cannot start"); + return; + } + + int nGames = args.length >= 4 ? Integer.parseInt(args[3]) : 1; + + System.out.println(String.format("Ai-%s vs Ai_%s - %s", d1.getName(), d2.getName(), Lang.nounWithNumeral(nGames, "game"))); + + List pp = new ArrayList(); + pp.add(RegisteredPlayer.fromDeck(d1).setPlayer(FServer.instance.getLobby().getAiPlayer("Ai-" + d1.getName()))); + pp.add(RegisteredPlayer.fromDeck(d2).setPlayer(FServer.instance.getLobby().getAiPlayer("Ai_" + d2.getName()))); + + Match mc = new Match(GameType.Constructed, pp); + for(int iGame = 0; iGame < nGames; iGame++) + simulateSingleMatch(mc, iGame); + System.out.flush(); + } + /** + * TODO: Write javadoc for this method. + * @param sw + * @param pp + */ + private void simulateSingleMatch(Match mc, int iGame) { + StopWatch sw = new StopWatch(); + sw.start(); + + CountDownLatch cdl = new CountDownLatch(1); + + Game g1 = mc.createGame(); + mc.startGame(g1, cdl); + try { + cdl.await(); // wait until game ends (in other thread) + } catch (InterruptedException e) { + // TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log. + e.printStackTrace(); + } + sw.stop(); + + List log = g1.getGameLog().getLogEntries(null); + Collections.reverse(log); + + for(GameLogEntry l : log) + System.out.println(l); + + System.out.println(String.format("\nGame %d ended in %d ms. %s has won!\n", 1+iGame, sw.getTime(), g1.getOutcome().getWinningLobbyPlayer().getName())); + } + + + private Deck deckFromCommandLineParameter(String deckname) { + int dotpos = deckname.lastIndexOf('.'); + if(dotpos > 0 && dotpos == deckname.length()-4) + return Deck.fromFile(new File(deckname)); + return Singletons.getModel().getDecks().getConstructed().get(deckname); + } + /** + * TODO: Write javadoc for this method. + * @return + */ + public boolean isInteractiveMode() { + return interactiveMode; + } + + +} diff --git a/src/main/java/forge/net/Lobby.java b/src/main/java/forge/net/Lobby.java index 1a42a100bc0..61b55d58dcb 100644 --- a/src/main/java/forge/net/Lobby.java +++ b/src/main/java/forge/net/Lobby.java @@ -1,128 +1,133 @@ -package forge.net; - -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; - -import forge.control.ChatArea; -import forge.game.player.LobbyPlayer; -import forge.game.player.LobbyPlayerAi; -import forge.game.player.LobbyPlayerHuman; -import forge.game.player.LobbyPlayerRemote; -import forge.gui.toolbox.FSkin; -import forge.net.client.INetClient; -import forge.util.MyRandom; - -/** - * TODO: Write javadoc for this type. - * - */ -public class Lobby { - - - private final String[] opponentNames = new String[] { - "Abigail", "Ada", "Adeline", "Adriana", "Agatha", "Agnes", "Aileen", "Alba", "Alcyon", - "Alethea", "Alice", "Alicia", "Alison", "Amanda", "Amelia", "Amy", "Andrea", "Angelina", - "Anita", "Ann", "Annabel", "Anne", "Audrey", "Barbara", "Belinda", "Bernice", "Bertha", - "Bonnie", "Brenda", "Bridget", "Bunny", "Carmen", "Carol", "Catherine", "Cheryl", - "Christine", "Cinderalla", "Claire", "Clarice", "Claudia", "Constance", "Cora", - "Corinne", "Cnythia", "Daisy", "Daphne", "Dawn", "Deborah", "Diana", "Dolly", "Dora", - "Doreen", "Doris", "Dorothy", "Eileen", "Elaine", "Elizabeth", "Emily", "Emma", "Ethel", - "Evelyn", "Fiona", "Florence", "Frances", "Geraldine", "Gertrude", "Gladys", "Gloria", - "Grace", "Greta", "Harriet", "Hazel", "Helen", "Hilda", "Ida", "Ingrid", "Irene", - "Isabel", "Jacinta", "Jackie", "Jane", "Janet", "Janice", "Jennifer", "Jessie", "Joan", - "Jocelyn", "Josephine", "Joyce", "Judith", "Julia", "Juliana", "Karina", "Kathleen", - "Laura", "Lilian", "Lily", "Linda", "Lisa", "Lilita", "Lora", "Lorna", "Lucy", "Lydia", - "Mabel", "Madeline", "Maggie", "Maria", "Mariam", "Marilyn", "Mary", "Matilda", "Mavis", - "Melanie", "Melinda", "Melody", "Michelle", "Mildred", "Molly", "Mona", "Monica", - "Nancy", "Nora", "Norma", "Olga", "Pamela", "Patricia", "Paula", "Pauline", "Pearl", - "Peggy", "Penny", "Phoebe", "Phyllis", "Polly", "Priscilla", "Rachel", "Rebecca", - "Rita", "Rosa", "Rosalind", "Rose", "Rosemary", "Rowena", "Ruby", "Sally", "Samantha", - "Sarah", "Selina", "Sharon", "Sheila", "Shirley", "Sonya", "Stella", "Sue", "Susan", - "Sylvia", "Tina", "Tracy", "Ursula", "Valentine", "Valerie", "Vanessa", "Veronica", - "Victoria", "Violet", "Vivian", "Wendy", "Winnie", "Yvonne", "Aaron", "Abraham", "Adam", - "Adrain", "Alain", "Alan", "Alban", "Albert", "Alec", "Alexander", "Alfonso", "Alfred", - "Allan", "Allen", "Alonso", "Aloysius", "Alphonso", "Alvin", "Andrew", "Andy", "Amadeus", - "Amselm", "Anthony", "Arnold", "Augusta", "Austin", "Barnaby", "Benedict", "Benjamin", - "Bertie", "Bertram", "Bill", "Bob", "Boris", "Brady", "Brian", "Bruce", "Burt", "Byron", - "Calvin", "Carl", "Carter", "Casey", "Cecil", "Charles", "Christian", "Christopher", - "Clarence", "Clement", "Colin", "Conan", "Dalton", "Damian", "Daniel", "David", "Denis", - "Derek", "Desmond", "Dick", "Dominic", "Donald", "Douglas", "Duncan", "Edmund", - "Edward", "Ellen", "Elton", "Elvis", "Eric", "Eugene", "Felix", "Francis", "Frank", - "Frederick", "Gary", "Geoffrey", "George", "Gerald", "Gerry", "Gordon", "Hamish", - "Hardy", "Harold", "Harry", "Henry", "Herbert", "Ignatius", "Jack", "James", "Jeffrey", - "Jim", "Joe", "John", "Joseph", "Karl", "Keith", "Kenneth", "Kevin", "Larry", "Lawrence", - "Leonard", "Lionel", "Louis", "Lucas", "Malcolm", "Mark", "Martin", "Mathew", "Maurice", - "Max", "Melvin", "Michael", "Milton", "Morgan", "Morris", "Murphy", "Neville", - "Nicholas", "Noel", "Norman", "Oliver", "Oscar", "Patrick", "Paul", "Perkin", "Peter", - "Philip", "Ralph", "Randy", "Raymond", "Richard", "Ricky", "Robert", "Robin", "Rodney", - "Roger", "Roland", "Ronald", "Roy", "Sam", "Sebastian", "Simon", "Stanley", "Stephen", - "Stuart", "Terence", "Thomas", "Tim", "Tom", "Tony", "Victor", "Vincent", "Wallace", - "Walter", "Wilfred", "William", "Winston" - }; - - private Map remotePlayers = new ConcurrentHashMap(); - private final LobbyPlayerHuman guiPlayer = new LobbyPlayerHuman("Human"); - private final LobbyPlayerAi system = new LobbyPlayerAi("System"); - - public final LobbyPlayer getGuiPlayer() { - return guiPlayer; - } - - public final LobbyPlayer getAiPlayer() { return getAiPlayer(getRandomName()); } - public final LobbyPlayer getAiPlayer(String name) { - LobbyPlayer player = new LobbyPlayerAi(name); - if(FSkin.isLoaded()) - player.setAvatarIndex(MyRandom.getRandom().nextInt(FSkin.getAvatars().size())); - return player; - } - - - /** - * TODO: Write javadoc for this method. - * @param nextInt - * @return - */ - private String getRandomName() { - Random my = MyRandom.getRandom(); - return opponentNames[my.nextInt(opponentNames.length)]; - - } - - /** - * TODO: Write javadoc for this method. - * @return - */ - public LobbyPlayer getQuestPlayer() { - return guiPlayer; - } - - /** - * TODO: Write javadoc for this method. - * @param name - * @return - */ - public synchronized LobbyPlayer findOrCreateRemotePlayer(String name, INetClient client) { - if (remotePlayers.containsKey(name)) - return remotePlayers.get(name); - - LobbyPlayerRemote res = new LobbyPlayerRemote(name, client); - speak(ChatArea.Room, system, res.getName() + " has joined the server."); - // have to load avatar from remote user's preferences here - remotePlayers.put(name, res); - - return res; - } - - public void disconnectPlayer(LobbyPlayer player) { - // Should set up a timer here to discard player and all of his games after 20 minutes of being offline - } - - - public void speak(ChatArea room, LobbyPlayer player, String message) { - getGuiPlayer().hear(player, message); - for(LobbyPlayer remote : remotePlayers.values()) { - remote.hear(player, message); - } - } -} +package forge.net; + +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; + +import forge.control.ChatArea; +import forge.game.player.LobbyPlayer; +import forge.game.player.LobbyPlayerAi; +import forge.game.player.LobbyPlayerHuman; +import forge.game.player.LobbyPlayerRemote; +import forge.gui.GuiDisplayUtil; +import forge.gui.toolbox.FSkin; +import forge.net.client.INetClient; +import forge.util.MyRandom; + +/** + * TODO: Write javadoc for this type. + * + */ +public class Lobby { + + + private final String[] opponentNames = new String[] { + "Abigail", "Ada", "Adeline", "Adriana", "Agatha", "Agnes", "Aileen", "Alba", "Alcyon", + "Alethea", "Alice", "Alicia", "Alison", "Amanda", "Amelia", "Amy", "Andrea", "Angelina", + "Anita", "Ann", "Annabel", "Anne", "Audrey", "Barbara", "Belinda", "Bernice", "Bertha", + "Bonnie", "Brenda", "Bridget", "Bunny", "Carmen", "Carol", "Catherine", "Cheryl", + "Christine", "Cinderalla", "Claire", "Clarice", "Claudia", "Constance", "Cora", + "Corinne", "Cnythia", "Daisy", "Daphne", "Dawn", "Deborah", "Diana", "Dolly", "Dora", + "Doreen", "Doris", "Dorothy", "Eileen", "Elaine", "Elizabeth", "Emily", "Emma", "Ethel", + "Evelyn", "Fiona", "Florence", "Frances", "Geraldine", "Gertrude", "Gladys", "Gloria", + "Grace", "Greta", "Harriet", "Hazel", "Helen", "Hilda", "Ida", "Ingrid", "Irene", + "Isabel", "Jacinta", "Jackie", "Jane", "Janet", "Janice", "Jennifer", "Jessie", "Joan", + "Jocelyn", "Josephine", "Joyce", "Judith", "Julia", "Juliana", "Karina", "Kathleen", + "Laura", "Lilian", "Lily", "Linda", "Lisa", "Lilita", "Lora", "Lorna", "Lucy", "Lydia", + "Mabel", "Madeline", "Maggie", "Maria", "Mariam", "Marilyn", "Mary", "Matilda", "Mavis", + "Melanie", "Melinda", "Melody", "Michelle", "Mildred", "Molly", "Mona", "Monica", + "Nancy", "Nora", "Norma", "Olga", "Pamela", "Patricia", "Paula", "Pauline", "Pearl", + "Peggy", "Penny", "Phoebe", "Phyllis", "Polly", "Priscilla", "Rachel", "Rebecca", + "Rita", "Rosa", "Rosalind", "Rose", "Rosemary", "Rowena", "Ruby", "Sally", "Samantha", + "Sarah", "Selina", "Sharon", "Sheila", "Shirley", "Sonya", "Stella", "Sue", "Susan", + "Sylvia", "Tina", "Tracy", "Ursula", "Valentine", "Valerie", "Vanessa", "Veronica", + "Victoria", "Violet", "Vivian", "Wendy", "Winnie", "Yvonne", "Aaron", "Abraham", "Adam", + "Adrain", "Alain", "Alan", "Alban", "Albert", "Alec", "Alexander", "Alfonso", "Alfred", + "Allan", "Allen", "Alonso", "Aloysius", "Alphonso", "Alvin", "Andrew", "Andy", "Amadeus", + "Amselm", "Anthony", "Arnold", "Augusta", "Austin", "Barnaby", "Benedict", "Benjamin", + "Bertie", "Bertram", "Bill", "Bob", "Boris", "Brady", "Brian", "Bruce", "Burt", "Byron", + "Calvin", "Carl", "Carter", "Casey", "Cecil", "Charles", "Christian", "Christopher", + "Clarence", "Clement", "Colin", "Conan", "Dalton", "Damian", "Daniel", "David", "Denis", + "Derek", "Desmond", "Dick", "Dominic", "Donald", "Douglas", "Duncan", "Edmund", + "Edward", "Ellen", "Elton", "Elvis", "Eric", "Eugene", "Felix", "Francis", "Frank", + "Frederick", "Gary", "Geoffrey", "George", "Gerald", "Gerry", "Gordon", "Hamish", + "Hardy", "Harold", "Harry", "Henry", "Herbert", "Ignatius", "Jack", "James", "Jeffrey", + "Jim", "Joe", "John", "Joseph", "Karl", "Keith", "Kenneth", "Kevin", "Larry", "Lawrence", + "Leonard", "Lionel", "Louis", "Lucas", "Malcolm", "Mark", "Martin", "Mathew", "Maurice", + "Max", "Melvin", "Michael", "Milton", "Morgan", "Morris", "Murphy", "Neville", + "Nicholas", "Noel", "Norman", "Oliver", "Oscar", "Patrick", "Paul", "Perkin", "Peter", + "Philip", "Ralph", "Randy", "Raymond", "Richard", "Ricky", "Robert", "Robin", "Rodney", + "Roger", "Roland", "Ronald", "Roy", "Sam", "Sebastian", "Simon", "Stanley", "Stephen", + "Stuart", "Terence", "Thomas", "Tim", "Tom", "Tony", "Victor", "Vincent", "Wallace", + "Walter", "Wilfred", "William", "Winston" + }; + + private Map remotePlayers = new ConcurrentHashMap(); + private final LobbyPlayerHuman guiPlayer = new LobbyPlayerHuman("Human"); + private final LobbyPlayerAi system = new LobbyPlayerAi("System"); + + public final LobbyPlayer getGuiPlayer() { + return guiPlayer; + } + + public final LobbyPlayer getAiPlayer() { return getAiPlayer(getRandomName()); } + public final LobbyPlayer getAiPlayer(String name) { + LobbyPlayer player = new LobbyPlayerAi(name); + if(FSkin.isLoaded()) + player.setAvatarIndex(MyRandom.getRandom().nextInt(FSkin.getAvatars().size())); + return player; + } + + + /** + * TODO: Write javadoc for this method. + * @param nextInt + * @return + */ + private String getRandomName() { + Random my = MyRandom.getRandom(); + String playerName = GuiDisplayUtil.getPlayerName(); + String aiName = opponentNames[my.nextInt(opponentNames.length)]; + while (aiName.equalsIgnoreCase(playerName)) { + aiName = opponentNames[my.nextInt(opponentNames.length)]; + } + return aiName; + } + + /** + * TODO: Write javadoc for this method. + * @return + */ + public LobbyPlayer getQuestPlayer() { + return guiPlayer; + } + + /** + * TODO: Write javadoc for this method. + * @param name + * @return + */ + public synchronized LobbyPlayer findOrCreateRemotePlayer(String name, INetClient client) { + if (remotePlayers.containsKey(name)) + return remotePlayers.get(name); + + LobbyPlayerRemote res = new LobbyPlayerRemote(name, client); + speak(ChatArea.Room, system, res.getName() + " has joined the server."); + // have to load avatar from remote user's preferences here + remotePlayers.put(name, res); + + return res; + } + + public void disconnectPlayer(LobbyPlayer player) { + // Should set up a timer here to discard player and all of his games after 20 minutes of being offline + } + + + public void speak(ChatArea room, LobbyPlayer player, String message) { + getGuiPlayer().hear(player, message); + for(LobbyPlayer remote : remotePlayers.values()) { + remote.hear(player, message); + } + } +} diff --git a/src/main/java/forge/properties/ForgePreferences.java b/src/main/java/forge/properties/ForgePreferences.java index 86baca947de..cfab1835bb6 100644 --- a/src/main/java/forge/properties/ForgePreferences.java +++ b/src/main/java/forge/properties/ForgePreferences.java @@ -34,6 +34,7 @@ public class ForgePreferences extends PreferencesStore { * Preference identifiers, and their default values. */ public static enum FPref { + PLAYER_NAME (""), CONSTRUCTED_P1_DECK_STATE(""), CONSTRUCTED_P2_DECK_STATE(""), CONSTRUCTED_GAMEPLAYERS(""), diff --git a/src/main/java/forge/sound/EventVisualizer.java b/src/main/java/forge/sound/EventVisualizer.java index 647b6ee0e2a..de6e69cabb0 100644 --- a/src/main/java/forge/sound/EventVisualizer.java +++ b/src/main/java/forge/sound/EventVisualizer.java @@ -1,210 +1,210 @@ -package forge.sound; - -import java.util.Collection; - -import forge.Card; -import forge.card.spellability.SpellAbility; -import forge.game.event.GameEventBlockersDeclared; -import forge.game.event.GameEventCardChangeZone; -import forge.game.event.GameEventCardDamaged; -import forge.game.event.GameEventCardDestroyed; -import forge.game.event.GameEventCardAttachment; -import forge.game.event.GameEventCardRegenerated; -import forge.game.event.GameEventCardSacrificed; -import forge.game.event.GameEventCardCounters; -import forge.game.event.GameEventGameOutcome; -import forge.game.event.GameEventTurnEnded; -import forge.game.event.GameEvent; -import forge.game.event.GameEventFlipCoin; -import forge.game.event.GameEventLandPlayed; -import forge.game.event.GameEventPlayerLivesChanged; -import forge.game.event.GameEventPlayerPoisoned; -import forge.game.event.GameEventCardTapped; -import forge.game.event.GameEventShuffle; -import forge.game.event.GameEventSpellResolved; -import forge.game.event.GameEventTokenCreated; -import forge.game.event.IGameEventVisitor; -import forge.game.zone.ZoneType; -import forge.gui.events.IUiEventVisitor; -import forge.gui.events.UiEventAttackerDeclared; -import forge.gui.events.UiEventBlockerAssigned; -import forge.net.FServer; -import forge.util.maps.MapOfLists; - -/** - * This class is in charge of converting any forge.game.event.Event to a SoundEffectType. - * - */ -public class EventVisualizer extends IGameEventVisitor.Base implements IUiEventVisitor { - - public SoundEffectType visit(GameEventCardDamaged event) { return SoundEffectType.Damage; } - public SoundEffectType visit(GameEventCardDestroyed event) { return SoundEffectType.Destroy; } - public SoundEffectType visit(GameEventCardAttachment event) { return SoundEffectType.Equip; } - public SoundEffectType visit(GameEventCardChangeZone event) { - ZoneType from = event.from == null ? null : event.from.getZoneType(); - ZoneType to = event.to.getZoneType(); - if( from == ZoneType.Library && to == ZoneType.Hand) - return SoundEffectType.Draw; - if( from == ZoneType.Hand && (to == ZoneType.Graveyard || to == ZoneType.Library) ) - return SoundEffectType.Discard; - - return to == ZoneType.Exile ? SoundEffectType.Exile : null; - } - public SoundEffectType visit(GameEventCardRegenerated event) { return SoundEffectType.Regen; } - public SoundEffectType visit(GameEventCardSacrificed event) { return SoundEffectType.Sacrifice; } - public SoundEffectType visit(GameEventCardCounters event) { return event.newValue > event.oldValue ? SoundEffectType.AddCounter : event.newValue < event.oldValue ? SoundEffectType.RemoveCounter : null; } - public SoundEffectType visit(GameEventTurnEnded event) { return SoundEffectType.EndOfTurn; } - public SoundEffectType visit(GameEventFlipCoin event) { return SoundEffectType.FlipCoin; } - public SoundEffectType visit(GameEventPlayerLivesChanged event) { return event.newLives < event.oldLives ? SoundEffectType.LifeLoss : SoundEffectType.LifeGain; } - public SoundEffectType visit(GameEventPlayerPoisoned event) { return SoundEffectType.Poison; } - public SoundEffectType visit(GameEventShuffle event) { return SoundEffectType.Shuffle; } - public SoundEffectType visit(GameEventTokenCreated event) { return SoundEffectType.Token; } - public SoundEffectType visit(GameEventBlockersDeclared event) { - boolean isLocalHuman = event.defendingPlayer.getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer(); - if (isLocalHuman) - return null; // already played sounds in interactive mode - - for(MapOfLists ab : event.blockers.values()) { - for(Collection bb : ab.values()) { - if ( !bb.isEmpty() ) { - // hasAnyBlocker = true; - return SoundEffectType.Block; - } - } - } - return null; - } - - /** - * Plays the sound corresponding to the outcome of the duel. - */ - public SoundEffectType visit(GameEventGameOutcome event) { - boolean humanWonTheDuel = event.result.getWinner() == FServer.instance.getLobby().getGuiPlayer(); - return humanWonTheDuel ? SoundEffectType.WinDuel : SoundEffectType.LoseDuel; - } - - /** - * Plays the sound corresponding to the card type/color when the card - * ability resolves on the stack. - */ - public SoundEffectType visit(GameEventSpellResolved evt) { - if (evt.spell == null ) { - return null; - } - - Card source = evt.spell.getSourceCard(); - if (evt.spell.isSpell()) { - // if there's a specific effect for this particular card, play it and - // we're done. - if (hasSpecificCardEffect(source)) { - return SoundEffectType.ScriptedEffect; - } - - if (source.isCreature() && source.isArtifact()) { - return SoundEffectType.ArtifactCreature; - } else if (source.isCreature()) { - return SoundEffectType.Creature; - } else if (source.isArtifact()) { - return SoundEffectType.Artifact; - } else if (source.isInstant()) { - return SoundEffectType.Instant; - } else if (source.isPlaneswalker()) { - return SoundEffectType.Planeswalker; - } else if (source.isSorcery()) { - return SoundEffectType.Sorcery; - } else if (source.isEnchantment()) { - return SoundEffectType.Enchantment; - } - } - return null; - } - - /** - * Plays the sound corresponding to the change of the card's tapped state - * (when a card is tapped or untapped). - * - * @param tapped_state if true, the "tap" sound is played; otherwise, the - * "untap" sound is played - * @return the sound effect type - */ - public SoundEffectType visit(GameEventCardTapped event) { - return event.tapped ? SoundEffectType.Tap : SoundEffectType.Untap; - } - - /** - * Plays the sound corresponding to the land type when the land is played. - * - * @param land the land card that was played - * @return the sound effect type - */ - public SoundEffectType visit(GameEventLandPlayed event) { - if (event.land == null) { - return null; - } - - // if there's a specific effect for this particular card, play it and - // we're done. - if (hasSpecificCardEffect(event.land)) { - return SoundEffectType.ScriptedEffect; - } - - for (SpellAbility sa : event.land.getManaAbility()) { - String manaColors = sa.getManaPartRecursive().getOrigProduced(); - - if (manaColors.contains("B")) return SoundEffectType.BlackLand; - if (manaColors.contains("U")) return SoundEffectType.BlueLand; - if (manaColors.contains("G")) return SoundEffectType.GreenLand; - if (manaColors.contains("R")) return SoundEffectType.RedLand; - if (manaColors.contains("W")) return SoundEffectType.WhiteLand; - } - - // play a generic land sound if no other sound corresponded to it. - return SoundEffectType.OtherLand; - } - - /** - * Play a specific sound effect based on card's name. - * - * @param c the card to play the sound effect for. - * @return the sound effect type - */ - private static boolean hasSpecificCardEffect(final Card c) { - // Implement sound effects for specific cards here, if necessary. - String effect = ""; - if (null != c) { - effect = c.getSVar("SoundEffect"); - } - return effect.isEmpty() ? false : true; - } - - - /** - * Returns the value of the SoundEffect SVar of the card that triggered - * the event, otherwise returns an empty string. - * - * @param evt the event which is the source of the sound effect - * @return a string containing the SoundEffect SVar, or empty string if - * SVar:SoundEffect does not exist. - */ - public String getScriptedSoundEffectName(GameEvent evt) { - Card c = null; - - if (evt instanceof GameEventSpellResolved) { - c = ((GameEventSpellResolved) evt).spell.getSourceCard(); - } else if (evt instanceof GameEventLandPlayed) { - c = ((GameEventLandPlayed) evt).land; - } - - return c != null ? c.getSVar("SoundEffect") : ""; - } - - - @Override - public SoundEffectType visit(UiEventBlockerAssigned event) { - return event.attackerBeingBlocked == null ? null : SoundEffectType.Block; - } - @Override - public SoundEffectType visit(UiEventAttackerDeclared event) { - return null; - } -} +package forge.sound; + +import java.util.Collection; + +import forge.Card; +import forge.card.spellability.SpellAbility; +import forge.game.event.GameEventBlockersDeclared; +import forge.game.event.GameEventCardChangeZone; +import forge.game.event.GameEventCardDamaged; +import forge.game.event.GameEventCardDestroyed; +import forge.game.event.GameEventCardAttachment; +import forge.game.event.GameEventCardRegenerated; +import forge.game.event.GameEventCardSacrificed; +import forge.game.event.GameEventCardCounters; +import forge.game.event.GameEventGameOutcome; +import forge.game.event.GameEventTurnEnded; +import forge.game.event.GameEvent; +import forge.game.event.GameEventFlipCoin; +import forge.game.event.GameEventLandPlayed; +import forge.game.event.GameEventPlayerLivesChanged; +import forge.game.event.GameEventPlayerPoisoned; +import forge.game.event.GameEventCardTapped; +import forge.game.event.GameEventShuffle; +import forge.game.event.GameEventSpellResolved; +import forge.game.event.GameEventTokenCreated; +import forge.game.event.IGameEventVisitor; +import forge.game.zone.ZoneType; +import forge.gui.events.IUiEventVisitor; +import forge.gui.events.UiEventAttackerDeclared; +import forge.gui.events.UiEventBlockerAssigned; +import forge.net.FServer; +import forge.util.maps.MapOfLists; + +/** + * This class is in charge of converting any forge.game.event.Event to a SoundEffectType. + * + */ +public class EventVisualizer extends IGameEventVisitor.Base implements IUiEventVisitor { + + public SoundEffectType visit(GameEventCardDamaged event) { return SoundEffectType.Damage; } + public SoundEffectType visit(GameEventCardDestroyed event) { return SoundEffectType.Destroy; } + public SoundEffectType visit(GameEventCardAttachment event) { return SoundEffectType.Equip; } + public SoundEffectType visit(GameEventCardChangeZone event) { + ZoneType from = event.from == null ? null : event.from.getZoneType(); + ZoneType to = event.to.getZoneType(); + if( from == ZoneType.Library && to == ZoneType.Hand) + return SoundEffectType.Draw; + if( from == ZoneType.Hand && (to == ZoneType.Graveyard || to == ZoneType.Library) ) + return SoundEffectType.Discard; + + return to == ZoneType.Exile ? SoundEffectType.Exile : null; + } + public SoundEffectType visit(GameEventCardRegenerated event) { return SoundEffectType.Regen; } + public SoundEffectType visit(GameEventCardSacrificed event) { return SoundEffectType.Sacrifice; } + public SoundEffectType visit(GameEventCardCounters event) { return event.newValue > event.oldValue ? SoundEffectType.AddCounter : event.newValue < event.oldValue ? SoundEffectType.RemoveCounter : null; } + public SoundEffectType visit(GameEventTurnEnded event) { return SoundEffectType.EndOfTurn; } + public SoundEffectType visit(GameEventFlipCoin event) { return SoundEffectType.FlipCoin; } + public SoundEffectType visit(GameEventPlayerLivesChanged event) { return event.newLives < event.oldLives ? SoundEffectType.LifeLoss : SoundEffectType.LifeGain; } + public SoundEffectType visit(GameEventPlayerPoisoned event) { return SoundEffectType.Poison; } + public SoundEffectType visit(GameEventShuffle event) { return SoundEffectType.Shuffle; } + public SoundEffectType visit(GameEventTokenCreated event) { return SoundEffectType.Token; } + public SoundEffectType visit(GameEventBlockersDeclared event) { + boolean isLocalHuman = event.defendingPlayer.getLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer(); + if (isLocalHuman) + return null; // already played sounds in interactive mode + + for(MapOfLists ab : event.blockers.values()) { + for(Collection bb : ab.values()) { + if ( !bb.isEmpty() ) { + // hasAnyBlocker = true; + return SoundEffectType.Block; + } + } + } + return null; + } + + /** + * Plays the sound corresponding to the outcome of the duel. + */ + public SoundEffectType visit(GameEventGameOutcome event) { + boolean humanWonTheDuel = event.result.getWinningLobbyPlayer() == FServer.instance.getLobby().getGuiPlayer(); + return humanWonTheDuel ? SoundEffectType.WinDuel : SoundEffectType.LoseDuel; + } + + /** + * Plays the sound corresponding to the card type/color when the card + * ability resolves on the stack. + */ + public SoundEffectType visit(GameEventSpellResolved evt) { + if (evt.spell == null ) { + return null; + } + + Card source = evt.spell.getSourceCard(); + if (evt.spell.isSpell()) { + // if there's a specific effect for this particular card, play it and + // we're done. + if (hasSpecificCardEffect(source)) { + return SoundEffectType.ScriptedEffect; + } + + if (source.isCreature() && source.isArtifact()) { + return SoundEffectType.ArtifactCreature; + } else if (source.isCreature()) { + return SoundEffectType.Creature; + } else if (source.isArtifact()) { + return SoundEffectType.Artifact; + } else if (source.isInstant()) { + return SoundEffectType.Instant; + } else if (source.isPlaneswalker()) { + return SoundEffectType.Planeswalker; + } else if (source.isSorcery()) { + return SoundEffectType.Sorcery; + } else if (source.isEnchantment()) { + return SoundEffectType.Enchantment; + } + } + return null; + } + + /** + * Plays the sound corresponding to the change of the card's tapped state + * (when a card is tapped or untapped). + * + * @param tapped_state if true, the "tap" sound is played; otherwise, the + * "untap" sound is played + * @return the sound effect type + */ + public SoundEffectType visit(GameEventCardTapped event) { + return event.tapped ? SoundEffectType.Tap : SoundEffectType.Untap; + } + + /** + * Plays the sound corresponding to the land type when the land is played. + * + * @param land the land card that was played + * @return the sound effect type + */ + public SoundEffectType visit(GameEventLandPlayed event) { + if (event.land == null) { + return null; + } + + // if there's a specific effect for this particular card, play it and + // we're done. + if (hasSpecificCardEffect(event.land)) { + return SoundEffectType.ScriptedEffect; + } + + for (SpellAbility sa : event.land.getManaAbility()) { + String manaColors = sa.getManaPartRecursive().getOrigProduced(); + + if (manaColors.contains("B")) return SoundEffectType.BlackLand; + if (manaColors.contains("U")) return SoundEffectType.BlueLand; + if (manaColors.contains("G")) return SoundEffectType.GreenLand; + if (manaColors.contains("R")) return SoundEffectType.RedLand; + if (manaColors.contains("W")) return SoundEffectType.WhiteLand; + } + + // play a generic land sound if no other sound corresponded to it. + return SoundEffectType.OtherLand; + } + + /** + * Play a specific sound effect based on card's name. + * + * @param c the card to play the sound effect for. + * @return the sound effect type + */ + private static boolean hasSpecificCardEffect(final Card c) { + // Implement sound effects for specific cards here, if necessary. + String effect = ""; + if (null != c) { + effect = c.getSVar("SoundEffect"); + } + return effect.isEmpty() ? false : true; + } + + + /** + * Returns the value of the SoundEffect SVar of the card that triggered + * the event, otherwise returns an empty string. + * + * @param evt the event which is the source of the sound effect + * @return a string containing the SoundEffect SVar, or empty string if + * SVar:SoundEffect does not exist. + */ + public String getScriptedSoundEffectName(GameEvent evt) { + Card c = null; + + if (evt instanceof GameEventSpellResolved) { + c = ((GameEventSpellResolved) evt).spell.getSourceCard(); + } else if (evt instanceof GameEventLandPlayed) { + c = ((GameEventLandPlayed) evt).land; + } + + return c != null ? c.getSVar("SoundEffect") : ""; + } + + + @Override + public SoundEffectType visit(UiEventBlockerAssigned event) { + return event.attackerBeingBlocked == null ? null : SoundEffectType.Block; + } + @Override + public SoundEffectType visit(UiEventAttackerDeclared event) { + return null; + } +}