- Added option to replace default "Human" with custom name during gameplay. (http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=11553).

This commit is contained in:
spr
2013-10-07 19:56:00 +00:00
parent a8cf682f71
commit db9a451e32
16 changed files with 1708 additions and 1498 deletions

1
.gitattributes vendored
View File

@@ -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

View File

@@ -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<GameLogEntry> {
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<String> outcomes = new ArrayList<String>();
for (Entry<LobbyPlayer, PlayerStatistics> 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<GameOutcome> 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<LobbyPlayer, PlayerStatistics> 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<Card> 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<Card> blockers = null;
for (Entry<GameEntity, MapOfLists<Card, Card>> kv : ev.blockers.entrySet()) {
GameEntity defender = kv.getKey();
MapOfLists<Card, Card> 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<Card, Collection<Card>> 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<GameLogEntry> {
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<GameOutcome> gamesPlayed) {
GameOutcome outcome1 = gamesPlayed.get(0);
List<Player> 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<Card> 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<Card> blockers = null;
for (Entry<GameEntity, MapOfLists<Card, Card>> kv : ev.blockers.entrySet()) {
GameEntity defender = kv.getKey();
MapOfLists<Card, Card> 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<Card, Collection<Card>> 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

View File

@@ -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.
* <p>
* 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<RegisteredPlayer> 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();
}
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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;
/**
* <p>
* GameInfo class.
* </p>
*
* @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<Pair<LobbyPlayer, PlayerStatistics>> {
/** The player got first turn. */
// private String playerGotFirstTurn = "Nobody";
/** The last turn number. */
private int lastTurnNumber = 0;
/** The player rating. */
private final List<Pair<LobbyPlayer, PlayerStatistics>> playerRating = new ArrayList<Pair<LobbyPlayer, PlayerStatistics>>(2);
private GameEndReason winCondition;
public GameOutcome(GameEndReason reason, final Iterable<Player> list) {
winCondition = reason;
for (final Player n : list) {
this.playerRating.add(Pair.of(n.getLobbyPlayer(), n.getStats()));
}
}
public boolean isDraw() {
for (Pair<LobbyPlayer, PlayerStatistics> pv : playerRating) {
if (pv.getValue().getOutcome().hasWon()) {
return false;
}
}
return true;
}
public boolean isWinner(final LobbyPlayer who) {
for (Pair<LobbyPlayer, PlayerStatistics> 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<LobbyPlayer, PlayerStatistics> 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<LobbyPlayer, PlayerStatistics> 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<Pair<LobbyPlayer, PlayerStatistics>> 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 <http://www.gnu.org/licenses/>.
*/
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;
/**
* <p>
* GameInfo class.
* </p>
*
* @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<Pair<LobbyPlayer, PlayerStatistics>> {
/** The player got first turn. */
// private String playerGotFirstTurn = "Nobody";
/** The last turn number. */
private int lastTurnNumber = 0;
/** The player rating. */
private final List<Pair<LobbyPlayer, PlayerStatistics>> playerRating = new ArrayList<Pair<LobbyPlayer, PlayerStatistics>>(2);
private final Iterable<Player> players;
private GameEndReason winCondition;
public GameOutcome(GameEndReason reason, final Iterable<Player> list) {
winCondition = reason;
players = list;
for (final Player n : list) {
this.playerRating.add(Pair.of(n.getLobbyPlayer(), n.getStats()));
}
}
public boolean isDraw() {
for (Pair<LobbyPlayer, PlayerStatistics> pv : playerRating) {
if (pv.getValue().getOutcome().hasWon()) {
return false;
}
}
return true;
}
public boolean isWinner(final LobbyPlayer who) {
for (Pair<LobbyPlayer, PlayerStatistics> 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<LobbyPlayer, PlayerStatistics> ps : playerRating) {
if (ps.getValue().getOutcome().hasWon()) {
return ps.getKey();
}
}
return null;
}
/**
* Gets winning {@code Player}.
* <p>
* 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<LobbyPlayer, PlayerStatistics> 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<Pair<LobbyPlayer, PlayerStatistics>> 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<Player> getPlayers() {
return (List<Player>)players;
}
}

View File

@@ -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<RegisteredPlayer> 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<GameOutcome> gamesPlayed = new ArrayList<GameOutcome>();
private final List<GameOutcome> gamesPlayedRo;
/**
* This should become constructor once.
*/
public Match(GameType type, List<RegisteredPlayer> players0) {
gamesPlayedRo = Collections.unmodifiableList(gamesPlayed);
players = Collections.unmodifiableList(Lists.newArrayList(players0));
gameType = type;
}
public Match(GameType type, List<RegisteredPlayer> players0, Boolean overrideAnte) {
this(type, players0);
if( overrideAnte != null )
this.useAnte = overrideAnte.booleanValue();
}
/**
* Gets the games played.
*
* @return the games played
*/
public final List<GameOutcome> 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<Pair<Player, Card>> 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<GameOutcome> 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<RegisteredPlayer> 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<RegisteredPlayer> 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<GameOutcome> gamesPlayed = new ArrayList<GameOutcome>();
private final List<GameOutcome> gamesPlayedRo;
/**
* This should become constructor once.
*/
public Match(GameType type, List<RegisteredPlayer> players0) {
gamesPlayedRo = Collections.unmodifiableList(gamesPlayed);
players = Collections.unmodifiableList(Lists.newArrayList(players0));
gameType = type;
}
public Match(GameType type, List<RegisteredPlayer> players0, Boolean overrideAnte) {
this(type, players0);
if( overrideAnte != null )
this.useAnte = overrideAnte.booleanValue();
}
/**
* Gets the games played.
*
* @return the games played
*/
public final List<GameOutcome> 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<Pair<Player, Card>> 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<GameOutcome> 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<RegisteredPlayer> getPlayers() {
return players;
}
/**
* TODO: Write javadoc for this method.
* @return
*/
public static int getPoisonCountersAmountToLose() {
return 10;
}
}

View File

@@ -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<Player> {
private final Map<Card,Integer> commanderDamage = new HashMap<Card,Integer>();
/** The poison counters. */
private int poisonCounters = 0;
@@ -108,7 +110,7 @@ public class Player extends GameEntity implements Comparable<Player> {
/** 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<Player> {
/** 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<Player> {
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<Player> {
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<Player> {
}
@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<Player> {
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<Player> {
return other != this && other != null && ( other.teamNumber < 0 || other.teamNumber != this.teamNumber );
}
// ////////////////////////
@@ -469,7 +480,7 @@ public class Player extends GameEntity implements Comparable<Player> {
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<Player> {
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<Player> {
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<Player> {
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<Player> {
}
return num;
}
public final Iterable<Card> getAssignedDamageSources() {
public final Iterable<Card> getAssignedDamageSources() {
return assignedDamage.keySet();
}
@@ -1042,7 +1053,7 @@ public class Player extends GameEntity implements Comparable<Player> {
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<Player> {
*
* @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<Player> {
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<Player> {
*/
public final List<Card> drawCards(final int n) {
final List<Card> drawn = new ArrayList<Card>();
if (!this.canDraw()) {
return drawn;
}
for (int i = 0; i < n; i++) {
// TODO: multiple replacements need to be selected by the controller
List<Card> dredgers = this.getDredge();
if (!dredgers.isEmpty()) {
@@ -1299,7 +1310,7 @@ public class Player extends GameEntity implements Comparable<Player> {
continue;
}
}
drawn.addAll(this.doDraw());
}
return drawn;
@@ -1325,14 +1336,14 @@ public class Player extends GameEntity implements Comparable<Player> {
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<Card> 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<Player> {
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<Player> {
}
}
}
// 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<Player> {
return CardLists.filter(this.getCardsIn(zone), CardPredicates.nameEquals(cardName));
}
public List<Card> getCardsActivableInExternalZones() {
final List<Card> cl = new ArrayList<Card>();
@@ -1580,7 +1591,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
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<Player> {
public final int getNumDiscardedThisTurn() {
return this.numDiscardedThisTurn;
}
/**
* <p>
* Getter for the field <code>numDiscardedThisTurn</code>.
@@ -1791,7 +1802,7 @@ public class Player extends GameEntity implements Comparable<Player> {
// Play the shuffle sound
game.fireEvent(new GameEventShuffle(this));
} // shuffle
// //////////////////////////////
// //////////////////////////////
// //////////////////////////////
@@ -1860,7 +1871,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
}
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<Player> {
// **** 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<Player> {
*
* @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<Player> {
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<Player> {
if (this.poisonCounters >= 10) {
return this.loseConditionMet(GameLossReason.Poisoned, null);
}
if(game.getType() == GameType.Commander)
{
Map<Card,Integer> cmdDmg = getCommanderDamage();
@@ -2201,13 +2212,13 @@ public class Player extends GameEntity implements Comparable<Player> {
{
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<Player> {
public final void resetProwl() {
this.prowl = new ArrayList<String>();
}
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<Player> {
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<Player> {
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<Player> {
public final int getLifeGainedThisTurn() {
return this.lifeGainedThisTurn;
}
/**
* <p>
* Setter for the field <code>lifeGainedThisTurn</code>.
@@ -2766,11 +2777,11 @@ public class Player extends GameEntity implements Comparable<Player> {
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<Player> {
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<Player> {
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<Player> {
}
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<Player> {
/**
*
* 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<Player> {
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<String,Object> runParams = new HashMap<String,Object>();
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<Player> {
{
if(!currentPlanes.isEmpty())
{
//Run PlaneswalkedFrom triggers here.
//Run PlaneswalkedFrom triggers here.
HashMap<String,Object> runParams = new HashMap<String,Object>();
runParams.put("Card", new ArrayList<Card>(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<Player> {
//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<Player> {
break;
}
}
game.setActivePlanes(currentPlanes);
}
@@ -3080,7 +3091,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final void resetAttackedThisCombat() {
// resets the status of attacked/blocked this phase
List<Card> 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<Player> {
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<Player> {
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<Player> {
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<Player> {
}
/**
* @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<Player> {
public boolean isPlayingExtraTurn() {
return isPlayingExtraTrun;
}
private boolean isHotSeatGame(Game game0) {
boolean isHotSeat = false;
List<RegisteredPlayer> 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;
}
}

View File

@@ -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<ZoneType, String> humanCardTexts,
final Map<ZoneType, String> 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<SpellAbility> 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

View File

@@ -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.
*
* <br><br><i>(C at beginning of class name denotes a control class.)</i>
@@ -38,10 +39,10 @@ import java.util.List;
public enum CSubmenuPreferences implements ICDoc {
/** */
SINGLETON_INSTANCE;
private VSubmenuPreferences view;
private ForgePreferences prefs;
private final List<Pair<JCheckBox, FPref>> lstControls = new ArrayList<Pair<JCheckBox,FPref>>();
/* (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<JCheckBox, FPref> 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<GameLogEntryType> 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<String> panel = this.view.getAiProfilesComboBoxPanel();
JComboBox<String> comboBox = createComboBox(AiProfileUtil.getProfilesArray(), userSetting);
String selectedItem = this.prefs.getPref(userSetting);
panel.setComboBox(comboBox, selectedItem);
panel.setComboBox(comboBox, selectedItem);
}
private void initializeSkinsComboBox() {
final FComboBoxPanel<String> 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 <E> JComboBox<E> createComboBox(E[] items, final ForgePreferences.FPref setting) {
final JComboBox<E> comboBox = new JComboBox<E>(items);
addComboBoxListener(comboBox, setting);
return comboBox;
}
private <E> void addComboBoxListener(final JComboBox<E> 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();
}
};
}
}

View File

@@ -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();
}
}
}

View File

@@ -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.
*
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
@@ -59,7 +61,8 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
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<CSubmenuPreferences> {
private final Map<FPref, KeyboardShortcutField> shortcutFields = new HashMap<FPref, KeyboardShortcutField>();
// ComboBox items are added in CSubmenuPreferences since this is just the View.
private final FComboBoxPanel<String> cbpSkin = new FComboBoxPanel<String>("Choose Skin:");
private final FComboBoxPanel<String> cbpSkin = new FComboBoxPanel<String>("Choose Skin:");
private final FComboBoxPanel<GameLogEntryType> cbpGameLogEntryType = new FComboBoxPanel<GameLogEntryType>("Game Log Verbosity:");
private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<String>("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<CSubmenuPreferences> {
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<CSubmenuPreferences> {
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<CSubmenuPreferences> {
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<CSubmenuPreferences> {
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<CSubmenuPreferences> {
scrContent.setBorder(null);
}
public void reloadShortcuts() {
for (Map.Entry<FPref, KeyboardShortcutField> e : shortcutFields.entrySet()) {
e.getValue().reload(e.getKey());
@@ -396,11 +402,11 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
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<CSubmenuPreferences> {
public JCheckBox getCbOverlayCardManaCost() {
return cbOverlayCardManaCost;
}
/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbRandomFoil() {
return cbRandomFoil;
@@ -471,7 +477,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
public JCheckBox getCbDevMode() {
return cbDevMode;
}
public FComboBoxPanel<String> getAiProfilesComboBoxPanel() {
return cbpAiProfiles;
}
@@ -479,11 +485,11 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
public FComboBoxPanel<GameLogEntryType> getGameLogVerbosityComboBoxPanel() {
return cbpGameLogEntryType;
}
public FComboBoxPanel<String> getSkinsComboBoxPanel() {
return cbpSkin;
}
/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbEnforceDeckLegality() {
return cbEnforceDeckLegality;
@@ -506,7 +512,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
/** @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<CSubmenuPreferences> {
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<CSubmenuPreferences> {
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;
}
}

View File

@@ -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<PaperCard> anteCards = new ArrayList<PaperCard>();
for (Player p : lastGame.getRegisteredPlayers()) {
if (p.getLobbyPlayer().equals(questPlayer) == isHumanWinner) {

View File

@@ -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<LobbyPlayer, PlayerStatistics> 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("\\[[^\\]]*\\]", "");
}
}

View File

@@ -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 <deck1[.dck]> <deck2[.dck]> [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<RegisteredPlayer> pp = new ArrayList<RegisteredPlayer>();
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<GameLogEntry> 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 <deck1[.dck]> <deck2[.dck]> [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<RegisteredPlayer> pp = new ArrayList<RegisteredPlayer>();
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<GameLogEntry> 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;
}
}

View File

@@ -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<String, LobbyPlayerRemote> remotePlayers = new ConcurrentHashMap<String, LobbyPlayerRemote>();
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<String, LobbyPlayerRemote> remotePlayers = new ConcurrentHashMap<String, LobbyPlayerRemote>();
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);
}
}
}

View File

@@ -34,6 +34,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
* Preference identifiers, and their default values.
*/
public static enum FPref {
PLAYER_NAME (""),
CONSTRUCTED_P1_DECK_STATE(""),
CONSTRUCTED_P2_DECK_STATE(""),
CONSTRUCTED_GAMEPLAYERS(""),

View File

@@ -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<SoundEffectType> implements IUiEventVisitor<SoundEffectType> {
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<Card, Card> ab : event.blockers.values()) {
for(Collection<Card> 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<SoundEffectType> implements IUiEventVisitor<SoundEffectType> {
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<Card, Card> ab : event.blockers.values()) {
for(Collection<Card> 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;
}
}