Refactor lobby and add network support.

- Lobby code has been revised completely to support network lobbies;
  code has been separated into model and view parts.
- Added preliminary network game support; most of it should be working
  but hasn't been tested thoroughly.
- Fixed issue where controlling another player wouldn't be recognised
  by the GUI.
- Fixed issue where hand panels wouldn't display anymore.
- Minor fixes/cleanup.
This commit is contained in:
elcnesh
2015-03-14 10:53:09 +00:00
parent a73b67dc04
commit b98684877d
105 changed files with 3089 additions and 1888 deletions

23
.gitattributes vendored
View File

@@ -164,6 +164,7 @@ forge-core/.settings/org.eclipse.jdt.core.prefs -text
forge-core/.settings/org.eclipse.m2e.core.prefs -text
forge-core/pom.xml -text
forge-core/pom.xml.releaseBackup -text
forge-core/src/main/java/forge/AIOption.java -text
forge-core/src/main/java/forge/CardStorageReader.java -text
forge-core/src/main/java/forge/FTrace.java -text
forge-core/src/main/java/forge/ImageKeys.java -text
@@ -17399,10 +17400,12 @@ forge-gui/src/main/java/forge/interfaces/IGuiBase.java -text
forge-gui/src/main/java/forge/interfaces/IGuiGame.java -text
forge-gui/src/main/java/forge/interfaces/IGuiTimer.java -text
forge-gui/src/main/java/forge/interfaces/ILobby.java -text
forge-gui/src/main/java/forge/interfaces/ILobbyListener.java -text
forge-gui/src/main/java/forge/interfaces/IMayViewCards.java -text
forge-gui/src/main/java/forge/interfaces/IPlayerChangeListener.java -text
forge-gui/src/main/java/forge/interfaces/IProgressBar.java -text
forge-gui/src/main/java/forge/interfaces/ITextField.java -text
forge-gui/src/main/java/forge/interfaces/IUpdateable.java -text
forge-gui/src/main/java/forge/interfaces/IWinLoseView.java -text
forge-gui/src/main/java/forge/itemmanager/BooleanExpression.java -text
forge-gui/src/main/java/forge/itemmanager/ColumnDef.java -text
@@ -17436,9 +17439,12 @@ forge-gui/src/main/java/forge/limited/WinstonDraft.java -text
forge-gui/src/main/java/forge/limited/WinstonDraftAI.java -text
forge-gui/src/main/java/forge/limited/package-info.java svneol=native#text/plain
forge-gui/src/main/java/forge/match/AbstractGuiGame.java -text
forge-gui/src/main/java/forge/match/GameLobby.java -text
forge-gui/src/main/java/forge/match/HostedMatch.java -text
forge-gui/src/main/java/forge/match/LobbySlot.java -text
forge-gui/src/main/java/forge/match/LocalLobby.java -text
forge-gui/src/main/java/forge/match/MatchButtonType.java -text
forge-gui/src/main/java/forge/match/MatchConstants.java -text
forge-gui/src/main/java/forge/match/NetGuiGame.java -text
forge-gui/src/main/java/forge/match/NextGameDecision.java -text
forge-gui/src/main/java/forge/match/input/Input.java -text
forge-gui/src/main/java/forge/match/input/InputAttack.java -text
@@ -17470,9 +17476,13 @@ forge-gui/src/main/java/forge/model/MetaSet.java -text
forge-gui/src/main/java/forge/model/MultipleForgeJarsFoundError.java -text
forge-gui/src/main/java/forge/model/UnOpenedMeta.java -text
forge-gui/src/main/java/forge/model/package-info.java svneol=native#text/plain
forge-gui/src/main/java/forge/net/ClientGameLobby.java -text
forge-gui/src/main/java/forge/net/FGameClient.java -text
forge-gui/src/main/java/forge/net/FServerManager.java -text
forge-gui/src/main/java/forge/net/NetGame.java -text
forge-gui/src/main/java/forge/net/LobbyUpdateEvent.java -text
forge-gui/src/main/java/forge/net/NetGameController.java -text
forge-gui/src/main/java/forge/net/NetGuiGame.java -text
forge-gui/src/main/java/forge/net/ServerGameLobby.java -text
forge-gui/src/main/java/forge/net/package-info.java -text
forge-gui/src/main/java/forge/planarconquest/ConquestAction.java -text
forge-gui/src/main/java/forge/planarconquest/ConquestCommander.java -text
@@ -17491,7 +17501,6 @@ forge-gui/src/main/java/forge/player/HumanCostDecision.java -text
forge-gui/src/main/java/forge/player/HumanPlay.java -text
forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java -text
forge-gui/src/main/java/forge/player/LobbyPlayerHuman.java -text
forge-gui/src/main/java/forge/player/NetGameController.java -text
forge-gui/src/main/java/forge/player/PlayerControllerHuman.java -text
forge-gui/src/main/java/forge/player/TargetSelection.java -text
forge-gui/src/main/java/forge/player/package-info.java -text
@@ -17606,6 +17615,7 @@ forge-net/src/main/java/forge/net/IConnectionObserver.java -text
forge-net/src/main/java/forge/net/Lobby.java -text
forge-net/src/main/java/forge/net/LobbyPlayerRemote.java -text
forge-net/src/main/java/forge/net/NetServer.java -text
forge-net/src/main/java/forge/net/ReplyPool.java -text
forge-net/src/main/java/forge/net/client/GameServlet.java -text
forge-net/src/main/java/forge/net/client/INetClient.java -text
forge-net/src/main/java/forge/net/client/InvalidFieldInPacketException.java -text
@@ -17618,15 +17628,14 @@ forge-net/src/main/java/forge/net/client/state/UnauthorizedClientState.java -tex
forge-net/src/main/java/forge/net/client/state/package-info.java -text
forge-net/src/main/java/forge/net/game/GuiGameEvent.java -text
forge-net/src/main/java/forge/net/game/IRemote.java -text
forge-net/src/main/java/forge/net/game/IdentifiableNetEvent.java -text
forge-net/src/main/java/forge/net/game/LobbySlotType.java -text
forge-net/src/main/java/forge/net/game/LobbyState.java -text
forge-net/src/main/java/forge/net/game/LobbyUpdateEvent.java -text
forge-net/src/main/java/forge/net/game/LoginEvent.java -text
forge-net/src/main/java/forge/net/game/LogoutEvent.java -text
forge-net/src/main/java/forge/net/game/MessageEvent.java -text
forge-net/src/main/java/forge/net/game/NetEvent.java -text
forge-net/src/main/java/forge/net/game/RegisterDeckEvent.java -text
forge-net/src/main/java/forge/net/game/client/ILobbyListener.java -text
forge-net/src/main/java/forge/net/game/ReplyEvent.java -text
forge-net/src/main/java/forge/net/game/UpdateLobbyPlayerEvent.java -text
forge-net/src/main/java/forge/net/game/client/IToServer.java -text
forge-net/src/main/java/forge/net/game/client/package-info.java -text
forge-net/src/main/java/forge/net/game/package-info.java -text

View File

@@ -1,7 +1,8 @@
package forge.ai;
import java.util.Map;
import java.util.Set;
import forge.AIOption;
import forge.LobbyPlayer;
import forge.game.Game;
import forge.game.player.IGameEntitiesFactory;
@@ -15,9 +16,9 @@ public class LobbyPlayerAi extends LobbyPlayer implements IGameEntitiesFactory {
private boolean allowCheatShuffle;
private boolean useSimulation;
public LobbyPlayerAi(String name, Map<String, String> options) {
public LobbyPlayerAi(String name, Set<AIOption> options) {
super(name);
if (options != null && "True".equals(options.get("UseSimulation"))) {
if (options != null && options.contains(AIOption.USE_SIMULATION)) {
this.useSimulation = true;
}
}

View File

@@ -36,6 +36,7 @@ import forge.game.player.DelayedReveal;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerController;
import forge.game.player.PlayerView;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*;
import forge.game.trigger.Trigger;
@@ -141,7 +142,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
ApiType api = sa.getApi();
if (null == api) {
@@ -240,6 +241,11 @@ public class PlayerControllerAi extends PlayerController {
// We don't know how to reveal cards to AI
}
@Override
public void reveal(Collection<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix) {
// We don't know how to reveal cards to AI
}
@Override
public ImmutablePair<CardCollection, CardCollection> arrangeForScry(CardCollection topN) {
CardCollection toBottom = new CardCollection();
@@ -850,7 +856,7 @@ public class PlayerControllerAi extends PlayerController {
String selectPrompt, boolean isOptional, Player decider) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
}

View File

@@ -0,0 +1,5 @@
package forge;
public enum AIOption {
USE_SIMULATION;
}

View File

@@ -41,6 +41,8 @@ import java.util.Set;
* @version $Id: java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardType implements Comparable<CardType>, CardTypeView {
private static final long serialVersionUID = 7612877255541109847L;
public static final CardTypeView EMPTY = new CardType();
public enum CoreType {

View File

@@ -1,5 +1,6 @@
package forge.card;
import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -8,7 +9,7 @@ import forge.card.CardType.CoreType;
import forge.card.CardType.Supertype;
//Interface to expose only the desired functions of CardType without allowing modification
public interface CardTypeView extends Iterable<String> {
public interface CardTypeView extends Iterable<String>, Serializable {
boolean isEmpty();
Iterable<CoreType> getCoreTypes();
Iterable<Supertype> getSupertypes();

View File

@@ -18,9 +18,11 @@
package forge.card;
import com.google.common.collect.UnmodifiableIterator;
import forge.card.mana.ManaCost;
import forge.util.BinaryUtil;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
@@ -34,7 +36,8 @@ import java.util.NoSuchElementException;
*
*
*/
public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte> {
public final class ColorSet implements Comparable<ColorSet>, Iterable<Byte>, Serializable {
private static final long serialVersionUID = 794691267379929080L;
private final byte myColor;
private final float orderWeight;

View File

@@ -19,6 +19,7 @@ package forge.card.mana;
import forge.card.MagicColor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -35,7 +36,9 @@ import org.apache.commons.lang3.StringUtils;
* @version $Id: CardManaCost.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostShard> {
public final class ManaCost implements Comparable<ManaCost>, Iterable<ManaCostShard>, Serializable {
private static final long serialVersionUID = -2477430496624149226L;
private static final char DELIM = (char)6;
private List<ManaCostShard> shards;

View File

@@ -17,6 +17,15 @@
*/
package forge.deck;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import forge.StaticData;
@@ -26,17 +35,9 @@ import forge.util.ItemPool;
import forge.util.ItemPoolSorter;
import forge.util.MyRandom;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CardPool extends ItemPool<PaperCard> {
private static final long serialVersionUID = -5379091255613968393L;
public CardPool() {
super(PaperCard.class);

View File

@@ -17,13 +17,17 @@
*/
package forge.item;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import com.google.common.base.Function;
import forge.ImageKeys;
import forge.StaticData;
import forge.card.CardRarity;
import forge.card.CardRules;
/**
* A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade).
* <br><br>
@@ -31,9 +35,11 @@ import forge.card.CardRules;
*
* @author Forge
*/
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard, Serializable {
private static final long serialVersionUID = 2942081982620691205L;
// Reference to rules
private final transient CardRules rules;
private transient CardRules rules;
// These fields are kinda PK for PrintedCard
private final String name;
@@ -43,7 +49,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
private Boolean hasImage;
// Calculated fields are below:
private final transient CardRarity rarity; // rarity is given in ctor when set is assigned
private transient CardRarity rarity; // rarity is given in ctor when set is assigned
@Override
public String getName() {
@@ -199,4 +205,13 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
return Integer.compare(artIndex, o.getArtIndex());
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
// default deserialization
ois.defaultReadObject();
final IPaperCard pc = StaticData.instance().getCommonCards().getCard(name, edition, artIndex);
this.rules = pc.getRules();
this.rarity = pc.getRarity();
}
}

View File

@@ -22,6 +22,7 @@ import com.google.common.base.Predicate;
import forge.item.InventoryItem;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
@@ -34,7 +35,8 @@ import java.util.Map.Entry;
* @param <T>
* an Object
*/
public class ItemPool<T extends InventoryItem> implements Iterable<Entry<T, Integer>> {
public class ItemPool<T extends InventoryItem> implements Iterable<Entry<T, Integer>>, Serializable {
private static final long serialVersionUID = 6572047177527559797L;
/** The fn to printed. */
public final transient Function<Entry<T, Integer>, T> FN_GET_KEY = new Function<Entry<T, Integer>, T>() {

View File

@@ -56,8 +56,10 @@ import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.player.RegisteredPlayer;
import forge.game.replacement.ReplacementHandler;
import forge.game.spellability.Ability;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.SpellAbilityView;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.MagicStack;
@@ -105,20 +107,20 @@ public class Game {
private final GameView view;
private final Tracker tracker = new Tracker();
private GameEntityCache<Player, PlayerView> playerCache = new GameEntityCache<>();
private final GameEntityCache<Player, PlayerView> playerCache = new GameEntityCache<>();
public Player getPlayer(PlayerView playerView) {
return playerCache.get(playerView);
}
public void addPlayer(Integer id, Player player) {
playerCache.put(id, player);
public void addPlayer(int id, Player player) {
playerCache.put(Integer.valueOf(id), player);
}
public GameEntityCache<Card, CardView> cardCache = new GameEntityCache<>();
private final GameEntityCache<Card, CardView> cardCache = new GameEntityCache<>();
public Card getCard(CardView cardView) {
return cardCache.get(cardView);
}
public void addCard(Integer id, Card card) {
cardCache.put(id, card);
public void addCard(int id, Card card) {
cardCache.put(Integer.valueOf(id), card);
}
public CardCollection getCardList(Iterable<CardView> cardViews) {
CardCollection list = new CardCollection();
@@ -126,10 +128,20 @@ public class Game {
return list;
}
private final GameEntityCache<SpellAbility, SpellAbilityView> spabCache = new GameEntityCache<>();
public SpellAbility getSpellAbility(final SpellAbilityView view) {
return spabCache.get(view);
}
public void addSpellAbility(int id, SpellAbility spellAbility) {
spabCache.put(Integer.valueOf(id), spellAbility);
}
public Game(List<RegisteredPlayer> players0, GameRules rules0, Match match0) { /* no more zones to map here */
rules = rules0;
match = match0;
spabCache.put(Ability.PLAY_LAND_SURROGATE.getId(), Ability.PLAY_LAND_SURROGATE);
int highestTeam = -1;
for (RegisteredPlayer psc : players0) {
// Track highest team number for auto assigning unassigned teams

View File

@@ -1,10 +1,12 @@
package forge.game;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.List;
public class GameEntityCache<Entity extends GameEntity, View extends GameEntityView> {
import forge.trackable.TrackableObject;
public class GameEntityCache<Entity extends IIdentifiable, View extends TrackableObject> {
private HashMap<Integer, Entity> entityCache = new HashMap<Integer, Entity>();
public void put(Integer id, Entity entity) {

View File

@@ -7,6 +7,8 @@ import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
public abstract class GameEntityView extends TrackableObject {
private static final long serialVersionUID = -5129089945124455670L;
public static GameEntityView get(GameEntity e) {
return e == null ? null : e.getView();
}

View File

@@ -19,6 +19,7 @@ package forge.game;
import forge.game.event.IGameEventVisitor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
@@ -30,10 +31,12 @@ import java.util.Observable;
* @author Forge
* @version $Id: GameLog.java 12297 2011-11-28 19:56:47Z slapshot5 $
*/
public class GameLog extends Observable {
private List<GameLogEntry> log = new ArrayList<GameLogEntry>();
public class GameLog extends Observable implements Serializable {
private static final long serialVersionUID = 6465283802022948827L;
private GameLogFormatter formatter = new GameLogFormatter(this);
private final List<GameLogEntry> log = new ArrayList<GameLogEntry>();
private final transient GameLogFormatter formatter = new GameLogFormatter(this);
/** Logging level:
* 0 - Turn

View File

@@ -1,7 +1,10 @@
package forge.game;
import java.io.Serializable;
public class GameLogEntry implements Serializable {
private static final long serialVersionUID = -5322859985172769630L;
public class GameLogEntry {
public final String message;
public final GameLogEntryType type;
// might add here date and some other fields
@@ -13,7 +16,6 @@ public class GameLogEntry {
@Override
public String toString() {
// TODO Auto-generated method stub
return type.getCaption() + ": " + message;
}
}

View File

@@ -111,14 +111,14 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
@Override
public GameLogEntry visit(final GameEventPlayerControl event) {
final LobbyPlayer newController = event.newController;
final LobbyPlayer newLobbyPlayer = event.newLobbyPlayer;
final Player p = event.player;
final String message;
if (newController == null) {
if (newLobbyPlayer == null) {
message = p.getName() + " has restored control over themself";
} else {
message = String.format("%s is controlled by %s", p.getName(), newController.getName());
message = String.format("%s is controlled by %s", p.getName(), newLobbyPlayer.getName());
}
return new GameLogEntry(GameLogEntryType.PLAYER_CONROL, message);
}

View File

@@ -25,6 +25,7 @@ import forge.item.PaperCard;
import org.apache.commons.lang3.tuple.Pair;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
@@ -41,7 +42,9 @@ import java.util.Map.Entry;
// only getters) and
// GameObserver class - who should be notified of any considerable ingame event
public final class GameOutcome implements Iterable<Pair<LobbyPlayer, PlayerStatistics>> {
public static class AnteResult {
public static class AnteResult implements Serializable {
private static final long serialVersionUID = 5087554550408543192L;
public final List<PaperCard> lostCards;
public final List<PaperCard> wonCards;
@@ -50,8 +53,7 @@ public final class GameOutcome implements Iterable<Pair<LobbyPlayer, PlayerStati
if (won) {
this.wonCards = cards;
this.lostCards = new ArrayList<>();
}
else {
} else {
this.lostCards = cards;
this.wonCards = new ArrayList<>();
}

View File

@@ -2,6 +2,8 @@ package forge.game;
import java.util.List;
import com.google.common.collect.Iterables;
import forge.LobbyPlayer;
import forge.deck.Deck;
import forge.game.GameOutcome.AnteResult;
@@ -22,6 +24,7 @@ import forge.trackable.TrackableProperty;
import forge.util.FCollectionView;
public class GameView extends TrackableObject {
private static final long serialVersionUID = 8522884512960961528L;
/*private final TrackableIndex<CardView> cards = new TrackableIndex<CardView>();
private final TrackableIndex<PlayerView> players = new TrackableIndex<PlayerView>();
@@ -29,7 +32,7 @@ public class GameView extends TrackableObject {
private final TrackableIndex<StackItemView> stackItems = new TrackableIndex<StackItemView>();*/
private final TrackableCollection<PlayerView> players;
private CombatView combatView;
private final Game game; //TODO: Remove this when possible before network support added
private final transient Game game; //TODO: Remove this when possible before network support added
public GameView(final Game game0) {
super(-1, game0.getTracker()); //ID not needed
@@ -88,10 +91,17 @@ public class GameView extends TrackableObject {
set(TrackableProperty.PlayerTurn, PlayerView.get(phaseHandler.getPlayerTurn()));
}
public FCollectionView<StackItemView> getStack() {
return get(TrackableProperty.Stack);
}
public StackItemView peekStack() {
return Iterables.getFirst(getStack(), null);
}
public int getStormCount() {
return get(TrackableProperty.StormCount);
}
void updateStack(MagicStack stack) {
void updateStack(final MagicStack stack) {
set(TrackableProperty.Stack, StackItemView.getCollection(stack));
set(TrackableProperty.StormCount, stack.getSpellsCastThisTurn().size());
}
@@ -166,10 +176,6 @@ public class GameView extends TrackableObject {
//TODO: Find better ways to make this information available to all GUIs without using the Game class
public FCollectionView<StackItemView> getStack() {
return StackItemView.getCollection(game.getStack());
}
public boolean isMatchWonBy(LobbyPlayer questPlayer) {
return game.getMatch().isWonBy(questPlayer);
}
@@ -182,10 +188,6 @@ public class GameView extends TrackableObject {
return game.getOutcome().getWinningLobbyPlayer();
}
public StackItemView peekStack() {
return StackItemView.get(game.getStack().peek());
}
public boolean isWinner(LobbyPlayer guiPlayer) {
return game.getOutcome().isWinner(guiPlayer);
}

View File

@@ -16,6 +16,7 @@ import forge.game.event.GameEventCombatChanged;
import forge.game.player.DelayedReveal;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerView;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
@@ -700,10 +701,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
final int fetchNum = Math.min(player.getCardsIn(ZoneType.Library).size(), 4);
CardCollectionView shown = !decider.hasKeyword("LimitSearchLibrary") ? player.getCardsIn(ZoneType.Library) : player.getCardsIn(ZoneType.Library, fetchNum);
// Look at whole library before moving onto choosing a card
delayedReveal = new DelayedReveal(shown, ZoneType.Library, player, source.getName() + " - Looking at cards in ");
delayedReveal = new DelayedReveal(shown, ZoneType.Library, PlayerView.get(player), source.getName() + " - Looking at cards in ");
}
else if (origin.contains(ZoneType.Hand) && player.isOpponentOf(decider)) {
delayedReveal = new DelayedReveal(player.getCardsIn(ZoneType.Hand), ZoneType.Hand, player, source.getName() + " - Looking at cards in ");
delayedReveal = new DelayedReveal(player.getCardsIn(ZoneType.Hand), ZoneType.Hand, PlayerView.get(player), source.getName() + " - Looking at cards in ");
}
}
@@ -784,7 +785,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
Card c = null;
if (sa.hasParam("AtRandom")) {
if (delayedReveal != null) {
delayedReveal.reveal(decider.getController());
decider.getController().reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
c = Aggregates.random(fetchList);
}
@@ -812,7 +813,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
fetchList.remove(c);
if (delayedReveal != null) {
delayedReveal.remove(c);
delayedReveal.remove(CardView.get(c));
}
chosenCards.add(c);

View File

@@ -10,6 +10,7 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.player.DelayedReveal;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.PlayerZone;
@@ -144,7 +145,7 @@ public class DigEffect extends SpellAbilityEffect {
}
else if (!sa.hasParam("NoLooking")) {
// show the user the revealed cards
delayedReveal = new DelayedReveal(top, srcZone, p);
delayedReveal = new DelayedReveal(top, srcZone, PlayerView.get(p));
if (noMove) {
// Let the activating player see the cards even if they're not moved

View File

@@ -4,6 +4,8 @@ import java.util.Collection;
import forge.util.FCollection;
public class CardCollection extends FCollection<Card> implements CardCollectionView {
private static final long serialVersionUID = -8133537013727100275L;
public static final CardCollectionView EMPTY = new CardCollection();
public static boolean hasCard(CardCollection cards) {

View File

@@ -10,10 +10,10 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables;
import forge.ImageKeys;
import forge.card.CardStateName;
import forge.card.CardEdition;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.card.CardStateName;
import forge.card.CardType;
import forge.card.CardTypeView;
import forge.card.ColorSet;
@@ -32,6 +32,8 @@ import forge.trackable.Tracker;
import forge.util.FCollectionView;
public class CardView extends GameEntityView {
private static final long serialVersionUID = -3624090829028979255L;
public static CardView get(Card c) {
return c == null ? null : c.getView();
}
@@ -715,6 +717,8 @@ public class CardView extends GameEntityView {
}
public class CardStateView extends TrackableObject {
private static final long serialVersionUID = 6673944200513430607L;
private final CardStateName state;
public CardStateView(final int id0, final CardStateName state0, final Tracker tracker) {

View File

@@ -17,6 +17,8 @@ import forge.util.FCollection;
public class CombatView extends TrackableObject {
private static final long serialVersionUID = 68085618912864941L;
public CombatView(final Tracker tracker) {
super(-1, tracker); //ID not needed
set(TrackableProperty.AttackersWithDefenders, new HashMap<CardView, GameEntityView>());

View File

@@ -2,20 +2,25 @@ package forge.game.event;
import forge.LobbyPlayer;
import forge.game.player.Player;
import forge.game.player.PlayerController;
public class GameEventPlayerControl extends GameEvent {
public final Player player;
public final LobbyPlayer oldController;
public final LobbyPlayer newController;
public final LobbyPlayer oldLobbyPlayer;
public final PlayerController oldController;
public final LobbyPlayer newLobbyPlayer;
public final PlayerController newController;
public GameEventPlayerControl(Player p, LobbyPlayer old, LobbyPlayer new1) {
player = p;
oldController = old;
newController = new1;
public GameEventPlayerControl(final Player p, final LobbyPlayer oldLobbyPlayer, final PlayerController oldController, final LobbyPlayer newLobbyPlayer, final PlayerController newController) {
this.player = p;
this.oldLobbyPlayer = oldLobbyPlayer;
this.oldController = oldController;
this.newLobbyPlayer = newLobbyPlayer;
this.newController = newController;
}
@Override
public <T> T visit(IGameEventVisitor<T> visitor) {
public <T> T visit(final IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
}

View File

@@ -1,5 +1,6 @@
package forge.game.keyword;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
@@ -7,8 +8,10 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
public class KeywordCollection implements Iterable<String> {
private KeywordCollectionView view;
public class KeywordCollection implements Iterable<String>, Serializable {
private static final long serialVersionUID = -2882986558147844702L;
private transient KeywordCollectionView view;
private final EnumMap<Keyword, List<KeywordInstance<?>>> map = new EnumMap<Keyword, List<KeywordInstance<?>>>(Keyword.class);
public boolean contains(Keyword keyword) {
@@ -134,7 +137,9 @@ public class KeywordCollection implements Iterable<String> {
return view;
}
public class KeywordCollectionView implements Iterable<String> {
public class KeywordCollectionView implements Iterable<String>, Serializable {
private static final long serialVersionUID = 7536969077044188264L;
protected KeywordCollectionView() {
}

View File

@@ -1,28 +1,32 @@
package forge.game.player;
import java.util.Collection;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardView;
import forge.game.zone.ZoneType;
//Stores information to reveal cards after a delay unless those cards can be revealed in the same dialog as cards being selected
/**
* Stores information to reveal cards after a delay unless those cards can be
* revealed in the same dialog as cards being selected
*/
public class DelayedReveal {
private final CardCollection cards;
private final Collection<CardView> cards;
private final ZoneType zone;
private final Player owner;
private final PlayerView owner;
private final String messagePrefix;
private boolean revealed;
public DelayedReveal(Iterable<Card> cards0, ZoneType zone0, Player owner0) {
public DelayedReveal(Iterable<Card> cards0, ZoneType zone0, PlayerView owner0) {
this(cards0, zone0, owner0, null);
}
public DelayedReveal(Iterable<Card> cards0, ZoneType zone0, Player owner0, String messagePrefix0) {
cards = new CardCollection(cards0); //create copy of list to allow modification
public DelayedReveal(Iterable<Card> cards0, ZoneType zone0, PlayerView owner0, String messagePrefix0) {
cards = CardView.getCollection(cards0);
zone = zone0;
owner = owner0;
messagePrefix = messagePrefix0;
}
public Iterable<Card> getCards() {
public Collection<CardView> getCards() {
return cards;
}
@@ -30,17 +34,16 @@ public class DelayedReveal {
return zone;
}
public Player getOwner() {
public PlayerView getOwner() {
return owner;
}
public void remove(Card card) {
public String getMessagePrefix() {
return messagePrefix;
}
public void remove(CardView card) {
cards.remove(card);
}
public void reveal(PlayerController controller) {
if (revealed) { return; } //avoid revealing more than once
revealed = true;
controller.reveal(cards, zone, owner, messagePrefix);
}
}

View File

@@ -2099,7 +2099,7 @@ public class Player extends GameEntity implements Comparable<Player> {
return mindSlaveMaster;
}
public final void setMindSlaveMaster(Player mindSlaveMaster0) {
public final void setMindSlaveMaster(final Player mindSlaveMaster0) {
if (mindSlaveMaster == mindSlaveMaster0) {
return;
}
@@ -2107,14 +2107,14 @@ public class Player extends GameEntity implements Comparable<Player> {
view.updateMindSlaveMaster(this);
if (mindSlaveMaster != null) {
LobbyPlayer oldLobbyPlayer = getLobbyPlayer();
IGameEntitiesFactory master = (IGameEntitiesFactory)mindSlaveMaster.getLobbyPlayer();
final LobbyPlayer oldLobbyPlayer = getLobbyPlayer();
final PlayerController oldController = getController();
final IGameEntitiesFactory master = (IGameEntitiesFactory)mindSlaveMaster.getLobbyPlayer();
controller = master.createMindSlaveController(mindSlaveMaster, this);
game.fireEvent(new GameEventPlayerControl(this, oldLobbyPlayer, getLobbyPlayer()));
}
else {
game.fireEvent(new GameEventPlayerControl(this, oldLobbyPlayer, oldController, getLobbyPlayer(), controller));
} else {
controller = controllerCreator;
game.fireEvent(new GameEventPlayerControl(this, getLobbyPlayer(), null));
game.fireEvent(new GameEventPlayerControl(this, getLobbyPlayer(), controller, null, null));
}
}

View File

@@ -26,6 +26,7 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardShields;
import forge.game.card.CardView;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
@@ -140,6 +141,8 @@ public abstract class PlayerController {
reveal(cards, zone, owner, null);
}
public abstract void reveal(CardCollectionView cards, ZoneType zone, Player owner, String messagePrefix);
public abstract void reveal(Collection<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix);
/** Shows message to player to reveal chosen cardName, creatureType, number etc. AI must analyze API to understand what that is */
public abstract void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value);
public abstract ImmutablePair<CardCollection, CardCollection> arrangeForScry(CardCollection topN);

View File

@@ -21,8 +21,9 @@ import forge.trackable.TrackableProperty;
import forge.trackable.Tracker;
import forge.util.FCollectionView;
public class PlayerView extends GameEntityView {
private static final long serialVersionUID = 7005892740909549086L;
public static PlayerView get(Player p) {
return p == null ? null : p.getView();
}

View File

@@ -147,12 +147,23 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
view0 = new SpellAbilityView(this);
}
view = view0;
if (hostCard != null && hostCard.getGame() != null) {
hostCard.getGame().addSpellAbility(id, this);
}
}
@Override
public int getId() {
return id;
}
@Override
public int hashCode() {
return getId();
}
@Override
public boolean equals(final Object obj) {
return obj instanceof SpellAbility && this.id == ((SpellAbility) obj).id;
};
@Override
public void setHostCard(final Card c) {
@@ -1247,6 +1258,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
public SpellAbilityView getView() {
view.updateHostCard(this);
view.updateDescription(this);
view.updateCanPlay(this);
view.updatePromptIfOnlyPossibleAbility(this);
return view;
}
}

View File

@@ -5,8 +5,9 @@ import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty;
public class SpellAbilityView extends TrackableObject {
private static final long serialVersionUID = 2514234930798754769L;
public static SpellAbilityView get(SpellAbility spab) {
return spab == null ? null : spab.getView();
}

View File

@@ -7,8 +7,9 @@ import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty;
import forge.util.FCollectionView;
public class StackItemView extends TrackableObject {
private static final long serialVersionUID = 6733415646691356052L;
public static StackItemView get(SpellAbilityStackInstance si) {
return si == null ? null : si.getView();
}

View File

@@ -17,6 +17,16 @@
*/
package forge.game.zone;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Stack;
import java.util.concurrent.LinkedBlockingDeque;
import com.esotericsoftware.minlog.Log;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -45,14 +55,17 @@ import forge.game.player.PlayerController.ManaPaymentPurpose;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementHandler;
import forge.game.replacement.ReplacementLayer;
import forge.game.spellability.*;
import forge.game.spellability.Ability;
import forge.game.spellability.AbilityStatic;
import forge.game.spellability.OptionalCost;
import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetChoices;
import forge.game.spellability.TargetRestrictions;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.LinkedBlockingDeque;
/**
* <p>
* MagicStack class.
@@ -518,6 +531,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
setResolving(false);
unfreezeStack();
sa.resetOnceResolved();
game.updateStackForView();
//game.getAction().checkStaticAbilities();
game.getPhaseHandler().onStackResolved();

View File

@@ -5,6 +5,8 @@ import java.util.Collection;
import forge.util.FCollection;
public class TrackableCollection<T extends TrackableObject> extends FCollection<T> {
private static final long serialVersionUID = 1528674215758232314L;
public TrackableCollection() {
}
public TrackableCollection(T e) {

View File

@@ -1,5 +1,6 @@
package forge.trackable;
import java.io.Serializable;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
@@ -8,7 +9,9 @@ import java.util.Set;
import forge.game.IIdentifiable;
//base class for objects that can be tracked and synced between game server and GUI
public abstract class TrackableObject implements IIdentifiable {
public abstract class TrackableObject implements IIdentifiable, Serializable {
private static final long serialVersionUID = 7386836745378571056L;
private final int id;
protected final transient Tracker tracker;
private final Map<TrackableProperty, Object> props;
@@ -60,6 +63,30 @@ public abstract class TrackableObject implements IIdentifiable {
}
}
/**
* Copy to this Trackable object the first object of the provided iterator
* whose id matches.
*
* @see TrackableObject#copy(TrackableObject)
*/
public final void copy(final Iterable<? extends TrackableObject> others) {
for (final TrackableObject other : others) {
if (this.equals(other)) {
copy(other);
break;
}
}
}
/**
* Copy all properties of another Trackable object to this object.
*/
public final void copy(final TrackableObject other) {
for (final TrackableProperty prop : other.props.keySet()) {
set(prop, other.get(prop));
}
}
//use when updating collection type properties with using set
protected final void flagAsChanged(final TrackableProperty key) {
changedProps.add(key);

View File

@@ -142,6 +142,7 @@ public enum TrackableProperty {
MatchOver(TrackableTypes.BooleanType),
NumGamesInMatch(TrackableTypes.IntegerType),
NumPlayedGamesInMatch(TrackableTypes.IntegerType),
Stack(TrackableTypes.StackItemViewListType),
StormCount(TrackableTypes.IntegerType),
GameOver(TrackableTypes.BooleanType),
PoisonCountersToLose(TrackableTypes.IntegerType),

View File

@@ -13,8 +13,8 @@ import forge.card.ColorSet;
import forge.card.mana.ManaCost;
import forge.game.GameEntityView;
import forge.game.card.CardView;
import forge.game.card.CounterType;
import forge.game.card.CardView.CardStateView;
import forge.game.card.CounterType;
import forge.game.keyword.KeywordCollection.KeywordCollectionView;
import forge.game.player.PlayerView;
import forge.game.spellability.StackItemView;
@@ -284,6 +284,22 @@ public class TrackableTypes {
}
}
};
public static final TrackableType<TrackableCollection<StackItemView>> StackItemViewListType = new TrackableType<TrackableCollection<StackItemView>>() {
@Override
protected TrackableCollection<StackItemView> getDefaultValue() {
return new TrackableCollection<StackItemView>();
}
@Override
protected TrackableCollection<StackItemView> deserialize(TrackableDeserializer td, TrackableCollection<StackItemView> oldValue) {
return td.readCollection(oldValue);
}
@Override
protected void serialize(TrackableSerializer ts, TrackableCollection<StackItemView> value) {
ts.write(value);
}
};
public static final TrackableType<ManaCost> ManaCostType = new TrackableType<ManaCost>() {
@Override
public ManaCost getDefaultValue() {

View File

@@ -1,5 +1,6 @@
package forge.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -12,7 +13,9 @@ import java.util.ListIterator;
import java.util.Set;
//base class for a collection with quick lookup and that maintains order
public class FCollection<T> implements List<T>, Set<T>, FCollectionView<T>, Cloneable {
public class FCollection<T> implements List<T>, Set<T>, FCollectionView<T>, Cloneable, Serializable {
private static final long serialVersionUID = -1664555336364294106L;
private final HashSet<T> set = new HashSet<T>();
private final LinkedList<T> list = new LinkedList<T>();

View File

@@ -4,6 +4,7 @@ import org.apache.commons.lang3.StringUtils;
import forge.game.GameObject;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
public class MessageUtil {
@@ -17,6 +18,14 @@ public class MessageUtil {
return message;
}
public static String formatMessage(String message, PlayerView player, Object related) {
if (related instanceof Player && message.indexOf("{player") >= 0) {
String noun = mayBeYou(player, related);
message = message.replace("{player}", noun).replace("{player's}", Lang.getPossesive(noun));
}
return message;
}
// These are not much related to PlayerController
public static String formatNotificationMessage(SpellAbility sa, Player player, GameObject target, String value) {
if (sa == null || sa.getApi() == null || sa.getHostCard() == null) {
@@ -50,4 +59,7 @@ public class MessageUtil {
public static String mayBeYou(Player player, Object what) {
return what == null ? "(null)" : what == player ? "you" : what.toString();
}
public static String mayBeYou(PlayerView player, Object what) {
return what == null ? "(null)" : what == player ? "you" : what.toString();
}
}

View File

@@ -29,7 +29,6 @@ import forge.download.GuiDownloader;
import forge.error.BugReportDialog;
import forge.gui.BoxedProductCardListViewer;
import forge.gui.CardListViewer;
import forge.gui.FNetOverlay;
import forge.gui.GuiChoose;
import forge.gui.framework.FScreen;
import forge.interfaces.IGuiBase;
@@ -280,9 +279,4 @@ public class GuiDesktop implements IGuiBase {
return match;
}
@Override
public void netMessage(final String origin, final String message) {
FNetOverlay.SINGLETON_INSTANCE.showUp("");
FNetOverlay.SINGLETON_INSTANCE.addMessage(origin, message);
}
}

View File

@@ -135,9 +135,14 @@ public enum FNetOverlay {
getPanel().validate();
}
SimpleDateFormat inFormat = new SimpleDateFormat("HH:mm:ss");
public void addMessage(String origin, String message) {
String toAdd = String.format("%n[%s] %s: %s", inFormat.format(new Date()), origin, message);
private final static SimpleDateFormat inFormat = new SimpleDateFormat("HH:mm:ss");
public void addMessage(final String origin, final String message) {
final String toAdd;
if (origin == null) {
toAdd = String.format("%n[%s] %s: %s", inFormat.format(new Date()), origin, message);
} else {
toAdd = String.format("%n[%s] %s", inFormat.format(new Date()), message);
}
txtLog.append(toAdd);
}
}

View File

@@ -2,39 +2,17 @@ package forge.screens.home;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import javax.swing.SwingUtilities;
import com.beust.jcommander.internal.Maps;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckFormat;
import forge.deck.DeckSection;
import forge.deck.DeckType;
import forge.deck.DeckgenUtil;
import forge.game.GameType;
import forge.game.player.RegisteredPlayer;
import forge.gui.GuiDialog;
import forge.interfaces.IGuiGame;
import forge.item.PaperCard;
import forge.match.HostedMatch;
import forge.model.CardCollections;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.toolbox.FList;
import forge.toolbox.FOptionPane;
import forge.util.Aggregates;
import forge.util.storage.IStorage;
public class CLobby {
@@ -118,7 +96,6 @@ public class CLobby {
}
// General updates when switching back to this view
view.updatePlayersFromPrefs();
view.getBtnStart().requestFocusInWindow();
}
});
@@ -134,14 +111,6 @@ public class CLobby {
view.getDeckChooser(6).initialize(FPref.CONSTRUCTED_P7_DECK_STATE, DeckType.COLOR_DECK);
view.getDeckChooser(7).initialize(FPref.CONSTRUCTED_P8_DECK_STATE, DeckType.COLOR_DECK);
// Start button event handling
view.getBtnStart().addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent arg0) {
startGame(view.getAppliedVariants());
}
});
final ForgePreferences prefs = FModel.getPreferences();
// Checkbox event handling
view.getCbSingletons().addActionListener(new ActionListener() {
@@ -165,208 +134,4 @@ public class CLobby {
view.getCbArtifacts().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_ARTIFACTS));
}
/** Starts a match with the applied variants. */
private void startGame(final Set<GameType> variantTypes) {
if (!view.isEnoughTeams()) {
FOptionPane.showMessageDialog("There are not enough teams! Please adjust team allocations.");
return;
}
for (final int i : view.getParticipants()) {
if (view.getDeckChooser(i).getPlayer() == null) {
FOptionPane.showMessageDialog("Please specify a deck for " + view.getPlayerName(i));
return;
}
} // Is it even possible anymore? I think current implementation assigns decks automatically.
GameType autoGenerateVariant = null;
boolean isCommanderMatch = false;
boolean isTinyLeadersMatch = false;
if (!variantTypes.isEmpty()) {
isTinyLeadersMatch = variantTypes.contains(GameType.TinyLeaders);
isCommanderMatch = isTinyLeadersMatch || variantTypes.contains(GameType.Commander);
if (!isCommanderMatch) {
for (GameType variant : variantTypes) {
if (variant.isAutoGenerated()) {
autoGenerateVariant = variant;
break;
}
}
}
}
boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY);
//Auto-generated decks don't need to be checked here
//Commander deck replaces regular deck and is checked later
if (checkLegality && autoGenerateVariant == null && !isCommanderMatch) {
for (final int i : view.getParticipants()) {
String name = view.getPlayerName(i);
String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(view.getDeckChooser(i).getPlayer().getDeck());
if (null != errMsg) {
FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Deck");
return;
}
}
}
final List<RegisteredPlayer> players = new ArrayList<RegisteredPlayer>();
final Map<RegisteredPlayer, IGuiGame> guis = Maps.newHashMap();
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
for (final int i : view.getParticipants()) {
final String name = view.getPlayerName(i);
final boolean isAI = view.isPlayerAI(i);
final LobbyPlayer lobbyPlayer = isAI
? GamePlayerUtil.createAiPlayer(name, view.getPlayerAvatar(i), view.getAiOptions(i))
: GamePlayerUtil.getGuiPlayer(name, i);
RegisteredPlayer rp = view.getDeckChooser(i).getPlayer();
if (variantTypes.isEmpty()) {
rp.setTeamNumber(view.getTeam(i));
players.add(rp.setPlayer(lobbyPlayer));
} else {
Deck deck = null;
PaperCard vanguardAvatar = null;
if (isCommanderMatch) {
final Object selected = view.getCommanderDeckLists().get(i).getSelectedValue();
if (selected instanceof String) {
final String sel = (String) selected;
final IStorage<Deck> comDecks = FModel.getDecks().getCommander();
if (sel.equals("Random") && comDecks.size() > 0) {
deck = Aggregates.random(comDecks);
}
} else {
deck = (Deck) selected;
}
GameType commanderGameType = isTinyLeadersMatch ? GameType.TinyLeaders : GameType.Commander;
if (deck == null) { //Can be null if player deselects the list selection or chose Generate
deck = DeckgenUtil.generateCommanderDeck(isAI, commanderGameType);
}
if (checkLegality) {
String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck);
if (null != errMsg) {
FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid " + commanderGameType + " Deck");
return;
}
}
} else if (autoGenerateVariant != null) {
deck = autoGenerateVariant.autoGenerateDeck(rp);
CardPool avatarPool = deck.get(DeckSection.Avatar);
if (avatarPool != null) {
vanguardAvatar = avatarPool.get(0);
}
}
// Initialise variables for other variants
deck = deck == null ? rp.getDeck() : deck;
Iterable<PaperCard> schemes = null;
final boolean playerIsArchenemy = view.isPlayerArchenemy(i);
Iterable<PaperCard> planes = null;
//Archenemy
if (variantTypes.contains(GameType.ArchenemyRumble)
|| (variantTypes.contains(GameType.Archenemy) && playerIsArchenemy)) {
Object selected = view.getSchemeDeckLists().get(i).getSelectedValue();
CardPool schemePool = null;
if (selected instanceof String) {
String sel = (String) selected;
if (sel.contains("Use deck's scheme section")) {
if (deck.has(DeckSection.Schemes)) {
schemePool = deck.get(DeckSection.Schemes);
} else {
sel = "Random";
}
}
IStorage<Deck> sDecks = FModel.getDecks().getScheme();
if (sel.equals("Random") && sDecks.size() != 0) {
schemePool = Aggregates.random(sDecks).get(DeckSection.Schemes);
}
} else {
schemePool = ((Deck) selected).get(DeckSection.Schemes);
}
if (schemePool == null) { //Can be null if player deselects the list selection or chose Generate
schemePool = DeckgenUtil.generateSchemePool();
}
if (checkLegality) {
String errMsg = DeckFormat.getSchemeSectionConformanceProblem(schemePool);
if (null != errMsg) {
FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Scheme Deck");
return;
}
}
schemes = schemePool.toFlatList();
}
//Planechase
if (variantTypes.contains(GameType.Planechase)) {
Object selected = view.getPlanarDeckLists().get(i).getSelectedValue();
CardPool planePool = null;
if (selected instanceof String) {
String sel = (String) selected;
if (sel.contains("Use deck's planes section")) {
if (deck.has(DeckSection.Planes)) {
planePool = deck.get(DeckSection.Planes);
} else {
sel = "Random";
}
}
IStorage<Deck> pDecks = FModel.getDecks().getPlane();
if (sel.equals("Random") && pDecks.size() != 0) {
planePool = Aggregates.random(pDecks).get(DeckSection.Planes);
}
} else {
planePool = ((Deck) selected).get(DeckSection.Planes);
}
if (planePool == null) { //Can be null if player deselects the list selection or chose Generate
planePool = DeckgenUtil.generatePlanarPool();
}
if (checkLegality) {
String errMsg = DeckFormat.getPlaneSectionConformanceProblem(planePool);
if (null != errMsg) {
FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Planar Deck");
return;
}
}
planes = planePool.toFlatList();
}
//Vanguard
if (variantTypes.contains(GameType.Vanguard)) {
Object selected = view.getVanguardLists().get(i).getSelectedValue();
if (selected instanceof String) {
String sel = (String) selected;
if (sel.contains("Use deck's default avatar") && deck.has(DeckSection.Avatar)) {
vanguardAvatar = deck.get(DeckSection.Avatar).get(0);
} else { //Only other string is "Random"
if (isAI) { //AI
vanguardAvatar = Aggregates.random(view.getNonRandomAiAvatars());
} else { //Human
vanguardAvatar = Aggregates.random(view.getNonRandomHumanAvatars());
}
}
} else {
vanguardAvatar = (PaperCard)selected;
}
if (vanguardAvatar == null) { //ERROR! null if avatar deselected on list
GuiDialog.message("No Vanguard avatar selected for " + name
+ ". Please choose one or disable the Vanguard variant");
return;
}
}
rp = RegisteredPlayer.forVariants(variantTypes, deck, schemes, playerIsArchenemy, planes, vanguardAvatar);
rp.setTeamNumber(view.getTeam(i));
players.add(rp.setPlayer(lobbyPlayer));
}
if (!isAI) {
guis.put(rp, gui);
}
view.getDeckChooser(i).saveState();
}
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.startMatch(GameType.Constructed, variantTypes, players, guis);
}
}

View File

@@ -6,7 +6,9 @@ import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
@@ -17,7 +19,9 @@ import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import forge.AIOption;
import forge.Singletons;
import forge.UiCommand;
import forge.assets.FSkinProp;
@@ -25,6 +29,7 @@ import forge.deck.DeckSection;
import forge.game.GameType;
import forge.gui.framework.FScreen;
import forge.item.PaperCard;
import forge.match.LobbySlot;
import forge.model.FModel;
import forge.net.game.LobbySlotType;
import forge.properties.ForgePreferences;
@@ -32,7 +37,6 @@ import forge.properties.ForgePreferences.FPref;
import forge.screens.deckeditor.CDeckEditorUI;
import forge.screens.deckeditor.controllers.CEditorCommander;
import forge.screens.deckeditor.controllers.CEditorVariant;
import forge.screens.home.VLobby.LobbyType;
import forge.screens.home.sanctioned.AvatarSelector;
import forge.toolbox.FComboBox;
import forge.toolbox.FComboBoxWrapper;
@@ -53,17 +57,16 @@ public class PlayerPanel extends FPanel {
private final static ForgePreferences prefs = FModel.getPreferences();
private static final SkinColor unfocusedPlayerOverlay = FSkin.getColor(FSkin.Colors.CLR_OVERLAY).alphaColor(120);
private LobbySlotType type;
private final int index;
private final LobbyType lobbyType;
private LobbySlotType type = LobbySlotType.LOCAL;
private boolean editableForClient;
private String playerName = StringUtils.EMPTY;
private boolean mayEdit, mayControl, mayRemove;
private final FLabel nameRandomiser;
private final FLabel avatarLabel = new FLabel.Builder().opaque(true).hoverable(true).iconScaleFactor(0.99f).iconInBackground(true).build();
private int avatarIndex;
private final FTextField txtPlayerName = new FTextField.Builder().text("Player name").build();
private final FTextField txtPlayerName = new FTextField.Builder().build();
private FRadioButton radioHuman;
private FRadioButton radioAi;
private JCheckBoxMenuItem radioAiUseSimulation;
@@ -78,7 +81,6 @@ public class PlayerPanel extends FPanel {
private final String variantBtnConstraints = "height 30px, hidemode 3";
private boolean playerIsArchenemy = false;
private final FLabel scmDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a scheme deck").build();
private final FLabel scmDeckEditor = new FLabel.ButtonBuilder().text("Scheme Deck Editor").build();
private final FLabel scmLabel;
@@ -95,26 +97,25 @@ public class PlayerPanel extends FPanel {
private final FLabel vgdLabel;
private final VLobby lobby;
public PlayerPanel(final VLobby lobby, final int index, final LobbyType lobbyType) {
public PlayerPanel(final VLobby lobby, final boolean allowNetworking, final int index, final LobbySlot slot, final boolean mayEdit, final boolean mayControl) {
super();
this.lobby = lobby;
this.index = index;
this.mayEdit = mayEdit;
this.mayControl = mayControl;
this.deckLabel = lobby.newLabel("Deck:");
this.scmLabel = lobby.newLabel("Scheme deck:");
this.cmdLabel = lobby.newLabel("Commander deck:");
this.pchLabel = lobby.newLabel("Planar deck:");
this.vgdLabel = lobby.newLabel("Vanguard:");
this.index = index;
this.lobbyType = lobbyType;
this.playerIsArchenemy = index == 0;
setLayout(new MigLayout("insets 10px, gap 5px"));
// Add a button to players 3+ (or if server) to remove them from the setup
closeBtn = createCloseButton();
if (index >= 2 || lobbyType == LobbyType.SERVER) {
this.add(closeBtn, "w 20, h 20, pos (container.w-20) 0");
}
createAvatar();
this.add(avatarLabel, "spany 2, width 80px, height 80px");
@@ -132,12 +133,18 @@ public class PlayerPanel extends FPanel {
this.add(lobby.newLabel("Team:"), "w 40px, h 30px");
populateTeamsComboBoxes();
// Set these before action listeners are added
this.setTeam(slot == null ? index : slot.getTeam());
this.setIsArchenemy(slot != null && slot.isArchenemy());
teamComboBox.addActionListener(teamListener);
aeTeamComboBox.addActionListener(teamListener);
teamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px");
aeTeamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px");
if (lobbyType == LobbyType.SERVER) {
this.add(radioOpen, "gapleft 1px");
if (allowNetworking) {
this.add(radioOpen, "cell 4 1, ax left, sx 2, wrap");
}
this.add(deckLabel, variantBtnConstraints + ", cell 0 2, sx 2, ax right");
@@ -161,7 +168,6 @@ public class PlayerPanel extends FPanel {
this.add(vgdSelectorBtn, variantBtnConstraints + ", cell 2 6, sx 4, growx, wrap");
addHandlersToVariantsControls();
updateVariantControlsVisibility();
this.addMouseListener(new FMouseAdapter() {
@Override public final void onLeftMouseDown(final MouseEvent e) {
@@ -169,46 +175,62 @@ public class PlayerPanel extends FPanel {
}
});
this.type = slot == null ? LobbySlotType.LOCAL : slot.getType();
this.setPlayerName(slot == null ? "" : slot.getName());
this.setAvatar(slot == null ? 0 : slot.getAvatarIndex());
update();
}
void update() {
if (type != LobbySlotType.REMOTE) {
if (radioHuman.isSelected()) {
type = LobbySlotType.LOCAL;
} else if (radioAi.isSelected()) {
type = LobbySlotType.AI;
} else if (radioOpen.isSelected()) {
type = LobbySlotType.OPEN;
}
avatarLabel.setEnabled(mayEdit);
avatarLabel.setIcon(FSkin.getAvatars().get(Integer.valueOf(type == LobbySlotType.OPEN ? -1 : avatarIndex)));
avatarLabel.repaintSelf();
txtPlayerName.setEnabled(mayEdit);
txtPlayerName.setText(type == LobbySlotType.OPEN ? StringUtils.EMPTY : playerName);
nameRandomiser.setEnabled(mayEdit);
deckLabel.setVisible(mayEdit);
deckBtn.setVisible(mayEdit);
closeBtn.setVisible(mayRemove);
if (mayRemove) {
radioHuman.setEnabled(mayControl);
radioAi.setEnabled(mayControl);
radioOpen.setEnabled(mayControl);
} else {
radioHuman.setVisible(mayControl);
radioAi.setVisible(mayControl);
radioOpen.setVisible(mayControl);
}
final boolean isEditable = lobbyType == LobbyType.LOCAL || type == LobbySlotType.LOCAL ||
(lobbyType == LobbyType.SERVER && (type == LobbySlotType.LOCAL || type == LobbySlotType.AI)) ||
(lobbyType == LobbyType.CLIENT && editableForClient);
avatarLabel.setEnabled(isEditable);
txtPlayerName.setEnabled(isEditable);
nameRandomiser.setEnabled(isEditable);
deckLabel.setVisible(isEditable);
deckBtn.setVisible(isEditable);
radioHuman.setSelected(type == LobbySlotType.LOCAL);
radioAi.setSelected(type == LobbySlotType.AI);
radioOpen.setSelected(type == LobbySlotType.OPEN);
final boolean hasSlotControls = lobbyType == LobbyType.LOCAL || (lobbyType == LobbyType.SERVER && type != LobbySlotType.REMOTE);
closeBtn.setVisible(hasSlotControls);
radioAi.setVisible(hasSlotControls);
radioHuman.setVisible(hasSlotControls);
radioOpen.setVisible(hasSlotControls && lobbyType == LobbyType.SERVER);
updateVariantControlsVisibility();
}
private final FMouseAdapter radioMouseAdapter = new FMouseAdapter() {
private final FMouseAdapter radioMouseAdapter(final FRadioButton source, final LobbySlotType type) {
return new FMouseAdapter() {
@Override public final void onLeftClick(final MouseEvent e) {
if (!source.isEnabled()) {
return;
}
setType(type);
lobby.firePlayerChangeListener(index);
avatarLabel.requestFocusInWindow();
lobby.updateVanguardList(index);
update();
lobby.update();
}
};
};
/** Listens to name text fields and gives the appropriate player focus.
* Also saves the name preference when leaving player one's text field. */
/**
* Listens to name text fields and gives the appropriate player focus. Also
* saves the name preference when leaving player one's text field.
*/
private FocusAdapter nameFocusListener = new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
@@ -226,7 +248,8 @@ public class PlayerPanel extends FPanel {
prefs.setPref(FPref.PLAYER_NAME, newName);
prefs.save();
}
lobby.firePlayerChangeListener();
lobby.firePlayerChangeListener(index);
lobby.update();
}
}
};
@@ -247,10 +270,10 @@ public class PlayerPanel extends FPanel {
final FLabel avatar = (FLabel)e.getSource();
PlayerPanel.this.lobby.changePlayerFocus(index);
lobby.changePlayerFocus(index);
avatar.requestFocusInWindow();
final AvatarSelector aSel = new AvatarSelector(getPlayerName(), avatarIndex, PlayerPanel.this.lobby.getUsedAvatars());
final AvatarSelector aSel = new AvatarSelector(playerName, avatarIndex, lobby.getUsedAvatars());
for (final FLabel lbl : aSel.getSelectables()) {
lbl.setCommand(new UiCommand() {
@Override
@@ -265,64 +288,35 @@ public class PlayerPanel extends FPanel {
aSel.dispose();
if (index < 2) {
PlayerPanel.this.lobby.updateAvatarPrefs();
lobby.updateAvatarPrefs();
}
lobby.firePlayerChangeListener();
lobby.firePlayerChangeListener(index);
}
@Override public final void onRightClick(final MouseEvent e) {
if (!avatarLabel.isEnabled()) {
return;
}
PlayerPanel.this.lobby.changePlayerFocus(index);
lobby.changePlayerFocus(index);
avatarLabel.requestFocusInWindow();
setRandomAvatar();
if (index < 2) {
PlayerPanel.this.lobby.updateAvatarPrefs();
lobby.updateAvatarPrefs();
}
}
};
public void updateVariantControlsVisibility() {
boolean isCommanderApplied = false;
boolean isPlanechaseApplied = false;
boolean isVanguardApplied = false;
boolean isArchenemyApplied = false;
boolean archenemyVisiblity = false;
boolean isDeckBuildingAllowed = true;
for (final GameType variant : lobby.getAppliedVariants()) {
switch (variant) {
case Archenemy:
isArchenemyApplied = true;
if (playerIsArchenemy) {
archenemyVisiblity = true;
}
break;
case ArchenemyRumble:
archenemyVisiblity = true;
break;
case Commander:
case TinyLeaders:
isCommanderApplied = true;
isDeckBuildingAllowed = false; //Commander deck replaces basic deck, so hide that
break;
case Planechase:
isPlanechaseApplied = true;
break;
case Vanguard:
isVanguardApplied = true;
break;
default:
if (variant.isAutoGenerated()) {
isDeckBuildingAllowed = false;
}
break;
}
}
private void updateVariantControlsVisibility() {
boolean isCommanderApplied = mayEdit && (lobby.hasVariant(GameType.Commander) || lobby.hasVariant(GameType.TinyLeaders));
boolean isPlanechaseApplied = mayEdit && lobby.hasVariant(GameType.Planechase);
boolean isVanguardApplied = mayEdit && lobby.hasVariant(GameType.Vanguard);
boolean isArchenemyApplied = mayEdit && lobby.hasVariant(GameType.Archenemy);
boolean archenemyVisiblity = mayEdit && lobby.hasVariant(GameType.ArchenemyRumble) || (isArchenemyApplied && isArchenemy());
// Commander deck building replaces normal one, so hide it
boolean isDeckBuildingAllowed = mayEdit && !isCommanderApplied && !lobby.hasVariant(GameType.MomirBasic);
deckLabel.setVisible(isDeckBuildingAllowed);
deckBtn.setVisible(isDeckBuildingAllowed);
@@ -336,7 +330,6 @@ public class PlayerPanel extends FPanel {
teamComboBox.setVisible(!isArchenemyApplied);
aeTeamComboBox.setVisible(isArchenemyApplied);
aeTeamComboBox.setEnabled(!(isArchenemyApplied && playerIsArchenemy));
pchDeckSelectorBtn.setVisible(isPlanechaseApplied);
pchDeckEditor.setVisible(isPlanechaseApplied);
@@ -367,30 +360,53 @@ public class PlayerPanel extends FPanel {
return type == LobbySlotType.AI;
}
public boolean isSimulatedAi() {
public Set<AIOption> getAiOptions() {
return isSimulatedAi()
? ImmutableSet.of(AIOption.USE_SIMULATION)
: Collections.<AIOption>emptySet();
}
private boolean isSimulatedAi() {
return radioAi.isSelected() && radioAiUseSimulation.isSelected();
}
public void setUseAiSimulation(final boolean useSimulation) {
radioAi.setSelected(useSimulation);
}
public boolean isLocal() {
return type == LobbySlotType.LOCAL;
}
public boolean isArchenemy() {
return playerIsArchenemy;
return aeTeamComboBox.getSelectedIndex() == 0;
}
public void setRemote(final boolean remote) {
if (remote) {
type = LobbySlotType.REMOTE;
} else {
public void setType(final LobbySlotType type) {
this.type = type;
switch (type) {
case LOCAL:
radioHuman.setSelected(true);
break;
case AI:
radioAi.setSelected(true);
break;
case OPEN:
radioOpen.setSelected(true);
type = LobbySlotType.OPEN;
break;
case REMOTE:
break;
}
update();
}
public void setEditableForClient(final boolean editable) {
editableForClient = editable;
public void setRemote(final boolean remote) {
if (remote) {
setType(LobbySlotType.REMOTE);
radioHuman.setSelected(false);
radioAi.setSelected(false);
radioOpen.setSelected(false);
} else {
setType(LobbySlotType.OPEN);
}
}
public void setVanguardButtonText(String text) {
@@ -408,77 +424,46 @@ public class PlayerPanel extends FPanel {
private void populateTeamsComboBoxes() {
aeTeamComboBox.addItem("Archenemy");
aeTeamComboBox.addItem("Heroes");
aeTeamComboBox.setSelectedIndex(lobby.getArchenemyTeams().get(index) - 1);
aeTeamComboBox.setEnabled(playerIsArchenemy);
for (int i = 1; i <= VLobby.MAX_PLAYERS; i++) {
teamComboBox.addItem(i);
}
teamComboBox.setSelectedIndex(lobby.getTeams().get(index) - 1);
teamComboBox.setEnabled(true);
}
private ActionListener teamListener = new ActionListener() {
@SuppressWarnings("unchecked")
@Override
public void actionPerformed(ActionEvent e) {
FComboBox<Object> cb = (FComboBox<Object>)e.getSource();
@Override public final void actionPerformed(final ActionEvent e) {
final FComboBox<Object> cb = (FComboBox<Object>) e.getSource();
cb.requestFocusInWindow();
Object selection = cb.getSelectedItem();
final Object selection = cb.getSelectedItem();
if (null == selection) {
return;
if (null != selection) {
lobby.changePlayerFocus(index);
lobby.firePlayerChangeListener(index);
}
if (PlayerPanel.this.lobby.getAppliedVariants().contains(GameType.Archenemy)) {
String sel = (String) selection;
if (sel.contains("Archenemy")) {
PlayerPanel.this.lobby.setLastArchenemy(index);
for (PlayerPanel pp : PlayerPanel.this.lobby.getPlayerPanels()) {
int i = pp.index;
PlayerPanel.this.lobby.getArchenemyTeams().set(i, i == PlayerPanel.this.lobby.getLastArchenemy() ? 1 : 2);
pp.aeTeamComboBox.setSelectedIndex(i == PlayerPanel.this.lobby.getLastArchenemy() ? 0 : 1);
pp.toggleIsPlayerArchenemy();
}
}
} else {
Integer sel = (Integer) selection;
PlayerPanel.this.lobby.getTeams().set(index, sel);
}
PlayerPanel.this.lobby.changePlayerFocus(index);
}
};
public void toggleIsPlayerArchenemy() {
if (lobby.getAppliedVariants().contains(GameType.Archenemy)) {
playerIsArchenemy = lobby.getLastArchenemy() == index;
} else {
playerIsArchenemy = lobby.getAppliedVariants().contains(GameType.ArchenemyRumble);
}
updateVariantControlsVisibility();
}
/**
* @param index
*/
private void addHandlersToVariantsControls() {
// Archenemy buttons
scmDeckSelectorBtn.setCommand(new Runnable() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntArchenemy().isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble);
@Override public final void run() {
lobby.setCurrentGameMode(lobby.getVntArchenemy().isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble);
scmDeckSelectorBtn.requestFocusInWindow();
PlayerPanel.this.lobby.changePlayerFocus(index, PlayerPanel.this.lobby.getCurrentGameMode());
lobby.changePlayerFocus(index);
}
});
scmDeckEditor.setCommand(new UiCommand() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntArchenemy().isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble);
lobby.setCurrentGameMode(lobby.getVntArchenemy().isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble);
Predicate<PaperCard> predSchemes = new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard arg0) {
@Override public final boolean apply(final PaperCard arg0) {
return arg0.getRules().getType().isScheme();
}
};
@@ -493,16 +478,16 @@ public class PlayerPanel extends FPanel {
cmdDeckSelectorBtn.setCommand(new Runnable() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntTinyLeaders().isSelected() ? GameType.TinyLeaders : GameType.Commander);
lobby.setCurrentGameMode(lobby.getVntTinyLeaders().isSelected() ? GameType.TinyLeaders : GameType.Commander);
cmdDeckSelectorBtn.requestFocusInWindow();
PlayerPanel.this.lobby.changePlayerFocus(index, PlayerPanel.this.lobby.getCurrentGameMode());
lobby.changePlayerFocus(index);
}
});
cmdDeckEditor.setCommand(new UiCommand() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntTinyLeaders().isSelected() ? GameType.TinyLeaders : GameType.Commander);
lobby.setCurrentGameMode(lobby.getVntTinyLeaders().isSelected() ? GameType.TinyLeaders : GameType.Commander);
Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_COMMANDER);
CDeckEditorUI.SINGLETON_INSTANCE.setEditorController(new CEditorCommander(CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture()));
}
@@ -512,16 +497,16 @@ public class PlayerPanel extends FPanel {
pchDeckSelectorBtn.setCommand(new Runnable() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(GameType.Planechase);
lobby.setCurrentGameMode(GameType.Planechase);
pchDeckSelectorBtn.requestFocusInWindow();
PlayerPanel.this.lobby.changePlayerFocus(index, GameType.Planechase);
lobby.changePlayerFocus(index, GameType.Planechase);
}
});
pchDeckEditor.setCommand(new UiCommand() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(GameType.Planechase);
lobby.setCurrentGameMode(GameType.Planechase);
Predicate<PaperCard> predPlanes = new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard arg0) {
@@ -539,9 +524,9 @@ public class PlayerPanel extends FPanel {
vgdSelectorBtn.setCommand(new Runnable() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(GameType.Vanguard);
lobby.setCurrentGameMode(GameType.Vanguard);
vgdSelectorBtn.requestFocusInWindow();
PlayerPanel.this.lobby.changePlayerFocus(index, GameType.Vanguard);
lobby.changePlayerFocus(index, GameType.Vanguard);
}
});
}
@@ -550,18 +535,22 @@ public class PlayerPanel extends FPanel {
* @param index
*/
private void createPlayerTypeOptions() {
final boolean isServer = lobbyType == LobbyType.SERVER;
radioHuman = new FRadioButton(isServer ? "Local" : "Human", index == 0);
radioAi = new FRadioButton("AI", !isServer && index != 0);
radioOpen = new FRadioButton("Open", isServer && index != 0);
radioHuman = new FRadioButton("Human");
radioAi = new FRadioButton("AI");
radioOpen = new FRadioButton("Open");
final JPopupMenu menu = new JPopupMenu();
radioAiUseSimulation = new JCheckBoxMenuItem("Use Simulation");
menu.add(radioAiUseSimulation);
radioAiUseSimulation.addActionListener(new ActionListener() {
@Override public final void actionPerformed(final ActionEvent e) {
lobby.firePlayerChangeListener(index);
} });
radioAi.setComponentPopupMenu(menu);
radioHuman.addMouseListener(radioMouseAdapter);
radioAi.addMouseListener(radioMouseAdapter);
radioOpen.addMouseListener(radioMouseAdapter);
radioHuman.addMouseListener(radioMouseAdapter(radioHuman, LobbySlotType.LOCAL));
radioAi.addMouseListener (radioMouseAdapter(radioAi, LobbySlotType.AI));
radioOpen.addMouseListener (radioMouseAdapter(radioOpen, LobbySlotType.OPEN));
final ButtonGroup tempBtnGroup = new ButtonGroup();
tempBtnGroup.add(radioHuman);
@@ -576,9 +565,9 @@ public class PlayerPanel extends FPanel {
deckBtn.setCommand(new Runnable() {
@Override
public void run() {
PlayerPanel.this.lobby.setCurrentGameMode(GameType.Constructed);
lobby.setCurrentGameMode(GameType.Constructed);
deckBtn.requestFocusInWindow();
PlayerPanel.this.lobby.changePlayerFocus(index, GameType.Constructed);
lobby.changePlayerFocus(index, GameType.Constructed);
}
});
}
@@ -594,7 +583,7 @@ public class PlayerPanel extends FPanel {
newNameBtn.setCommand(new UiCommand() {
@Override
public void run() {
String newName = PlayerPanel.this.lobby.getNewName();
String newName = lobby.getNewName();
if (null == newName) {
return;
}
@@ -605,7 +594,7 @@ public class PlayerPanel extends FPanel {
prefs.save();
}
txtPlayerName.requestFocus();
PlayerPanel.this.lobby.changePlayerFocus(index);
lobby.changePlayerFocus(index);
}
});
newNameBtn.addFocusListener(nameFocusListener);
@@ -640,10 +629,10 @@ public class PlayerPanel extends FPanel {
.icon(FSkin.getIcon(FSkinProp.ICO_CLOSE)).hoverable(true).build();
closeBtn.setCommand(new Runnable() {
@Override public final void run() {
if (type == LobbySlotType.REMOTE && !SOptionPane.showConfirmDialog(String.format("Really kick %s?", getPlayerName()), "Kick", false)) {
if (type == LobbySlotType.REMOTE && !SOptionPane.showConfirmDialog(String.format("Really kick %s?", playerName), "Kick", false)) {
return;
}
PlayerPanel.this.lobby.removePlayer(index);
lobby.removePlayer(index);
}
});
return closeBtn;
@@ -654,9 +643,8 @@ public class PlayerPanel extends FPanel {
if (index < currentPrefs.length) {
avatarIndex = Integer.parseInt(currentPrefs[index]);
avatarLabel.setIcon(FSkin.getAvatars().get(avatarIndex));
}
else {
setRandomAvatar();
} else {
setRandomAvatar(false);
}
avatarLabel.setToolTipText("L-click: Select avatar. R-click: Randomize avatar.");
@@ -664,25 +652,22 @@ public class PlayerPanel extends FPanel {
avatarLabel.addMouseListener(avatarMouseListener);
}
/** Applies a random avatar, avoiding avatars already used.
* @param playerIndex */
public void setRandomAvatar() {
/** Applies a random avatar, avoiding avatars already used. */
private void setRandomAvatar() {
setRandomAvatar(true);
}
private void setRandomAvatar(final boolean fireListeners) {
int random = 0;
List<Integer> usedAvatars = lobby.getUsedAvatars();
final List<Integer> usedAvatars = lobby.getUsedAvatars();
do {
random = MyRandom.getRandom().nextInt(FSkin.getAvatars().size());
} while (usedAvatars.contains(random));
setAvatar(random);
lobby.firePlayerChangeListener();
if (fireListeners) {
lobby.firePlayerChangeListener(index);
}
public void setAvatar(int newAvatarIndex) {
avatarIndex = newAvatarIndex;
SkinImage icon = FSkin.getAvatars().get(newAvatarIndex);
avatarLabel.setIcon(icon);
avatarLabel.repaintSelf();
}
private final FSkin.LineSkinBorder focusedBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS).alphaColor(255), 3);
@@ -690,18 +675,53 @@ public class PlayerPanel extends FPanel {
public void setFocused(boolean focused) {
avatarLabel.setBorder(focused ? focusedBorder : defaultBorder);
avatarLabel.setHoverable(focused);
}
String getPlayerName() {
return txtPlayerName.getText();
}
public void setPlayerName(final String string) {
playerName = string;
txtPlayerName.setText(string);
}
public int getAvatarIndex() {
return avatarIndex;
}
public void setPlayerName(String string) {
txtPlayerName.setText(string);
public void setAvatar(final int newAvatarIndex) {
avatarIndex = newAvatarIndex;
final SkinImage icon = FSkin.getAvatars().get(newAvatarIndex);
avatarLabel.setIcon(icon);
avatarLabel.repaintSelf();
}
public String getPlayerName() {
return txtPlayerName.getText();
public int getTeam() {
return teamComboBox.getSelectedIndex();
}
public void setTeam(final int team) {
teamComboBox.suppressActionListeners();
teamComboBox.setSelectedIndex(team);
teamComboBox.unsuppressActionListeners();
}
public int getArchenemyTeam() {
return aeTeamComboBox.getSelectedIndex();
}
public void setIsArchenemy(final boolean isArchenemy) {
aeTeamComboBox.suppressActionListeners();
aeTeamComboBox.setSelectedIndex(isArchenemy ? 0 : 1);
aeTeamComboBox.unsuppressActionListeners();
}
public void setMayEdit(final boolean mayEdit) {
this.mayEdit = mayEdit;
}
public void setMayControl(final boolean mayControl) {
this.mayControl = mayControl;
}
public void setMayRemove(final boolean mayRemove) {
this.mayRemove = mayRemove;
}
}

View File

@@ -137,7 +137,7 @@ public enum VHomeUI implements IVTopLevelUI {
allSubmenus.add(VSubmenuSealed.SINGLETON_INSTANCE);
//allSubmenus.add(VSubmenuWinston.SINGLETON_INSTANCE);
allSubmenus.add(VSubmenuOnlineLobby.SINGLETON_INSTANCE);
//allSubmenus.add(VSubmenuOnlineLobby.SINGLETON_INSTANCE);
allSubmenus.add(VSubmenuDuels.SINGLETON_INSTANCE);
allSubmenus.add(VSubmenuChallenges.SINGLETON_INSTANCE);

View File

@@ -6,14 +6,9 @@ import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import javax.swing.JButton;
@@ -24,25 +19,31 @@ import javax.swing.event.ListSelectionListener;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import forge.AIOption;
import forge.UiCommand;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckProxy;
import forge.deck.DeckSection;
import forge.deck.DeckType;
import forge.deck.DeckgenUtil;
import forge.deckchooser.DecksComboBoxEvent;
import forge.deckchooser.FDeckChooser;
import forge.deckchooser.IDecksComboBoxListener;
import forge.game.GameType;
import forge.game.card.CardView;
import forge.gui.CardDetailPanel;
import forge.interfaces.ILobby;
import forge.interfaces.IPlayerChangeListener;
import forge.interfaces.IUpdateable;
import forge.item.PaperCard;
import forge.match.GameLobby;
import forge.match.LobbySlot;
import forge.model.FModel;
import forge.net.game.LobbySlotType;
import forge.net.game.LobbyState;
import forge.net.game.LobbyState.LobbyPlayerData;
import forge.net.game.server.RemoteClient;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.toolbox.FCheckBox;
@@ -55,39 +56,34 @@ import forge.toolbox.FScrollPanel;
import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.FTextField;
import forge.util.Aggregates;
import forge.util.Lang;
import forge.util.NameGenerator;
import forge.util.storage.IStorage;
/**
* Lobby view. View of a number of players at the deck selection stage.
*
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
*/
public class VLobby implements ILobby {
public class VLobby implements IUpdateable {
static final int MAX_PLAYERS = 8;
private static final ForgePreferences prefs = FModel.getPreferences();
public enum LobbyType { LOCAL, SERVER, CLIENT; }
// General variables
private final LobbyType type;
private int localPlayer = 0;
private final GameLobby lobby;
private IPlayerChangeListener playerChangeListener = null;
private final LblHeader lblTitle = new LblHeader("Sanctioned Format: Constructed");
private int activePlayersNum = 2;
private int activePlayersNum = 0;
private int playerWithFocus = 0; // index of the player that currently has focus
private PlayerPanel playerPanelWithFocus;
private GameType currentGameMode = GameType.Constructed;
private List<Integer> teams = new ArrayList<Integer>(MAX_PLAYERS);
private List<Integer> archenemyTeams = new ArrayList<Integer>(MAX_PLAYERS);
private final StartButton btnStart = new StartButton();
private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2"));
private final JPanel constructedFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); // Main content frame
// Variants frame and variables
private final Set<GameType> appliedVariants = new TreeSet<GameType>();
private final FPanel variantsPanel = new FPanel(new MigLayout("insets 10, gapx 10"));
private final VariantCheckBox vntVanguard = new VariantCheckBox(GameType.Vanguard);
private final VariantCheckBox vntMomirBasic = new VariantCheckBox(GameType.MomirBasic);
@@ -96,6 +92,8 @@ public class VLobby implements ILobby {
private final VariantCheckBox vntPlanechase = new VariantCheckBox(GameType.Planechase);
private final VariantCheckBox vntArchenemy = new VariantCheckBox(GameType.Archenemy);
private final VariantCheckBox vntArchenemyRumble = new VariantCheckBox(GameType.ArchenemyRumble);
private final ImmutableList<VariantCheckBox> vntBoxes =
ImmutableList.of(vntVanguard, vntMomirBasic, vntCommander, vntTinyLeaders, vntPlanechase, vntArchenemy, vntArchenemyRumble);
// Player frame elements
private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3"));
@@ -109,6 +107,7 @@ public class VLobby implements ILobby {
private final List<FDeckChooser> deckChoosers = new ArrayList<FDeckChooser>(8);
private final FCheckBox cbSingletons = new FCheckBox("Singleton Mode");
private final FCheckBox cbArtifacts = new FCheckBox("Remove Artifacts");
private final Deck[] decks = new Deck[MAX_PLAYERS];
// Variants
private final List<FList<Object>> schemeDeckLists = new ArrayList<FList<Object>>();
@@ -132,8 +131,8 @@ public class VLobby implements ILobby {
private final Vector<Object> aiListData = new Vector<Object>();
// CTR
public VLobby(final LobbyType type) {
this.type = type;
public VLobby(final GameLobby lobby) {
this.lobby = lobby;
lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
@@ -142,55 +141,23 @@ public class VLobby implements ILobby {
variantsPanel.setOpaque(false);
variantsPanel.add(newLabel("Variants:"));
variantsPanel.add(vntVanguard);
variantsPanel.add(vntMomirBasic);
variantsPanel.add(vntCommander);
variantsPanel.add(vntTinyLeaders);
variantsPanel.add(vntPlanechase);
variantsPanel.add(vntArchenemy);
variantsPanel.add(vntArchenemyRumble);
for (final VariantCheckBox vcb : vntBoxes) {
variantsPanel.add(vcb);
}
constructedFrame.add(new FScrollPane(variantsPanel, false, true,
ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED),
"w 100%, h 45px!, gapbottom 10px, spanx 2, wrap");
////////////////////////////////////////////////////////
///////////////////// Player Panel /////////////////////
// Construct individual player panels
String constraints = "pushx, growx, wrap, hidemode 3";
for (int i = 0; i < MAX_PLAYERS; i++) {
teams.add(i + 1);
archenemyTeams.add(i == 0 ? 1 : 2);
final PlayerPanel player = new PlayerPanel(this, i, type);
if (type == LobbyType.CLIENT) {
player.setRemote(true);
}
playerPanels.add(player);
// Populate players panel
player.setVisible(i < activePlayersNum);
playersScroll.add(player, constraints);
if (i == 0) {
constraints += ", gaptop 5px";
}
}
playerPanelWithFocus = playerPanels.get(0);
playerPanelWithFocus.setFocused(true);
playersFrame.setOpaque(false);
playersFrame.add(playersScroll, "w 100%, h 100%-35px");
if (type != LobbyType.CLIENT) {
if (lobby.hasControl()) {
addPlayerBtn.setFocusable(true);
addPlayerBtn.setCommand(new Runnable() {
@Override public final void run() {
addPlayer();
lobby.addSlot();
}
});
playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx");
@@ -209,9 +176,16 @@ public class VLobby implements ILobby {
decksFrame.setOpaque(false);
// Start Button
if (type != LobbyType.CLIENT) {
if (lobby.hasControl()) {
pnlStart.setOpaque(false);
pnlStart.add(btnStart, "align center");
// Start button event handling
btnStart.addActionListener(new ActionListener() {
@Override public final void actionPerformed(final ActionEvent arg0) {
lobby.startGame();
}
});
}
}
@@ -227,143 +201,95 @@ public class VLobby implements ILobby {
}
populateDeckPanel(GameType.Constructed);
populateVanguardLists();
}
private int addPlayerInFreeSlot(final String name) {
if (activePlayersNum >= MAX_PLAYERS) {
return -1;
}
for (final PlayerPanel pp : getPlayerPanels()) {
if (pp.isVisible() && (
pp.getType() == LobbySlotType.OPEN || (pp.isLocal() && type == LobbyType.SERVER))) {
final int index = pp.getIndex();
addPlayer(index);
pp.setPlayerName(name);
System.out.println("Put player " + name + " in slot " + index);
return index;
}
}
return -1;
}
private void addPlayer() {
if (activePlayersNum >= MAX_PLAYERS) {
return;
}
int freeIndex = -1;
for (int i = 0; i < MAX_PLAYERS; i++) {
if (!playerPanels.get(i).isVisible()) {
freeIndex = i;
break;
}
}
addPlayer(freeIndex);
}
private void addPlayer(final int slot) {
playerPanels.get(slot).setVisible(true);
activePlayersNum++;
public void update() {
activePlayersNum = lobby.getNumberOfSlots();
addPlayerBtn.setEnabled(activePlayersNum < MAX_PLAYERS);
playerPanels.get(slot).setVisible(true);
playerPanels.get(slot).focusOnAvatar();
firePlayerChangeListener();
for (final VariantCheckBox vcb : vntBoxes) {
vcb.setSelected(hasVariant(vcb.variant));
vcb.setEnabled(lobby.hasControl());
}
void removePlayer(final int playerIndex) {
if (activePlayersNum <= playerIndex) {
return;
}
activePlayersNum--;
final FPanel player = playerPanels.get(playerIndex);
player.setVisible(false);
addPlayerBtn.setEnabled(true);
//find closest player still in game and give focus
int min = MAX_PLAYERS;
final List<Integer> participants = getParticipants();
if (!participants.isEmpty()) {
int closest = 2;
for (final int participantIndex : getParticipants()) {
final int diff = Math.abs(playerIndex - participantIndex);
if (diff < min) {
min = diff;
closest = participantIndex;
}
}
changePlayerFocus(closest);
playerPanels.get(closest).focusOnAvatar();
}
firePlayerChangeListener();
}
@Override
public int login(final RemoteClient client) {
return addPlayerInFreeSlot(client.getUsername());
}
@Override
public void logout(final RemoteClient client) {
removePlayer(client.getIndex());
}
@Override
public LobbyState getState() {
final LobbyState state = new LobbyState();
for (int i = 0; i < activePlayersNum; i++) {
state.addPlayer(getData(i));
}
return state;
}
public void setState(final LobbyState state) {
setLocalPlayer(state.getLocalPlayer());
final List<LobbyPlayerData> players = state.getPlayers();
final int pSize = players.size();
activePlayersNum = pSize;
for (int i = 0; i < pSize; i++) {
final LobbyPlayerData player = players.get(i);
final PlayerPanel panel = playerPanels.get(i);
if (type == LobbyType.CLIENT) {
panel.setRemote(i != localPlayer);
panel.setEditableForClient(i == localPlayer);
} else {
panel.setRemote(player.getType() == LobbySlotType.REMOTE);
panel.setEditableForClient(false);
}
panel.setPlayerName(player.getName());
panel.setAvatar(player.getAvatarIndex());
final boolean allowNetworking = lobby.isAllowNetworking();
for (int i = 0; i < MAX_PLAYERS; i++) {
final boolean hasPanel = i < playerPanels.size();
if (i < activePlayersNum) {
// visible panels
final LobbySlot slot = lobby.getSlot(i);
final PlayerPanel panel;
if (hasPanel) {
panel = playerPanels.get(i);
panel.setVisible(true);
} else {
panel = new PlayerPanel(this, allowNetworking, i, slot, lobby.mayEdit(i), lobby.hasControl());
playerPanels.add(panel);
String constraints = "pushx, growx, wrap, hidemode 3";
if (i == 0) {
constraints += ", gaptop 5px";
playerPanelWithFocus = panel;
playerPanelWithFocus.setFocused(true);
}
playersScroll.add(panel, constraints);
}
panel.setType(slot.getType());
panel.setPlayerName(slot.getName());
panel.setAvatar(slot.getAvatarIndex());
panel.setTeam(slot.getTeam());
panel.setIsArchenemy(slot.isArchenemy());
panel.setUseAiSimulation(slot.getAiOptions().contains(AIOption.USE_SIMULATION));
panel.setMayEdit(lobby.mayEdit(i));
panel.setMayControl(lobby.mayControl(i));
panel.setMayRemove(lobby.mayRemove(i));
panel.update();
deckChoosers.get(i).setIsAi(slot.getType() == LobbySlotType.AI);
} else if (hasPanel) {
playerPanels.get(i).setVisible(false);
}
}
private void setLocalPlayer(final int index) {
localPlayer = index;
if (playerWithFocus >= activePlayersNum) {
playerWithFocus = activePlayersNum - 1;
}
changePlayerFocus(playerWithFocus);
refreshPanels(true, true);
}
public void setPlayerChangeListener(final IPlayerChangeListener listener) {
this.playerChangeListener = listener;
}
void firePlayerChangeListener() {
void firePlayerChangeListener(final int index) {
if (playerChangeListener != null) {
playerChangeListener.update(getData(localPlayer));
playerChangeListener.update(index, getSlot(index));
}
}
void fireDeckChangeListener(final int index, final Deck deck) {
decks[index] = deck;
if (playerChangeListener != null) {
playerChangeListener.update(index, UpdateLobbyPlayerEvent.deckUpdate(deck));
}
}
void fireDeckSectionChangeListener(final int index, final DeckSection section, final CardPool cards) {
decks[index].putSection(section, cards);
if (playerChangeListener != null) {
playerChangeListener.update(index, UpdateLobbyPlayerEvent.deckUpdate(section, cards));
}
}
private LobbyPlayerData getData(final int index) {
void removePlayer(final int index) {
lobby.removeSlot(index);
}
boolean hasVariant(final GameType variant) {
return lobby.hasVariant(variant);
}
private UpdateLobbyPlayerEvent getSlot(final int index) {
final PlayerPanel panel = playerPanels.get(index);
return new LobbyPlayerData(panel.getPlayerName(), panel.getAvatarIndex(), panel.getType());
return UpdateLobbyPlayerEvent.create(panel.getType(), panel.getPlayerName(), panel.getAvatarIndex(), panel.getTeam(), panel.isArchenemy(), panel.getAiOptions());
}
/** Builds the actual deck panel layouts for each player.
@@ -374,53 +300,142 @@ public class VLobby implements ILobby {
String labelConstraints = "gaptop 10px, gapbottom 5px";
// Main deck
final FDeckChooser mainChooser = new FDeckChooser(null, isPlayerAI(playerIndex));
final FDeckChooser mainChooser = new FDeckChooser(null, false);
mainChooser.initialize();
mainChooser.getLstDecks().setSelectCommand(new UiCommand() {
@Override
public void run() {
@Override public final void run() {
VLobby.this.onDeckClicked(playerIndex, mainChooser.getSelectedDeckType(), mainChooser.getLstDecks().getSelectedItems());
}
});
deckChoosers.add(mainChooser);
// Scheme deck list
FPanel schemeDeckPanel = new FPanel();
final FPanel schemeDeckPanel = new FPanel();
schemeDeckPanel.setBorderToggle(false);
schemeDeckPanel.setLayout(new MigLayout(sectionConstraints));
schemeDeckPanel.add(new FLabel.Builder().text("Select Scheme deck:").build(), labelConstraints);
FList<Object> schemeDeckList = new FList<Object>();
final FList<Object> schemeDeckList = new FList<Object>();
schemeDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
schemeDeckList.addListSelectionListener(new ListSelectionListener() {
@Override public final void valueChanged(final ListSelectionEvent e) {
if (playerIndex >= activePlayersNum) {
return;
}
FScrollPane scrSchemes = new FScrollPane(schemeDeckList, true,
final Object selected = schemeDeckList.getSelectedValue();
final Deck deck = decks[playerIndex];
CardPool schemePool = null;
if (selected instanceof String) {
String sel = (String) selected;
if (sel.contains("Use deck's scheme section")) {
if (deck.has(DeckSection.Schemes)) {
schemePool = deck.get(DeckSection.Schemes);
} else {
sel = "Random";
}
}
final IStorage<Deck> sDecks = FModel.getDecks().getScheme();
if (sel.equals("Random") && sDecks.size() != 0) {
schemePool = Aggregates.random(sDecks).get(DeckSection.Schemes);
}
} else if (selected instanceof Deck) {
schemePool = ((Deck) selected).get(DeckSection.Schemes);
}
if (schemePool == null) { //Can be null if player deselects the list selection or chose Generate
schemePool = DeckgenUtil.generateSchemePool();
}
fireDeckSectionChangeListener(playerIndex, DeckSection.Schemes, schemePool);
getDeckChooser(playerIndex).saveState();
}
});
final FScrollPane scrSchemes = new FScrollPane(schemeDeckList, true,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
schemeDeckPanel.add(scrSchemes, "grow, push");
schemeDeckLists.add(schemeDeckList);
schemeDeckPanels.add(schemeDeckPanel);
// Commander deck list
FPanel commanderDeckPanel = new FPanel();
final FPanel commanderDeckPanel = new FPanel();
commanderDeckPanel.setBorderToggle(false);
commanderDeckPanel.setLayout(new MigLayout(sectionConstraints));
commanderDeckPanel.add(new FLabel.Builder().text("Select Commander deck:").build(), labelConstraints);
FList<Object> commanderDeckList = new FList<Object>();
final FList<Object> commanderDeckList = new FList<Object>();
commanderDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
commanderDeckList.addListSelectionListener(new ListSelectionListener() {
@Override public final void valueChanged(final ListSelectionEvent e) {
if (playerIndex >= activePlayersNum) {
return;
}
FScrollPane scrCommander = new FScrollPane(commanderDeckList, true,
final Object selected = commanderDeckList.getSelectedValue();
Deck deck = null;
if (selected instanceof String) {
final String sel = (String) selected;
final IStorage<Deck> comDecks = FModel.getDecks().getCommander();
if (sel.equals("Random") && comDecks.size() > 0) {
deck = Aggregates.random(comDecks);
}
} else if (selected instanceof Deck) {
deck = (Deck) selected;
}
final GameType commanderGameType = hasVariant(GameType.TinyLeaders) ? GameType.TinyLeaders : GameType.Commander;
if (deck == null) { //Can be null if player deselects the list selection or chose Generate
deck = DeckgenUtil.generateCommanderDeck(isPlayerAI(playerIndex), commanderGameType);
}
fireDeckChangeListener(playerIndex, deck);
getDeckChooser(playerIndex).saveState();
}
});
final FScrollPane scrCommander = new FScrollPane(commanderDeckList, true,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
commanderDeckPanel.add(scrCommander, "grow, push");
commanderDeckLists.add(commanderDeckList);
commanderDeckPanels.add(commanderDeckPanel);
// Planar deck list
FPanel planarDeckPanel = new FPanel();
final FPanel planarDeckPanel = new FPanel();
planarDeckPanel.setBorderToggle(false);
planarDeckPanel.setLayout(new MigLayout(sectionConstraints));
planarDeckPanel.add(new FLabel.Builder().text("Select Planar deck:").build(), labelConstraints);
FList<Object> planarDeckList = new FList<Object>();
final FList<Object> planarDeckList = new FList<Object>();
planarDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
planarDeckList.addListSelectionListener(new ListSelectionListener() {
@Override public final void valueChanged(final ListSelectionEvent e) {
if (playerIndex >= activePlayersNum) {
return;
}
FScrollPane scrPlanes = new FScrollPane(planarDeckList, true,
final Object selected = planarDeckList.getSelectedValue();
final Deck deck = decks[playerIndex];
CardPool planePool = null;
if (selected instanceof String) {
String sel = (String) selected;
if (sel.contains("Use deck's planes section")) {
if (deck.has(DeckSection.Planes)) {
planePool = deck.get(DeckSection.Planes);
} else {
sel = "Random";
}
}
final IStorage<Deck> pDecks = FModel.getDecks().getPlane();
if (sel.equals("Random") && pDecks.size() != 0) {
planePool = Aggregates.random(pDecks).get(DeckSection.Planes);
}
} else if (selected instanceof Deck) {
planePool = ((Deck) selected).get(DeckSection.Planes);
}
if (planePool == null) { //Can be null if player deselects the list selection or chose Generate
planePool = DeckgenUtil.generatePlanarPool();
}
fireDeckSectionChangeListener(playerIndex, DeckSection.Planes, planePool);
getDeckChooser(playerIndex).saveState();
}
});
final FScrollPane scrPlanes = new FScrollPane(planarDeckList, true,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
planarDeckPanel.add(scrPlanes, "grow, push");
planarDeckLists.add(planarDeckList);
@@ -430,15 +445,15 @@ public class VLobby implements ILobby {
FPanel vgdDeckPanel = new FPanel();
vgdDeckPanel.setBorderToggle(false);
FList<Object> vgdAvatarList = new FList<Object>();
final FList<Object> vgdAvatarList = new FList<Object>();
vgdAvatarList.setListData(isPlayerAI(playerIndex) ? aiListData : humanListData);
vgdAvatarList.setSelectedIndex(0);
vgdAvatarList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
vgdAvatarList.addListSelectionListener(vgdLSListener);
FScrollPane scrAvatars = new FScrollPane(vgdAvatarList, true,
final FScrollPane scrAvatars = new FScrollPane(vgdAvatarList, true,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
CardDetailPanel vgdDetail = new CardDetailPanel();
final CardDetailPanel vgdDetail = new CardDetailPanel();
vgdAvatarDetails.add(vgdDetail);
vgdDeckPanel.setLayout(new MigLayout(sectionConstraints));
@@ -449,16 +464,19 @@ public class VLobby implements ILobby {
vgdPanels.add(vgdDeckPanel);
}
protected void onDeckClicked(int iPlayer, DeckType type, Collection<DeckProxy> selectedDecks) {
String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
protected void onDeckClicked(final int iPlayer, final DeckType type, final Collection<DeckProxy> selectedDecks) {
if (iPlayer < activePlayersNum && lobby.mayEdit(iPlayer)) {
final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME);
playerPanels.get(iPlayer).setDeckSelectorButtonText(text);
fireDeckChangeListener(iPlayer, selectedDecks.iterator().next().getDeck());
}
}
/** Populates the deck panel with the focused player's deck choices. */
private void populateDeckPanel(final GameType forGameType) {
decksFrame.removeAll();
if (playerPanelWithFocus.getType() == LobbySlotType.OPEN || playerPanelWithFocus.getType() == LobbySlotType.REMOTE) {
if (!lobby.mayEdit(playerWithFocus)) {
return;
}
@@ -487,7 +505,7 @@ public class VLobby implements ILobby {
}
/** @return {@link javax.swing.JButton} */
public JButton getBtnStart() {
JButton getBtnStart() {
return this.btnStart;
}
@@ -497,18 +515,18 @@ public class VLobby implements ILobby {
public List<FDeckChooser> getDeckChoosers() { return Collections.unmodifiableList(deckChoosers); }
/** Gets the random deck checkbox for Singletons. */
public FCheckBox getCbSingletons() { return cbSingletons; }
FCheckBox getCbSingletons() { return cbSingletons; }
/** Gets the random deck checkbox for Artifacts. */
public FCheckBox getCbArtifacts() { return cbArtifacts; }
FCheckBox getCbArtifacts() { return cbArtifacts; }
public FCheckBox getVntArchenemy() { return vntArchenemy; }
public FCheckBox getVntArchenemyRumble() { return vntArchenemyRumble; }
public FCheckBox getVntCommander() { return vntCommander; }
public FCheckBox getVntMomirBasic() { return vntMomirBasic; }
public FCheckBox getVntPlanechase() { return vntPlanechase; }
public FCheckBox getVntTinyLeaders() { return vntTinyLeaders; }
public FCheckBox getVntVanguard() { return vntVanguard; }
FCheckBox getVntArchenemy() { return vntArchenemy; }
FCheckBox getVntArchenemyRumble() { return vntArchenemyRumble; }
FCheckBox getVntCommander() { return vntCommander; }
FCheckBox getVntMomirBasic() { return vntMomirBasic; }
FCheckBox getVntPlanechase() { return vntPlanechase; }
FCheckBox getVntTinyLeaders() { return vntTinyLeaders; }
FCheckBox getVntVanguard() { return vntVanguard; }
public int getLastArchenemy() { return lastArchenemy; }
public void setLastArchenemy(final int archenemy) { lastArchenemy = archenemy; }
@@ -524,38 +542,22 @@ public class VLobby implements ILobby {
return deckChoosers.get(playernum);
}
public List<Integer> getTeams() { return teams; }
public List<Integer> getArchenemyTeams() { return archenemyTeams; }
public GameType getCurrentGameMode() { return currentGameMode; }
public void setCurrentGameMode(final GameType mode) { currentGameMode = mode; }
public boolean isPlayerAI(int playernum) {
return playerPanels.get(playernum).isAi();
GameType getCurrentGameMode() {
return lobby.getGameType();
}
void setCurrentGameMode(final GameType mode) {
lobby.setGameType(mode);
update();
}
public Map<String, String> getAiOptions(int playernum) {
if (playerPanels.get(playernum).isSimulatedAi()) {
Map<String, String> options = new HashMap<String, String>();
options.put("UseSimulation", "True");
return options;
}
return null;
public boolean isPlayerAI(final int playernum) {
return playernum < activePlayersNum ? playerPanels.get(playernum).isAi() : false;
}
public int getNumPlayers() {
return activePlayersNum;
}
public final List<Integer> getParticipants() {
final List<Integer> participants = new ArrayList<Integer>(activePlayersNum);
for (final PlayerPanel panel : playerPanels) {
if (panel.isVisible()) {
participants.add(playerPanels.indexOf(panel));
}
}
return participants;
}
/** Revalidates the player and deck sections. Necessary after adding or hiding any panels. */
private void refreshPanels(boolean refreshPlayerFrame, boolean refreshDeckFrame) {
if (refreshPlayerFrame) {
@@ -569,7 +571,7 @@ public class VLobby implements ILobby {
}
public void changePlayerFocus(int newFocusOwner) {
changePlayerFocus(newFocusOwner, appliedVariants.contains(currentGameMode) ? currentGameMode : GameType.Constructed);
changePlayerFocus(newFocusOwner, lobby.getGameType());
}
void changePlayerFocus(int newFocusOwner, GameType gType) {
@@ -593,32 +595,15 @@ public class VLobby implements ILobby {
prefs.save();
}
/** Updates the avatars from preferences on update. */
public void updatePlayersFromPrefs() {
ForgePreferences prefs = FModel.getPreferences();
// Avatar
String[] avatarPrefs = prefs.getPref(FPref.UI_AVATARS).split(",");
for (int i = 0; i < avatarPrefs.length; i++) {
int avatarIndex = Integer.parseInt(avatarPrefs[i]);
playerPanels.get(i).setAvatar(avatarIndex);
}
// Name
String prefName = prefs.getPref(FPref.PLAYER_NAME);
playerPanels.get(0).setPlayerName(StringUtils.isBlank(prefName) ? "Human" : prefName);
}
/** Adds a pre-styled FLabel component with the specified title. */
FLabel newLabel(String title) {
return new FLabel.Builder().text(title).fontSize(14).fontStyle(Font.ITALIC).build();
}
List<Integer> getUsedAvatars() {
List<Integer> usedAvatars = Arrays.asList(-1,-1,-1,-1,-1,-1,-1,-1);
int i = 0;
for (PlayerPanel pp : playerPanels) {
usedAvatars.set(i++, pp.getAvatarIndex());
final List<Integer> usedAvatars = Lists.newArrayListWithCapacity(MAX_PLAYERS);
for (final PlayerPanel pp : playerPanels) {
usedAvatars.add(pp.getAvatarIndex());
}
return usedAvatars;
}
@@ -660,92 +645,30 @@ public class VLobby implements ILobby {
return names;
}
public String getPlayerName(int i) {
return playerPanels.get(i).getPlayerName();
}
public int getPlayerAvatar(int i) {
return playerPanels.get(i).getAvatarIndex();
}
public boolean isEnoughTeams() {
int lastTeam = -1;
final List<Integer> teamList = appliedVariants.contains(GameType.Archenemy) ? archenemyTeams : teams;
for (final int i : getParticipants()) {
if (lastTeam == -1) {
lastTeam = teamList.get(i);
} else if (lastTeam != teamList.get(i)) {
return true;
}
}
return false;
}
/////////////////////////////////////////////
//========== Various listeners in build order
@SuppressWarnings("serial") private class VariantCheckBox extends FCheckBox {
private final GameType variantType;
private VariantCheckBox(GameType variantType0) {
super(variantType0.toString());
variantType = variantType0;
private final GameType variant;
private VariantCheckBox(final GameType variantType) {
super(variantType.toString());
this.variant = variantType;
setToolTipText(variantType.getDescription());
addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
@Override public final void itemStateChanged(final ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
appliedVariants.add(variantType);
currentGameMode = variantType;
//ensure other necessary variants are unchecked
switch (variantType) {
case Archenemy:
vntArchenemyRumble.setSelected(false);
break;
case ArchenemyRumble:
vntArchenemy.setSelected(false);
break;
case Commander:
vntTinyLeaders.setSelected(false);
vntMomirBasic.setSelected(false);
break;
case TinyLeaders:
vntCommander.setSelected(false);
vntMomirBasic.setSelected(false);
break;
case Vanguard:
vntMomirBasic.setSelected(false);
break;
case MomirBasic:
vntCommander.setSelected(false);
vntVanguard.setSelected(false);
break;
default:
break;
lobby.applyVariant(variantType);
} else {
lobby.removeVariant(variantType);
}
}
else {
appliedVariants.remove(variantType);
if (currentGameMode == variantType) {
currentGameMode = GameType.Constructed;
}
}
for (PlayerPanel pp : playerPanels) {
pp.toggleIsPlayerArchenemy();
}
changePlayerFocus(playerWithFocus, currentGameMode);
VLobby.this.update();
}
});
}
}
ActionListener nameListener = new ActionListener() {
final ActionListener nameListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
FTextField nField = (FTextField)e.getSource();
@@ -756,39 +679,51 @@ public class VLobby implements ILobby {
/** This listener will look for a vanguard avatar being selected in the lists
/ and update the corresponding detail panel. */
private ListSelectionListener vgdLSListener = new ListSelectionListener() {
@Override public final void valueChanged(final ListSelectionEvent e) {
final int index = vgdAvatarLists.indexOf(e.getSource());
if (index >= activePlayersNum) {
return;
}
final Object selected = vgdAvatarLists.get(index).getSelectedValue();
final PlayerPanel pp = playerPanels.get(index);
final CardDetailPanel cdp = vgdAvatarDetails.get(index);
@Override
public void valueChanged(ListSelectionEvent e) {
int index = vgdAvatarLists.indexOf(e.getSource());
Object obj = vgdAvatarLists.get(index).getSelectedValue();
PlayerPanel pp = playerPanels.get(index);
CardDetailPanel cdp = vgdAvatarDetails.get(index);
if (obj instanceof PaperCard) {
pp.setVanguardButtonText(((PaperCard) obj).getName());
cdp.setCard(CardView.getCardForUi((PaperCard) obj));
final PaperCard vanguardAvatar;
final Deck deck = decks[index];
if (selected instanceof PaperCard) {
pp.setVanguardButtonText(((PaperCard) selected).getName());
cdp.setCard(CardView.getCardForUi((PaperCard) selected));
cdp.setVisible(true);
refreshPanels(false, true);
}
else {
pp.setVanguardButtonText((String) obj);
vanguardAvatar = (PaperCard)selected;
} else {
String sel = (String) selected;
pp.setVanguardButtonText(sel);
cdp.setVisible(false);
if (sel.contains("Use deck's default avatar") && deck.has(DeckSection.Avatar)) {
vanguardAvatar = deck.get(DeckSection.Avatar).get(0);
} else { //Only other string is "Random"
if (playerPanels.get(index).isAi()) { //AI
vanguardAvatar = Aggregates.random(getNonRandomAiAvatars());
} else { //Human
vanguardAvatar = Aggregates.random(getNonRandomHumanAvatars());
}
}
}
final CardPool avatarOnce = new CardPool();
avatarOnce.add(vanguardAvatar);
fireDeckSectionChangeListener(index, DeckSection.Avatar, avatarOnce);
getDeckChooser(index).saveState();
}
};
/////////////////////////////////////
//========== METHODS FOR VARIANTS
public Set<GameType> getAppliedVariants() {
return Collections.unmodifiableSet(appliedVariants);
}
public int getTeam(final int playerIndex) {
return appliedVariants.contains(GameType.Archenemy) ? archenemyTeams.get(playerIndex) : teams.get(playerIndex);
}
/** Gets the list of planar deck lists. */
public List<FList<Object>> getPlanarDeckLists() {
return planarDeckLists;

View File

@@ -8,74 +8,102 @@ import javax.swing.JMenu;
import forge.GuiBase;
import forge.Singletons;
import forge.UiCommand;
import forge.game.GameRules;
import forge.game.GameType;
import forge.gui.FNetOverlay;
import forge.gui.framework.FScreen;
import forge.gui.framework.ICDoc;
import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener;
import forge.interfaces.IPlayerChangeListener;
import forge.interfaces.IUpdateable;
import forge.match.GameLobby.GameLobbyData;
import forge.menus.IMenuProvider;
import forge.menus.MenuUtil;
import forge.model.FModel;
import forge.net.ClientGameLobby;
import forge.net.FGameClient;
import forge.net.FServerManager;
import forge.net.game.LobbyState;
import forge.net.game.LobbyState.LobbyPlayerData;
import forge.net.game.LoginEvent;
import forge.net.game.client.ILobbyListener;
import forge.net.ServerGameLobby;
import forge.net.game.IRemote;
import forge.net.game.IdentifiableNetEvent;
import forge.net.game.MessageEvent;
import forge.net.game.NetEvent;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.properties.ForgePreferences.FPref;
import forge.screens.home.VLobby;
import forge.screens.home.VLobby.LobbyType;
import forge.screens.home.sanctioned.ConstructedGameMenu;
public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
SINGLETON_INSTANCE;
final void host(final int portNumber) {
final VLobby lobby = VOnlineLobby.SINGLETON_INSTANCE.setLobby(LobbyType.SERVER);
final FServerManager server = FServerManager.getInstance();
final ServerGameLobby lobby = new ServerGameLobby();
final VLobby view = VOnlineLobby.SINGLETON_INSTANCE.setLobby(lobby);
FServerManager.getInstance().startServer(portNumber);
FServerManager.getInstance().setLobby(lobby);
FServerManager.getInstance().hostGame(new GameRules(GameType.Constructed));
server.startServer(portNumber);
server.setLobby(lobby);
FNetOverlay.SINGLETON_INSTANCE.showUp("Hosting game");
lobby.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final LobbyPlayerData data) {
FServerManager.getInstance().updateLobbyState();
lobby.setListener(new IUpdateable() {
@Override public final void update() {
view.update();
server.updateLobbyState();
}
});
view.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final int index, final UpdateLobbyPlayerEvent event) {
server.updateSlot(index, event);
server.updateLobbyState();
}
});
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", GuiBase.getInterface().getNewGuiGame());
FNetOverlay.SINGLETON_INSTANCE.setGameClient(client);
client.addLobbyListener(new ILobbyListener() {
@Override public final void update(final LobbyState state) {
lobby.setState(state);
server.setLobbyListener(new ILobbyListener() {
@Override public final void update(final GameLobbyData state, final int slot) {
// NO-OP, lobby connected directly
}
@Override public final void message(final String source, final String message) {
FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message);
}
});
client.connect("localhost", portNumber);
FNetOverlay.SINGLETON_INSTANCE.setGameClient(new IRemote() {
@Override public final void send(final NetEvent event) {
if (event instanceof MessageEvent) {
final MessageEvent message = (MessageEvent) event;
FNetOverlay.SINGLETON_INSTANCE.addMessage(message.getSource(), message.getMessage());
server.broadcast(event);
}
}
@Override public final Object sendAndWait(final IdentifiableNetEvent event) {
send(event);
return null;
}
});
view.update();
Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY);
FNetOverlay.SINGLETON_INSTANCE.showUp(String.format("Hosting on port %d", portNumber));
}
final void join(final String hostname, final int port) {
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", GuiBase.getInterface().getNewGuiGame());
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", gui);
FNetOverlay.SINGLETON_INSTANCE.setGameClient(client);
final VLobby lobby = VOnlineLobby.SINGLETON_INSTANCE.setLobby(LobbyType.CLIENT);
final ClientGameLobby lobby = new ClientGameLobby();
final VLobby view = VOnlineLobby.SINGLETON_INSTANCE.setLobby(lobby);
lobby.setListener(view);
client.addLobbyListener(new ILobbyListener() {
@Override public final void update(final LobbyState state) {
lobby.setState(state);
}
@Override public final void message(final String source, final String message) {
FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message);
}
@Override public final void update(final GameLobbyData state, final int slot) {
lobby.setLocalPlayer(slot);
lobby.setData(state);
}
});
lobby.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final LobbyPlayerData data) {
client.send(new LoginEvent(data.getName()));
view.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final int index, final UpdateLobbyPlayerEvent event) {
client.send(event);
}
});
client.connect(hostname, port);

View File

@@ -12,8 +12,8 @@ import forge.gui.framework.EDocID;
import forge.gui.framework.FScreen;
import forge.gui.framework.IVDoc;
import forge.gui.framework.IVTopLevelUI;
import forge.match.GameLobby;
import forge.screens.home.VLobby;
import forge.screens.home.VLobby.LobbyType;
import forge.toolbox.FPanel;
import forge.util.gui.SOptionPane;
import forge.view.FView;
@@ -33,9 +33,9 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
VLobby getLobby() {
return lobby;
}
VLobby setLobby(final LobbyType type) {
this.lobby = new VLobby(type);
getLayoutControl().setLobby(lobby);
VLobby setLobby(final GameLobby lobby) {
this.lobby = new VLobby(lobby);
getLayoutControl().setLobby(this.lobby);
return this.lobby;
}
@@ -65,8 +65,10 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
container.repaint();
}
if (!lobby.getPlayerPanels().isEmpty()) {
lobby.changePlayerFocus(0);
}
}
@Override
public EDocID getDocumentID() {

View File

@@ -9,11 +9,14 @@ import forge.deckchooser.IDecksComboBoxListener;
import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab;
import forge.gui.framework.EDocID;
import forge.interfaces.IPlayerChangeListener;
import forge.match.GameLobby;
import forge.match.LocalLobby;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.screens.home.EMenuGroup;
import forge.screens.home.IVSubmenu;
import forge.screens.home.VLobby;
import forge.screens.home.VHomeUI;
import forge.screens.home.VLobby.LobbyType;
import forge.screens.home.VLobby;
/**
* Assembles Swing components of constructed submenu singleton.
@@ -28,12 +31,22 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
private DragCell parentCell;
private final DragTab tab = new DragTab("Constructed Mode");
private final VLobby lobby = new VLobby(LobbyType.LOCAL);
private final GameLobby lobby = new LocalLobby();
private final VLobby vLobby = new VLobby(lobby);
private VSubmenuConstructed() {
lobby.setListener(vLobby);
vLobby.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final int index, final UpdateLobbyPlayerEvent event) {
lobby.applyToSlot(index, event);
}
});
vLobby.update();
}
public VLobby getLobby() {
return lobby;
return vLobby;
}
/////////////////////////////////////
@@ -112,25 +125,27 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
container.removeAll();
container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right"));
container.add(lobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx");
container.add(vLobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx");
for (final FDeckChooser fdc : lobby.getDeckChoosers()) {
for (final FDeckChooser fdc : vLobby.getDeckChoosers()) {
fdc.populate();
fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() {
@Override public final void deckTypeSelected(final DecksComboBoxEvent ev) {
lobby.getPlayerPanelWithFocus().focusOnAvatar();
vLobby.getPlayerPanelWithFocus().focusOnAvatar();
}
});
}
container.add(lobby.getConstructedFrame(), "gap 20px 20px 20px 0px, push, grow");
container.add(lobby.getPanelStart(), "gap 0 0 3.5%! 3.5%!, ax center");
container.add(vLobby.getConstructedFrame(), "gap 20px 20px 20px 0px, push, grow");
container.add(vLobby.getPanelStart(), "gap 0 0 3.5%! 3.5%!, ax center");
if (container.isShowing()) {
container.validate();
container.repaint();
}
lobby.changePlayerFocus(0);
if (!vLobby.getPlayerPanels().isEmpty()) {
vLobby.changePlayerFocus(0);
}
}
}

View File

@@ -51,7 +51,6 @@ import forge.control.KeyboardShortcuts;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deckchooser.FDeckViewer;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
@@ -61,9 +60,8 @@ import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.gui.FNetOverlay;
import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
import forge.gui.GuiUtils;
@@ -78,9 +76,9 @@ import forge.interfaces.IButton;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.match.AbstractGuiGame;
import forge.match.MatchButtonType;
import forge.menus.IMenuProvider;
import forge.model.FModel;
import forge.player.PlayerControllerHuman;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.match.controllers.CAntes;
@@ -103,6 +101,7 @@ import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.MouseTriggerEvent;
import forge.toolbox.special.PhaseIndicator;
import forge.toolbox.special.PhaseLabel;
import forge.trackable.TrackableCollection;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
import forge.util.gui.SOptionPane;
@@ -183,6 +182,21 @@ public final class CMatchUI
public void setGameView(final GameView gameView) {
super.setGameView(gameView);
screen.setTabCaption(gameView.getTitle());
if (sortedPlayers != null) {
for (final PlayerView pv : sortedPlayers) {
pv.copy(gameView.getPlayers());
}
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
for (final VField f : getFieldViews()) {
f.getTabletop().setupPlayZone();
}
for (final VHand h : getHandViews()) {
h.getLayoutControl().updateHand();
}
}
});
}
}
@Override
@@ -248,7 +262,7 @@ public final class CMatchUI
view.getLblAvatar().getResizeTimer().start();
}
public void initMatch(final FCollectionView<PlayerView> sortedPlayers, final Iterable<PlayerView> myPlayers) {
private void initMatch(final FCollectionView<PlayerView> sortedPlayers, final Iterable<PlayerView> myPlayers) {
this.sortedPlayers = sortedPlayers;
this.setLocalPlayers(myPlayers);
allHands = sortedPlayers.size() == getLocalPlayerCount();
@@ -356,7 +370,7 @@ public final class CMatchUI
@Override
public final boolean stopAtPhase(final PlayerView turn, final PhaseType phase) {
final VField vf = getFieldViewFor(turn);
final PhaseLabel label = vf.getPhaseIndicator() .getLabelFor(phase);
final PhaseLabel label = vf.getPhaseIndicator().getLabelFor(phase);
return label == null || label.getEnabled();
}
@@ -541,12 +555,6 @@ public final class CMatchUI
return panels;
}
@Override
public boolean resetForNewGame() {
FloatingCardArea.closeAll();
return true;
}
@Override
public IButton getBtnOK(final PlayerView playerView) {
return view.getBtnOK();
@@ -558,13 +566,15 @@ public final class CMatchUI
}
@Override
public void focusButton(final IButton button) {
public void focusButton(final MatchButtonType button) {
// ensure we don't steal focus from an overlay
if (!SOverlayUtils.overlayHasFocus()) {
FThreads.invokeInEdtLater(new Runnable() {
@Override
public void run() {
((FButton)button).requestFocusInWindow();
@Override public final void run() {
final FButton btn = button == MatchButtonType.OK
? getCPrompt().getView().getBtnOK()
: getCPrompt().getView().getBtnCancel();
btn.requestFocusInWindow();
}
});
}
@@ -579,7 +589,7 @@ public final class CMatchUI
public void updatePhase() {
final PlayerView p = getGameView().getPlayerTurn();
final PhaseType ph = getGameView().getPhase();
PhaseLabel lbl = getFieldViewFor(p).getPhaseIndicator().getLabelFor(ph);
final PhaseLabel lbl = p == null ? null : getFieldViewFor(p).getPhaseIndicator().getLabelFor(ph);
resetAllPhaseButtons();
if (lbl != null) {
@@ -629,8 +639,12 @@ public final class CMatchUI
@Override
public void updateStack() {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
getCStack().update();
}
});
}
/**
* Clear all visually highlighted card panels on the battlefield.
@@ -664,7 +678,7 @@ public final class CMatchUI
}
@Override
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
public SpellAbilityView getAbilityToPlay(final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
if (triggerEvent == null) {
if (abilities.isEmpty()) {
return null;
@@ -691,7 +705,7 @@ public final class CMatchUI
boolean enabled;
boolean hasEnabled = false;
int shortcut = KeyEvent.VK_1; //use number keys as shortcuts for abilities 1-9
for (final SpellAbility ab : abilities) {
for (final SpellAbilityView ab : abilities) {
enabled = ab.canPlay();
if (enabled) {
hasEnabled = true;
@@ -763,12 +777,7 @@ public final class CMatchUI
}
@Override
public void hear(LobbyPlayer player, String message) {
FNetOverlay.SINGLETON_INSTANCE.addMessage(player.getName(), message);
}
@Override
public void openView(final Iterable<PlayerView> myPlayers) {
public void openView(final TrackableCollection<PlayerView> myPlayers) {
final GameView gameView = getGameView();
gameView.getGameLog().addObserver(getCLog());
@@ -841,16 +850,14 @@ public final class CMatchUI
}
@Override
public GameEntityView chooseSingleEntityForEffect(final String title, final FCollectionView<? extends GameEntity> optionList, final DelayedReveal delayedReveal, final boolean isOptional, final PlayerControllerHuman controller) {
public GameEntityView chooseSingleEntityForEffect(final String title, final Collection<? extends GameEntityView> optionList, final DelayedReveal delayedReveal, final boolean isOptional) {
if (delayedReveal != null) {
delayedReveal.reveal(controller); //TODO: Merge this into search dialog
reveal(delayedReveal.getMessagePrefix(), delayedReveal.getCards()); //TODO: Merge this into search dialog
}
controller.tempShow(optionList);
final List<GameEntityView> gameEntityViews = GameEntityView.getEntityCollection(optionList);
if (isOptional) {
return oneOrNone(title, gameEntityViews);
return oneOrNone(title, optionList);
}
return one(title, gameEntityViews);
return one(title, optionList);
}
@Override

View File

@@ -1,6 +1,7 @@
package forge.screens.match;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.SwingUtilities;
@@ -91,21 +92,49 @@ public class VMatchUI implements IVTopLevelUI {
}
}
// Add extra hands to existing hand panel.
for (int i = 1; i < lstHands.size(); i++) {
// If already in layout, no need to add again.
if (lstHands.get(i).getParentCell() == null) { // if i == 0, we get NPE in two lines
DragCell cellWithHand = lstHands.get(0).getParentCell();
cellWithHand.addDoc(lstHands.get(i));
// Determine (an) existing hand panel
DragCell cellWithHands = null;
for (final EDocID handId : EDocID.Hands) {
cellWithHands = handId.getDoc().getParentCell();
if (cellWithHands != null) {
break;
}
}
if (cellWithHands == null) {
// Default to a cell we know exists
cellWithHands = EDocID.REPORT_LOG.getDoc().getParentCell();
}
for (final EDocID handId : EDocID.Hands) {
final DragCell parentCell = handId.getDoc().getParentCell();
VHand myVHand = null;
for (final VHand vHand : lstHands) {
if (handId.equals(vHand.getDocumentID())) {
myVHand = vHand;
break;
}
}
// Remove any hand panels that aren't needed anymore
for (int i = EDocID.Hands.length - 1; i >= lstHands.size(); i--) {
DragCell cellWithHand = EDocID.Hands[i].getDoc().getParentCell();
if (cellWithHand != null) {
cellWithHand.removeDoc(EDocID.Hands[i].getDoc());
EDocID.Hands[i].setDoc(new VEmptyDoc(EDocID.Hands[i]));
if (myVHand == null) {
// Hand not present, remove cell if necessary
if (parentCell != null) {
parentCell.removeDoc(handId.getDoc());
handId.setDoc(new VEmptyDoc(handId));
}
} else {
// Hand present, add it if necessary
if (parentCell == null) {
final EDocID fieldDoc = EDocID.Fields[Arrays.asList(EDocID.Hands).indexOf(handId)];
if (fieldDoc.getDoc().getParentCell() != null) {
fieldDoc.getDoc().getParentCell().addDoc(myVHand);
continue;
}
final EDocID commandDoc = EDocID.Commands[Arrays.asList(EDocID.Hands).indexOf(handId)];
if (commandDoc.getDoc().getParentCell() != null) {
commandDoc.getDoc().getParentCell().addDoc(myVHand);
continue;
}
cellWithHands.addDoc(myVHand);
}
}
}

View File

@@ -20,6 +20,7 @@ package forge.screens.match.controllers;
import java.util.Iterator;
import com.google.common.collect.Iterators;
import com.google.common.primitives.Ints;
import forge.Singletons;
import forge.UiCommand;
@@ -130,7 +131,8 @@ public class CDock implements ICDoc {
public void initialize() {
final String temp = FModel.getPreferences()
.getPref(FPref.UI_TARGETING_OVERLAY);
setArcState(ArcState.values()[Integer.valueOf(temp)]);
final Integer arcState = Ints.tryParse(temp);
setArcState(ArcState.values()[arcState == null ? 0 : arcState.intValue()]);
refreshArcStateDisplay();
view.getBtnConcede().setCommand(new UiCommand() {

View File

@@ -32,7 +32,7 @@ import forge.UiCommand;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.gui.framework.ICDoc;
import forge.gui.framework.SDisplayUtil;
import forge.screens.match.CMatchUI;
@@ -120,7 +120,7 @@ public class CPrompt implements ICDoc {
return matchUI.getGameController().selectCard(cardView, otherCardViewsToSelect, triggerEvent);
}
public void selectAbility(final SpellAbility sa) {
public void selectAbility(final SpellAbilityView sa) {
matchUI.getGameController().selectAbility(sa);
}

View File

@@ -1,18 +1,22 @@
package forge.toolbox;
import forge.interfaces.IComboBox;
import forge.toolbox.FComboBox.TextAlignment;
import forge.toolbox.FSkin.SkinFont;
import javax.swing.*;
import java.awt.*;
import java.awt.Container;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Vector;
import javax.swing.ComboBoxModel;
import javax.swing.JComponent;
import javax.swing.ListCellRenderer;
import com.google.common.collect.ObjectArrays;
import forge.interfaces.IComboBox;
import forge.toolbox.FComboBox.TextAlignment;
import forge.toolbox.FSkin.SkinFont;
/**
* Wrapper for combo box with extra logic (either FComboBoxWrapper or FComboBoxPanel should be used instead of FComboBox so skinning works)
*
@@ -28,6 +32,7 @@ public class FComboBoxWrapper<E> implements IComboBox<E> {
}
private FComboBox<E> comboBox;
private ActionListener[] suppressedActionListeners = null;
private Object constraints;
public FComboBoxWrapper() {
@@ -111,6 +116,24 @@ public class FComboBoxWrapper<E> implements IComboBox<E> {
comboBox.addItemListener(l);
}
public void suppressActionListeners() {
final ActionListener[] listeners = comboBox.getActionListeners();
for (final ActionListener al : listeners) {
comboBox.removeActionListener(al);
}
suppressedActionListeners = suppressedActionListeners == null
? listeners
: ObjectArrays.concat(suppressedActionListeners, listeners, ActionListener.class);
}
public void unsuppressActionListeners() {
if (suppressedActionListeners != null) {
for (final ActionListener al : suppressedActionListeners) {
comboBox.addActionListener(al);
}
suppressedActionListeners = null;
}
}
public void addKeyListener(KeyListener l) {
comboBox.addKeyListener(l);
}

View File

@@ -1,11 +1,14 @@
package forge.toolbox;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import forge.util.ITriggerEvent;
//MouseEvent wrapper used for passing trigger to input classes
public class MouseTriggerEvent implements ITriggerEvent {
public class MouseTriggerEvent implements ITriggerEvent, Serializable {
private static final long serialVersionUID = -4746127117012991732L;
private final MouseEvent event;
public MouseTriggerEvent(MouseEvent event0) {

View File

@@ -1,5 +1,13 @@
package forge.gamesimulationtests.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
@@ -27,6 +35,7 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardShields;
import forge.game.card.CardView;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
@@ -39,8 +48,13 @@ import forge.game.player.DelayedReveal;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerController;
import forge.game.player.PlayerView;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetChoices;
import forge.game.trigger.Trigger;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
@@ -48,18 +62,17 @@ import forge.gamesimulationtests.util.card.CardSpecification;
import forge.gamesimulationtests.util.card.CardSpecificationHandler;
import forge.gamesimulationtests.util.player.PlayerSpecification;
import forge.gamesimulationtests.util.player.PlayerSpecificationHandler;
import forge.gamesimulationtests.util.playeractions.*;
import forge.player.HumanPlay;
import forge.gamesimulationtests.util.playeractions.ActivateAbilityAction;
import forge.gamesimulationtests.util.playeractions.CastSpellFromHandAction;
import forge.gamesimulationtests.util.playeractions.DeclareAttackersAction;
import forge.gamesimulationtests.util.playeractions.DeclareBlockersAction;
import forge.gamesimulationtests.util.playeractions.PlayerActions;
import forge.item.PaperCard;
import forge.player.HumanPlay;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
import forge.util.MyRandom;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
/**
* Default harmless implementation for tests.
* Test-specific behaviour can easily be added by mocking (parts of) this class.
@@ -166,7 +179,7 @@ public class PlayerControllerForTests extends PlayerController {
@Override
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return chooseItem(optionList);
}
@@ -224,6 +237,11 @@ public class PlayerControllerForTests extends PlayerController {
//nothing needs to be done here
}
@Override
public void reveal(Collection<CardView> cards, ZoneType zone, PlayerView owner, String messagePrefix) {
//nothing needs to be done here
}
@Override
public void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value) {
//nothing needs to be done here
@@ -628,7 +646,7 @@ public class PlayerControllerForTests extends PlayerController {
String selectPrompt, boolean isOptional, Player decider) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
}

View File

@@ -288,7 +288,4 @@ public class GuiMobile implements IGuiBase {
return Forge.hostedMatch = new HostedMatch();
}
@Override
public void netMessage(final String origin, final String message) {
}
}

View File

@@ -21,7 +21,7 @@ import forge.util.Callback;
public class GameEntityPicker extends TabPageScreen<GameEntityPicker> {
private final FOptionPane optionPane;
public GameEntityPicker(String title, List<GameEntityView> choiceList, List<CardView> revealList, String revealListCaption, FImage revealListImage, boolean isOptional, final Callback<GameEntityView> callback) {
public GameEntityPicker(String title, Collection<? extends GameEntityView> choiceList, Collection<CardView> revealList, String revealListCaption, FImage revealListImage, boolean isOptional, final Callback<GameEntityView> callback) {
super(new PickerTab[] {
new PickerTab(choiceList, "Choices", FSkinImage.DECKLIST, 1),
new PickerTab(revealList, revealListCaption, revealListImage, 0)

View File

@@ -124,10 +124,6 @@ public class FMenuItem extends FDisplayObject implements IButton {
g.drawLine(1, SEPARATOR_COLOR, 0, h, w, h);
}
@Override
public String getText() {
return text;
}
@Override
public void setText(String text0) {
}

View File

@@ -296,7 +296,7 @@ public class ConstructedScreen extends LaunchScreen {
PlayerPanel playerPanel = playerPanels.get(i);
String name = getPlayerName(i);
LobbyPlayer lobbyPlayer = playerPanel.isPlayerAI() ? GamePlayerUtil.createAiPlayer(name,
getPlayerAvatar(i)) : GamePlayerUtil.getGuiPlayer(name, i);
getPlayerAvatar(i)) : GamePlayerUtil.getGuiPlayer(name, i == 0);
RegisteredPlayer rp = playerPanel.deckChooser.getPlayer();
if (appliedVariants.isEmpty()) {

View File

@@ -26,7 +26,6 @@ import forge.card.CardRenderer;
import forge.card.GameEntityPicker;
import forge.deck.CardPool;
import forge.deck.FSideboardDialog;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
@@ -34,17 +33,16 @@ import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.item.PaperCard;
import forge.match.AbstractGuiGame;
import forge.match.MatchButtonType;
import forge.model.FModel;
import forge.player.PlayerControllerHuman;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.match.views.VAssignDamage;
import forge.screens.match.views.VCardDisplayArea.CardAreaPanel;
import forge.screens.match.views.VPhaseIndicator;
import forge.screens.match.views.VPhaseIndicator.PhaseLabel;
import forge.screens.match.views.VPlayerPanel;
@@ -53,6 +51,7 @@ import forge.screens.match.views.VPrompt;
import forge.screens.match.winlose.ViewWinLose;
import forge.toolbox.FDisplayObject;
import forge.toolbox.FOptionPane;
import forge.trackable.TrackableCollection;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
import forge.util.MessageUtil;
@@ -102,18 +101,12 @@ public class MatchController extends AbstractGuiGame {
}
}
@Override
public boolean resetForNewGame() {
CardAreaPanel.resetForNewGame(); //ensure card panels reset between games
return true;
}
public boolean hotSeatMode() {
return FModel.getPreferences().getPrefBoolean(FPref.MATCH_HOT_SEAT_MODE);
}
@Override
public void openView(final Iterable<PlayerView> myPlayers) {
public void openView(final TrackableCollection<PlayerView> myPlayers) {
setLocalPlayers(myPlayers);
final boolean noHumans = !hasLocalPlayers();
@@ -162,7 +155,7 @@ public class MatchController extends AbstractGuiGame {
}
@Override
public void focusButton(final IButton button) {
public void focusButton(final MatchButtonType button) {
//not needed for mobile game
}
@@ -228,7 +221,7 @@ public class MatchController extends AbstractGuiGame {
}
@Override
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
public SpellAbilityView getAbilityToPlay(List<SpellAbilityView> abilities, ITriggerEvent triggerEvent) {
if (abilities.isEmpty()) {
return null;
}
@@ -324,10 +317,6 @@ public class MatchController extends AbstractGuiGame {
}
}
@Override
public void hear(LobbyPlayer player, String message) {
}
@Override
public boolean stopAtPhase(PlayerView turn, PhaseType phase) {
return view.stopAtPhase(turn, phase);
@@ -475,27 +464,23 @@ public class MatchController extends AbstractGuiGame {
}
@Override
public GameEntityView chooseSingleEntityForEffect(final String title, final FCollectionView<? extends GameEntity> optionList, final DelayedReveal delayedReveal, final boolean isOptional, final PlayerControllerHuman controller) {
controller.tempShow(optionList);
final List<GameEntityView> choiceList = GameEntityView.getEntityCollection(optionList);
public GameEntityView chooseSingleEntityForEffect(final String title, final Collection<? extends GameEntityView> optionList, final DelayedReveal delayedReveal, final boolean isOptional) {
if (delayedReveal == null || Iterables.isEmpty(delayedReveal.getCards())) {
if (isOptional) {
return SGuiChoose.oneOrNone(title, choiceList);
return SGuiChoose.oneOrNone(title, optionList);
}
return SGuiChoose.one(title, choiceList);
return SGuiChoose.one(title, optionList);
}
controller.tempShow(delayedReveal.getCards());
final List<CardView> revealList = CardView.getCollection(delayedReveal.getCards());
final String revealListCaption = StringUtils.capitalize(MessageUtil.formatMessage("{player's} " + delayedReveal.getZone().name(), controller.getPlayer(), delayedReveal.getOwner()));
final Collection<CardView> revealList = delayedReveal.getCards();
final String revealListCaption = StringUtils.capitalize(MessageUtil.formatMessage("{player's} " + delayedReveal.getZone().name(), delayedReveal.getOwner(), delayedReveal.getOwner()));
final FImage revealListImage = MatchController.getView().getPlayerPanels().values().iterator().next().getZoneTab(delayedReveal.getZone()).getIcon();
//use special dialog for choosing card and offering ability to see all revealed cards at the same time
return new WaitCallback<GameEntityView>() {
@Override
public void run() {
GameEntityPicker picker = new GameEntityPicker(title, choiceList, revealList, revealListCaption, revealListImage, isOptional, this);
GameEntityPicker picker = new GameEntityPicker(title, optionList, revealList, revealListCaption, revealListImage, isOptional, this);
picker.show();
}
}.invokeAndWait();

View File

@@ -242,6 +242,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
if (ev.player.getGame().isGameOver()) {
return null;
}
if (ev.newController instanceof PlayerControllerHuman) {
matchController.setGameController(PlayerView.get(ev.player), (PlayerControllerHuman) ev.newController);
}
needPlayerControlUpdate = true;
return processEvent();
}

View File

@@ -9,7 +9,7 @@ import forge.LobbyPlayer;
import forge.game.Game;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGuiGame;
import forge.match.input.Input;
@@ -90,7 +90,7 @@ public class WatchLocalGame extends PlayerControllerHuman {
}
@Override
public void selectAbility(final SpellAbility sa) {
public void selectAbility(final SpellAbilityView sa) {
}
@Override

View File

@@ -4,11 +4,8 @@ import forge.UiCommand;
import forge.assets.FSkinProp;
public interface IButton {
boolean isEnabled();
void setEnabled(boolean b0);
boolean isVisible();
void setVisible(boolean b0);
String getText();
void setText(String text0);
boolean isSelected();
void setSelected(boolean b0);

View File

@@ -4,7 +4,7 @@ import java.util.List;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.match.NextGameDecision;
import forge.util.ITriggerEvent;
@@ -33,7 +33,7 @@ public interface IGameController {
boolean selectCard(CardView cardView,
List<CardView> otherCardViewsToSelect, ITriggerEvent triggerEvent);
void selectAbility(SpellAbility sa);
void selectAbility(SpellAbilityView sa);
boolean tryUndoLastAction();

View File

@@ -51,5 +51,4 @@ public interface IGuiBase {
void showBazaar();
IGuiGame getNewGuiGame();
HostedMatch hostMatch();
void netMessage(String origin, String message);
}

View File

@@ -11,7 +11,6 @@ import com.google.common.base.Function;
import forge.LobbyPlayer;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
@@ -19,25 +18,24 @@ import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.player.PlayerControllerHuman;
import forge.util.FCollectionView;
import forge.match.MatchButtonType;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
public interface IGuiGame {
void setGameView(GameView gameView);
void setGameController(PlayerView player, IGameController gameController);
boolean resetForNewGame();
void openView(Iterable<PlayerView> myPlayers);
void openView(TrackableCollection<PlayerView> myPlayers);
void afterGameEnd();
void showCombat();
void showPromptMessage(PlayerView playerView, String message);
boolean stopAtPhase(PlayerView playerTurn, PhaseType phase);
IButton getBtnOK(PlayerView playerView);
IButton getBtnCancel(PlayerView playerView);
void focusButton(IButton button);
void focusButton(MatchButtonType button);
void flashIncorrectAction();
void updatePhase();
void updateTurn(PlayerView player);
@@ -54,8 +52,7 @@ public interface IGuiGame {
void updateManaPool(Iterable<PlayerView> manaPoolUpdate);
void updateLives(Iterable<PlayerView> livesUpdate);
void setPanelSelection(CardView hostCard);
void hear(LobbyPlayer player, String message);
SpellAbility getAbilityToPlay(List<SpellAbility> abilities,
SpellAbilityView getAbilityToPlay(List<SpellAbilityView> abilities,
ITriggerEvent triggerEvent);
Map<CardView, Integer> assignDamage(CardView attacker,
List<CardView> blockers, int damage, GameEntityView defender,
@@ -161,9 +158,8 @@ public interface IGuiGame {
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
GameEntityView chooseSingleEntityForEffect(String title,
FCollectionView<? extends GameEntity> optionList,
DelayedReveal delayedReveal, boolean isOptional,
PlayerControllerHuman controller);
Collection<? extends GameEntityView> optionList,
DelayedReveal delayedReveal, boolean isOptional);
void setCard(CardView card);
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);

View File

@@ -1,10 +1,10 @@
package forge.interfaces;
import forge.net.game.LobbyState;
import forge.match.GameLobby;
import forge.net.game.server.RemoteClient;
public interface ILobby {
LobbyState getState();
GameLobby getState();
int login(RemoteClient client);
void logout(RemoteClient client);
}

View File

@@ -0,0 +1,8 @@
package forge.interfaces;
import forge.match.GameLobby.GameLobbyData;
public interface ILobbyListener {
void message(String source, String message);
void update(GameLobbyData state, int slot);
}

View File

@@ -1,7 +1,7 @@
package forge.interfaces;
import forge.net.game.LobbyState.LobbyPlayerData;
import forge.net.game.UpdateLobbyPlayerEvent;
public interface IPlayerChangeListener {
void update(LobbyPlayerData data);
void update(int index, UpdateLobbyPlayerEvent event);
}

View File

@@ -0,0 +1,5 @@
package forge.interfaces;
public interface IUpdateable {
void update();
}

View File

@@ -210,9 +210,9 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
btnOk.setEnabled(okEnabled);
btnCancel.setEnabled(cancelEnabled);
if (okEnabled && focusOk) {
focusButton(btnOk);
focusButton(MatchButtonType.OK);
} else if (cancelEnabled) {
focusButton(btnCancel);
focusButton(MatchButtonType.CANCEL);
}
}

View File

@@ -0,0 +1,442 @@
package forge.match;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import forge.AIOption;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckFormat;
import forge.deck.DeckSection;
import forge.game.GameType;
import forge.game.GameView;
import forge.game.player.Player;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame;
import forge.interfaces.IUpdateable;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.net.game.LobbySlotType;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences.FPref;
import forge.util.NameGenerator;
import forge.util.gui.SOptionPane;
public abstract class GameLobby {
private final static int MAX_PLAYERS = 8;
private GameLobbyData data = new GameLobbyData();
private int lastArchenemy = 0;
private IUpdateable listener;
private final boolean allowNetworking;
private HostedMatch hostedMatch;
private final Map<LobbySlot, IGameController> gameControllers = Maps.newHashMap();
protected GameLobby(final boolean allowNetworking) {
this.allowNetworking = allowNetworking;
}
public final boolean isAllowNetworking() {
return allowNetworking;
}
public void setListener(final IUpdateable listener) {
this.listener = listener;
}
public GameLobbyData getData() {
return data;
}
public void setData(final GameLobbyData data) {
this.data = data;
updateView();
}
public GameType getGameType() {
return data.currentGameMode;
}
public void setGameType(final GameType type) {
data.currentGameMode = type;
updateView();
}
public boolean hasVariant(final GameType variant) {
return data.appliedVariants.contains(variant);
}
public int getNumberOfSlots() {
return data.slots.size();
}
public LobbySlot getSlot(final int index) {
if (index < 0 || index >= getNumberOfSlots()) {
return null;
}
return data.slots.get(index);
}
public void applyToSlot(final int index, final UpdateLobbyPlayerEvent event) {
final LobbySlot slot = getSlot(index);
if (slot == null) {
throw new NullPointerException();
}
final int nSlots = getNumberOfSlots();
final boolean triesToChangeArchenemy = event.getArchenemy() != null;
final boolean archenemyRemoved = triesToChangeArchenemy && !event.getArchenemy().booleanValue();
final boolean hasArchenemyChanged = triesToChangeArchenemy && slot.isArchenemy() != event.getArchenemy().booleanValue();
slot.apply(event);
// Change archenemy teams
if (hasVariant(GameType.Archenemy) && hasArchenemyChanged) {
final int newArchenemy = archenemyRemoved ? lastArchenemy : index;
if (archenemyRemoved) {
lastArchenemy = index;
}
for (int otherIndex = 0; otherIndex < nSlots; otherIndex++) {
final LobbySlot otherSlot = getSlot(otherIndex);
final boolean becomesArchenemy = otherIndex == newArchenemy;
if (!archenemyRemoved && otherSlot.isArchenemy() && !becomesArchenemy) {
lastArchenemy = otherIndex;
}
otherSlot.setIsArchenemy(becomesArchenemy);
}
updateView();
}
}
public IGameController getController(final int index) {
return gameControllers.get(getSlot(index));
}
public GameView getGameView() {
return hostedMatch.getGameView();
}
public abstract boolean hasControl();
public abstract boolean mayEdit(final int index);
public abstract boolean mayControl(final int index);
public abstract boolean mayRemove(final int index);
public abstract IGuiGame getGui(final int index);
public void addSlot() {
final int newIndex = getNumberOfSlots();
addSlot(new LobbySlot(allowNetworking ? LobbySlotType.OPEN : LobbySlotType.LOCAL, null, newIndex, newIndex, false, Collections.<AIOption>emptySet()));
}
protected final void addSlot(final LobbySlot slot) {
if (data.slots.size() >= MAX_PLAYERS) {
return;
}
data.slots.add(slot);
if (StringUtils.isEmpty(slot.getName())) {
slot.setName(randomName());
}
if (data.slots.size() == 1) {
// If first slot, make archenemy
slot.setIsArchenemy(true);
lastArchenemy = 0;
}
updateView();
}
private String randomName() {
final List<String> names = Lists.newArrayListWithCapacity(MAX_PLAYERS);
for (final LobbySlot slot : data.slots) {
names.add(slot.getName());
}
return NameGenerator.getRandomName("Any", "Any", names);
}
protected final String localName() {
return FModel.getPreferences().getPref(FPref.PLAYER_NAME);
}
protected final int[] localAvatarIndices() {
final String[] sAvatars = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
final int[] result = new int[sAvatars.length];
for (int i = 0; i < sAvatars.length; i++) {
final Integer val = Ints.tryParse(sAvatars[i]);
result[i] = val == null ? -1 : val.intValue();
}
return result;
}
public void removeSlot(final int index) {
if (index < 0 || index >= data.slots.size()) {
return;
}
if (getSlot(index).isArchenemy()) {
getSlot(lastArchenemy).setIsArchenemy(true);
// Should actually be a stack here, but that's rather involved for
// such a nonimportant feature
lastArchenemy = 0;
} else if (lastArchenemy == index) {
lastArchenemy = 0;
} else {
lastArchenemy--;
}
data.slots.remove(index);
updateView();
}
public void applyVariant(final GameType variant) {
data.currentGameMode = variant;
data.appliedVariants.add(variant);
//ensure other necessary variants are unchecked
switch (variant) {
case Archenemy:
data.appliedVariants.remove(GameType.ArchenemyRumble);
break;
case ArchenemyRumble:
data.appliedVariants.remove(GameType.Archenemy);
break;
case Commander:
data.appliedVariants.remove(GameType.TinyLeaders);
data.appliedVariants.remove(GameType.MomirBasic);
break;
case TinyLeaders:
data.appliedVariants.remove(GameType.Commander);
data.appliedVariants.remove(GameType.MomirBasic);
break;
case Vanguard:
data.appliedVariants.remove(GameType.MomirBasic);
break;
case MomirBasic:
data.appliedVariants.remove(GameType.Commander);
data.appliedVariants.remove(GameType.Vanguard);
break;
default:
break;
}
updateView();
}
public void removeVariant(final GameType variant) {
data.appliedVariants.remove(variant);
if (data.appliedVariants.isEmpty()) {
data.appliedVariants.add(GameType.Constructed);
}
updateView();
}
private boolean isEnoughTeams() {
int lastTeam = -1;
final boolean useArchenemyTeams = data.appliedVariants.contains(GameType.Archenemy);
for (final LobbySlot slot : data.slots) {
final int team = useArchenemyTeams ? (slot.isArchenemy() ? 0 : 1) : slot.getTeam();
if (lastTeam == -1) {
lastTeam = team;
} else if (lastTeam != team) {
return true;
}
}
return false;
}
protected final void updateView() {
if (listener != null) {
listener.update();
}
}
/** Starts a match with the applied variants. */
public void startGame() {
if (!isEnoughTeams()) {
SOptionPane.showMessageDialog("There are not enough teams! Please adjust team allocations.");
return;
}
final List<LobbySlot> activeSlots = Lists.newArrayListWithCapacity(getNumberOfSlots());
for (final LobbySlot slot : data.getSlots()) {
if (slot.getType() != LobbySlotType.OPEN) {
activeSlots.add(slot);
}
}
for (final LobbySlot slot : activeSlots) {
if (slot.getDeck() == null) {
SOptionPane.showMessageDialog(String.format("Please specify a deck for %s", slot.getName()));
return;
}
}
final Set<GameType> variantTypes = data.appliedVariants;
GameType autoGenerateVariant = null;
boolean isCommanderMatch = false;
boolean isTinyLeadersMatch = false;
if (!variantTypes.isEmpty()) {
isTinyLeadersMatch = variantTypes.contains(GameType.TinyLeaders);
isCommanderMatch = isTinyLeadersMatch || variantTypes.contains(GameType.Commander);
if (!isCommanderMatch) {
for (final GameType variant : variantTypes) {
if (variant.isAutoGenerated()) {
autoGenerateVariant = variant;
break;
}
}
}
}
boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY);
//Auto-generated decks don't need to be checked here
//Commander deck replaces regular deck and is checked later
if (checkLegality && autoGenerateVariant == null && !isCommanderMatch) {
for (final LobbySlot slot : activeSlots) {
final String name = slot.getName();
String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(slot.getDeck());
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Deck");
return;
}
}
}
final List<RegisteredPlayer> players = new ArrayList<RegisteredPlayer>();
final Map<RegisteredPlayer, IGuiGame> guis = Maps.newHashMap();
final Map<RegisteredPlayer, LobbySlot> playerToSlot = Maps.newHashMap();
boolean hasNameBeenSet = false;
for (final LobbySlot slot : activeSlots) {
final IGuiGame gui = getGui(data.slots.indexOf(slot));
final String name = slot.getName();
final int avatar = slot.getAvatarIndex();
final int team = slot.getTeam();
final boolean isArchenemy = slot.isArchenemy();
final Set<AIOption> aiOptions = slot.getAiOptions();
final boolean isAI = slot.getType() == LobbySlotType.AI;
final LobbyPlayer lobbyPlayer;
if (isAI) {
lobbyPlayer = GamePlayerUtil.createAiPlayer(name, avatar, aiOptions);
} else {
boolean setNameNow = false;
if (!hasNameBeenSet && slot.getType() == LobbySlotType.LOCAL) {
setNameNow = true;
hasNameBeenSet = true;
}
lobbyPlayer = GamePlayerUtil.getGuiPlayer(name, setNameNow);
}
Deck deck = slot.getDeck();
RegisteredPlayer rp = new RegisteredPlayer(deck);
if (variantTypes.isEmpty()) {
rp.setTeamNumber(team);
players.add(rp.setPlayer(lobbyPlayer));
} else {
PaperCard vanguardAvatar = null;
if (isCommanderMatch) {
final GameType commanderGameType = isTinyLeadersMatch ? GameType.TinyLeaders : GameType.Commander;
if (checkLegality) {
String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck);
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid " + commanderGameType + " Deck");
return;
}
}
} else if (autoGenerateVariant != null) {
deck = autoGenerateVariant.autoGenerateDeck(rp);
final CardPool avatarPool = deck.get(DeckSection.Avatar);
if (avatarPool != null) {
vanguardAvatar = avatarPool.get(0);
}
}
// Initialise variables for other variants
deck = deck == null ? rp.getDeck() : deck;
Iterable<PaperCard> schemes = null;
Iterable<PaperCard> planes = null;
//Archenemy
if (variantTypes.contains(GameType.ArchenemyRumble)
|| (variantTypes.contains(GameType.Archenemy) && isArchenemy)) {
final CardPool schemePool = deck.get(DeckSection.Schemes);
if (checkLegality) {
String errMsg = DeckFormat.getSchemeSectionConformanceProblem(schemePool);
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Scheme Deck");
return;
}
}
schemes = schemePool.toFlatList();
}
//Planechase
if (variantTypes.contains(GameType.Planechase)) {
final CardPool planePool = deck.get(DeckSection.Planes);
if (checkLegality) {
String errMsg = DeckFormat.getPlaneSectionConformanceProblem(planePool);
if (null != errMsg) {
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Planar Deck");
return;
}
}
planes = planePool.toFlatList();
}
//Vanguard
if (variantTypes.contains(GameType.Vanguard)) {
if (vanguardAvatar == null) { //ERROR! null if avatar deselected on list
SOptionPane.showMessageDialog("No Vanguard avatar selected for " + name
+ ". Please choose one or disable the Vanguard variant");
return;
}
}
rp = RegisteredPlayer.forVariants(variantTypes, deck, schemes, isArchenemy, planes, vanguardAvatar);
rp.setTeamNumber(team);
players.add(rp.setPlayer(lobbyPlayer));
}
if (!isAI) {
guis.put(rp, gui);
}
playerToSlot.put(rp, slot);
}
hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.startMatch(GameType.Constructed, variantTypes, players, guis);
for (final Player p : hostedMatch.getGame().getPlayers()) {
final LobbySlot slot = playerToSlot.get(p.getRegisteredPlayer());
if (p.getController() instanceof IGameController) {
gameControllers.put(slot, (IGameController) p.getController());
}
}
}
public final static class GameLobbyData implements Serializable {
private static final long serialVersionUID = -9038854736144187540L;
private transient GameType currentGameMode = GameType.Constructed;
private final Set<GameType> appliedVariants = EnumSet.of(GameType.Constructed);
private final List<LobbySlot> slots = Lists.newArrayList();
public GameType getCurrentGameMode() {
return currentGameMode;
}
public void setCurrentGameMode(GameType currentGameMode) {
this.currentGameMode = currentGameMode;
}
public Set<GameType> getAppliedVariants() {
return Collections.unmodifiableSet(appliedVariants);
}
public List<LobbySlot> getSlots() {
return Collections.unmodifiableList(slots);
}
}
}

View File

@@ -44,8 +44,8 @@ import forge.properties.ForgePreferences.FPref;
import forge.quest.QuestController;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import forge.trackable.TrackableCollection;
import forge.util.CollectionSuppliers;
import forge.util.FCollection;
import forge.util.GuiDisplayUtil;
import forge.util.NameGenerator;
import forge.util.maps.HashMapOfLists;
@@ -137,8 +137,6 @@ public class HostedMatch {
game.subscribeToEvents(SoundSystem.instance);
game.subscribeToEvents(visitor);
final String[] indices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
// Instantiate all required field slots (user at 0)
final List<Player> sortedPlayers = Lists.newArrayList(game.getRegisteredPlayers());
Collections.sort(sortedPlayers, new Comparator<Player>() {
@@ -150,18 +148,23 @@ public class HostedMatch {
}
});
final String[] avatarIndices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
final GameView gameView = getGameView();
int i = 0;
int avatarIndex = 0;
humanCount = 0;
final MapOfLists<IGuiGame, PlayerView> playersPerGui = new HashMapOfLists<IGuiGame, PlayerView>(CollectionSuppliers.<PlayerView>arrayLists());
for (final Player p : sortedPlayers) {
if (i < indices.length) {
avatarIndex = Integer.parseInt(indices[i]);
i++;
for (int iPlayer = 0; iPlayer < sortedPlayers.size(); iPlayer++) {
final RegisteredPlayer rp = match.getPlayers().get(iPlayer);
final Player p = sortedPlayers.get(iPlayer);
p.getLobbyPlayer().setAvatarIndex(rp.getPlayer().getAvatarIndex());
if (p.getLobbyPlayer().getAvatarIndex() == -1) {
if (iPlayer < avatarIndices.length) {
p.getLobbyPlayer().setAvatarIndex(Integer.parseInt(avatarIndices[iPlayer]));
} else {
p.getLobbyPlayer().setAvatarIndex(0);
}
}
p.getLobbyPlayer().setAvatarIndex(avatarIndex);
p.updateAvatar();
if (p.getController() instanceof PlayerControllerHuman) {
@@ -177,8 +180,9 @@ public class HostedMatch {
humanCount++;
}
}
for (final Entry<IGuiGame, Collection<PlayerView>> e : playersPerGui.entrySet()) {
e.getKey().openView(new FCollection<PlayerView>(e.getValue()));
e.getKey().openView(new TrackableCollection<PlayerView>(e.getValue()));
}
if (humanCount == 0) { //watch game but do not participate

View File

@@ -0,0 +1,109 @@
package forge.match;
import java.io.Serializable;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import forge.AIOption;
import forge.deck.Deck;
import forge.net.game.LobbySlotType;
import forge.net.game.UpdateLobbyPlayerEvent;
public final class LobbySlot implements Serializable {
private static final long serialVersionUID = 6918205436608794289L;
private LobbySlotType type;
private String name;
private int avatarIndex;
private int team;
private boolean isArchenemy;
private Deck deck;
private ImmutableSet<AIOption> aiOptions;
public LobbySlot(final LobbySlotType type, final String name, final int avatarIndex, final int team, final boolean isArchenemy, final Set<AIOption> aiOptions) {
this.type = type;
this.name = name;
this.avatarIndex = avatarIndex;
this.team = team;
this.isArchenemy = isArchenemy;
this.setAiOptions(aiOptions);
}
void apply(final UpdateLobbyPlayerEvent data) {
if (data.getType() != null) {
setType(data.getType());
}
if (data.getName() != null) {
setName(data.getName());
}
if (data.getAvatarIndex() != -1) {
setAvatarIndex(data.getAvatarIndex());
}
if (data.getTeam() != -1) {
setTeam(data.getTeam());
}
if (data.getArchenemy() != null) {
setIsArchenemy(data.getArchenemy().booleanValue());
}
if (data.getAiOptions() != null) {
setAiOptions(data.getAiOptions());
}
if (data.getDeck() != null) {
setDeck(data.getDeck());
} else if (getDeck() != null && data.getSection() != null && data.getCards() != null) {
getDeck().putSection(data.getSection(), data.getCards());
}
}
public LobbySlotType getType() {
return type;
}
public void setType(final LobbySlotType type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public int getAvatarIndex() {
return avatarIndex;
}
public void setAvatarIndex(final int avatarIndex) {
this.avatarIndex = avatarIndex;
}
public int getTeam() {
return team;
}
public void setTeam(final int team) {
this.team = team;
}
public boolean isArchenemy() {
return isArchenemy;
}
public void setIsArchenemy(final boolean isArchenemy) {
this.isArchenemy = isArchenemy;
}
public Deck getDeck() {
return deck;
}
public void setDeck(final Deck deck) {
this.deck = deck;
}
public ImmutableSet<AIOption> getAiOptions() {
return aiOptions;
}
public void setAiOptions(final Set<AIOption> aiOptions) {
this.aiOptions = aiOptions == null ? ImmutableSet.<AIOption>of() : ImmutableSet.copyOf(aiOptions);
}
}

View File

@@ -0,0 +1,44 @@
package forge.match;
import java.util.Collections;
import forge.AIOption;
import forge.GuiBase;
import forge.interfaces.IGuiGame;
import forge.net.game.LobbySlotType;
public final class LocalLobby extends GameLobby {
public LocalLobby() {
super(false);
final String humanName = localName();
final int[] avatarIndices = localAvatarIndices();
final LobbySlot slot0 = new LobbySlot(LobbySlotType.LOCAL, humanName, avatarIndices[0], 0, true, Collections.<AIOption>emptySet());
addSlot(slot0);
final LobbySlot slot1 = new LobbySlot(LobbySlotType.AI, null, avatarIndices[1], 1, false, Collections.<AIOption>emptySet());
addSlot(slot1);
}
@Override public boolean hasControl() {
return true;
}
@Override public boolean mayEdit(final int index) {
return true;
}
@Override public boolean mayControl(final int index) {
return true;
}
@Override public boolean mayRemove(final int index) {
return index >= 2;
}
@Override public IGuiGame getGui(final int index) {
return GuiBase.getInterface().getNewGuiGame();
}
}

View File

@@ -0,0 +1,6 @@
package forge.match;
public enum MatchButtonType {
OK,
CANCEL;
}

View File

@@ -1,415 +0,0 @@
package forge.match;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import forge.LobbyPlayer;
import forge.UiCommand;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.item.PaperCard;
import forge.net.game.GuiGameEvent;
import forge.net.game.server.IToClient;
import forge.player.PlayerControllerHuman;
import forge.trackable.TrackableObject;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
public class NetGuiGame extends AbstractGuiGame {
private final IToClient client;
public NetGuiGame(final IToClient client) {
this.client = client;
}
private void send(final String method) {
send(method, Collections.<TrackableObject>emptySet());
}
private void send(final String method, final TrackableObject object) {
send(method, Collections.singleton(object));
}
private void send(final String method, final Iterable<? extends TrackableObject> objects) {
client.send(new GuiGameEvent(method, objects));
}
@Override
public void setGameView(final GameView gameView) {
super.setGameView(gameView);
send("setGameView", gameView);
}
@Override
public boolean resetForNewGame() {
send("resetForNewGame");
return true;
}
@Override
public void openView(final Iterable<PlayerView> myPlayers) {
send("openView", myPlayers);
}
@Override
public void afterGameEnd() {
send("afterGameEnd");
}
@Override
public void showCombat() {
send("showCombat");
}
@Override
public void showPromptMessage(PlayerView playerView, String message) {
// TODO Auto-generated method stub
}
@Override
public boolean stopAtPhase(PlayerView playerTurn, PhaseType phase) {
// TODO Auto-generated method stub
return false;
}
@Override
public IButton getBtnOK(final PlayerView playerView) {
return new NetButton(playerView, "OK");
}
@Override
public IButton getBtnCancel(final PlayerView playerView) {
return new NetButton(playerView, "Cancel");
}
@Override
public void focusButton(IButton button) {
// TODO Auto-generated method stub
}
@Override
public void flashIncorrectAction() {
// TODO Auto-generated method stub
}
@Override
public void updatePhase() {
// TODO Auto-generated method stub
}
@Override
public void updateTurn(PlayerView player) {
// TODO Auto-generated method stub
}
@Override
public void updatePlayerControl() {
// TODO Auto-generated method stub
}
@Override
public void enableOverlay() {
// TODO Auto-generated method stub
}
@Override
public void disableOverlay() {
// TODO Auto-generated method stub
}
@Override
public void finishGame() {
// TODO Auto-generated method stub
}
@Override
public Object showManaPool(PlayerView player) {
// TODO Auto-generated method stub
return null;
}
@Override
public void hideManaPool(PlayerView player, Object zoneToRestore) {
// TODO Auto-generated method stub
}
@Override
public void updateStack() {
// TODO Auto-generated method stub
}
@Override
public void updateZones(List<Pair<PlayerView, ZoneType>> zonesToUpdate) {
// TODO Auto-generated method stub
}
@Override
public void updateSingleCard(CardView card) {
// TODO Auto-generated method stub
}
@Override
public void updateManaPool(Iterable<PlayerView> manaPoolUpdate) {
// TODO Auto-generated method stub
}
@Override
public void updateLives(Iterable<PlayerView> livesUpdate) {
// TODO Auto-generated method stub
}
@Override
public void setPanelSelection(CardView hostCard) {
// TODO Auto-generated method stub
}
@Override
public void hear(LobbyPlayer player, String message) {
// TODO Auto-generated method stub
}
@Override
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities,
ITriggerEvent triggerEvent) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<CardView, Integer> assignDamage(CardView attacker,
List<CardView> blockers, int damage, GameEntityView defender,
boolean overrideOrder) {
// TODO Auto-generated method stub
return null;
}
@Override
public void message(String message, String title) {
// TODO Auto-generated method stub
}
@Override
public void showErrorDialog(String message, String title) {
// TODO Auto-generated method stub
}
@Override
public boolean showConfirmDialog(String message, String title,
String yesButtonText, String noButtonText, boolean defaultYes) {
// TODO Auto-generated method stub
return false;
}
@Override
public int showOptionDialog(String message, String title, FSkinProp icon,
String[] options, int defaultOption) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int showCardOptionDialog(CardView card, String message,
String title, FSkinProp icon, String[] options, int defaultOption) {
// TODO Auto-generated method stub
return 0;
}
@Override
public String showInputDialog(String message, String title, FSkinProp icon,
String initialInput, String[] inputOptions) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean confirm(CardView c, String question, boolean defaultIsYes,
String[] options) {
// TODO Auto-generated method stub
return false;
}
@Override
public <T> List<T> getChoices(String message, int min, int max,
Collection<T> choices, T selected, Function<T, String> display) {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> List<T> order(String title, String top, int remainingObjectsMin,
int remainingObjectsMax, List<T> sourceChoices,
List<T> destChoices, CardView referenceCard,
boolean sideboardingMode) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<PaperCard> sideboard(CardPool sideboard, CardPool main) {
// TODO Auto-generated method stub
return null;
}
@Override
public GameEntityView chooseSingleEntityForEffect(String title,
FCollectionView<? extends GameEntity> optionList,
DelayedReveal delayedReveal, boolean isOptional,
PlayerControllerHuman controller) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setCard(CardView card) {
// TODO Auto-generated method stub
}
@Override
public void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi) {
// TODO Auto-generated method stub
}
@Override
public boolean openZones(Collection<ZoneType> zones,
Map<PlayerView, Object> players) {
// TODO Auto-generated method stub
return false;
}
@Override
public void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor) {
// TODO Auto-generated method stub
}
@Override
public boolean isUiSetToSkipPhase(PlayerView playerTurn, PhaseType phase) {
// TODO Auto-generated method stub
return false;
}
@Override
protected void updateCurrentPlayer(PlayerView player) {
// TODO Auto-generated method stub
}
private final static class NetButton implements IButton {
private final PlayerView playerView;
private final String button;
private NetButton(final PlayerView playerView, final String button) {
this.playerView = playerView;
this.button = button;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setEnabled(boolean b0) {
// TODO Auto-generated method stub
}
@Override
public boolean isVisible() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setVisible(boolean b0) {
// TODO Auto-generated method stub
}
@Override
public String getText() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setText(String text0) {
// TODO Auto-generated method stub
}
@Override
public boolean isSelected() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setSelected(boolean b0) {
// TODO Auto-generated method stub
}
@Override
public boolean requestFocusInWindow() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setCommand(UiCommand command0) {
// TODO Auto-generated method stub
}
@Override
public void setTextColor(FSkinProp color) {
// TODO Auto-generated method stub
}
@Override
public void setTextColor(int r, int g, int b) {
// TODO Auto-generated method stub
}
}
}

View File

@@ -83,6 +83,11 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
showMessage(sb.toString());
}
@Override
protected final boolean allowAwaitNextInput() {
return true;
}
/** {@inheritDoc} */
@Override
protected final void onOk() {

View File

@@ -0,0 +1,36 @@
package forge.net;
import forge.interfaces.IGuiGame;
import forge.match.GameLobby;
public final class ClientGameLobby extends GameLobby {
private int localPlayer = -1;
public ClientGameLobby() {
super(true);
}
public void setLocalPlayer(final int index) {
this.localPlayer = index;
}
@Override public boolean hasControl() {
return false;
}
@Override public boolean mayEdit(final int index) {
return index == localPlayer;
}
@Override public boolean mayControl(final int index) {
return false;
}
@Override public boolean mayRemove(final int index) {
return false;
}
@Override public IGuiGame getGui(final int index) {
return null;
}
}

View File

@@ -15,22 +15,44 @@ import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.FThreads;
import forge.UiCommand;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener;
import forge.match.MatchButtonType;
import forge.model.FModel;
import forge.net.game.GuiGameEvent;
import forge.net.game.LobbyUpdateEvent;
import forge.net.game.IdentifiableNetEvent;
import forge.net.game.LoginEvent;
import forge.net.game.MessageEvent;
import forge.net.game.NetEvent;
import forge.net.game.client.ILobbyListener;
import forge.net.game.ReplyEvent;
import forge.net.game.client.IToServer;
import forge.properties.ForgePreferences.FPref;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
public class FGameClient implements IToServer {
private final IGuiGame clientGui;
@@ -39,6 +61,7 @@ public class FGameClient implements IToServer {
}
private final List<ILobbyListener> lobbyListeners = Lists.newArrayList();
private final ReplyPool replies = new ReplyPool();
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
@@ -81,14 +104,32 @@ public class FGameClient implements IToServer {
}
}
@Override
public void send(final NetEvent event) {
System.out.println("Client sent " + event);
channel.writeAndFlush(event);
}
@Override
public Object sendAndWait(final IdentifiableNetEvent event) throws TimeoutException {
replies.initialize(event.getId());
send(event);
// Wait for reply
return replies.get(event.getId());
}
public void addLobbyListener(final ILobbyListener listener) {
lobbyListeners.add(listener);
}
private void setGameControllers(final Iterable<PlayerView> myPlayers) {
for (final PlayerView p : myPlayers) {
clientGui.setGameController(p, new NetGameController(this));
}
}
private class GameClientHandler extends ChannelInboundHandlerAdapter {
/**
* Creates a client-side handler.
@@ -98,24 +139,214 @@ public class FGameClient implements IToServer {
@Override
public void channelActive(final ChannelHandlerContext ctx) {
// Don't use send here, as this.channel is not yet set!
ctx.channel().writeAndFlush(new LoginEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME)));
// Don't use send() here, as this.channel is not yet set!
ctx.channel().writeAndFlush(new LoginEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME), Integer.parseInt(FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",")[0])));
}
@SuppressWarnings("unchecked")
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
System.out.println("Client received: " + msg);
if (msg instanceof GuiGameEvent) {
if (msg instanceof ReplyEvent) {
final ReplyEvent event = (ReplyEvent) msg;
replies.complete(event.getIndex(), event.getReply());
} else if (msg instanceof GuiGameEvent) {
final GuiGameEvent event = (GuiGameEvent) msg;
final Object[] args = event.getObjects();
Serializable reply = null;
boolean doReply = false;
final IButton btn;
if (args.length >= 2 && args[0] instanceof PlayerView && args[1] instanceof MatchButtonType) {
btn = args[1] == MatchButtonType.OK ? clientGui.getBtnOK((PlayerView) args[0]) : clientGui.getBtnCancel((PlayerView) args[0]);
} else {
btn = null;
}
switch (event.getMethod()) {
case "setGameView":
clientGui.setGameView((GameView) event.getObject());
clientGui.setGameView((GameView) args[0]);
break;
case "openView":
clientGui.openView((Iterable<PlayerView>) event.getObjects());
default:
final TrackableCollection<PlayerView> myPlayers = (TrackableCollection<PlayerView>) args[0];
setGameControllers(myPlayers);
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
//clientGui.setGameView(new NetGameView(FGameClient.this));
clientGui.openView(myPlayers);
}
});
break;
case "afterGameEnd":
clientGui.afterGameEnd();
break;
case "showCombat":
clientGui.showCombat();
break;
case "showPromptMessage":
clientGui.showPromptMessage((PlayerView) args[0], (String) args[1]);
break;
case "stopAtPhase":
reply = clientGui.stopAtPhase((PlayerView) args[0], (PhaseType) args[1]);
doReply = true;
break;
case "focusButton":
clientGui.focusButton((MatchButtonType) args[0]);
break;
case "flashIncorrectAction":
clientGui.flashIncorrectAction();
break;
case "updatePhase":
clientGui.updatePhase();
break;
case "updateTurn":
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
clientGui.updateTurn((PlayerView) args[0]);
}
});
break;
case "udpdatePlayerControl":
clientGui.updatePlayerControl();
break;
case "enableOverlay":
clientGui.enableOverlay();
break;
case "disbleOverlay":
clientGui.disableOverlay();
break;
case "finishGame":
clientGui.finishGame();
break;
case "showManaPool":
clientGui.showManaPool((PlayerView) args[0]);
break;
case "hideManaPool":
clientGui.hideManaPool((PlayerView) args[0], args[1]);
break;
case "updateStack":
clientGui.updateStack();
break;
case "updateZones":
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public final void run() {
clientGui.updateZones((List<Pair<PlayerView, ZoneType>>) args[0]);
}
});
break;
case "updateSingleCard":
clientGui.updateSingleCard((CardView) args[0]);
break;
case "updateManaPool":
clientGui.updateManaPool((Iterable<PlayerView>) args[0]);
break;
case "updateLives":
clientGui.updateLives((Iterable<PlayerView>) args[0]);
break;
case "setPanelSelection":
clientGui.setPanelSelection((CardView) args[0]);
break;
case "getAbilityToPlay":
reply = clientGui.getAbilityToPlay((List<SpellAbilityView>) args[0], (ITriggerEvent) args[1]);
doReply = true;
break;
case "assignDamage":
reply = (Serializable) clientGui.assignDamage((CardView) args[0], (List<CardView>) args[1], (int) args[2], (GameEntityView) args[3], (boolean) args[4]);
doReply = true;
break;
case "message":
clientGui.message((String) args[0], (String) args[1]);
break;
case "showErrorDialog":
clientGui.showErrorDialog((String) args[0], (String) args[1]);
break;
case "showConfirmDialog":
reply = clientGui.showConfirmDialog((String) args[0], (String) args[1], (String) args[2], (String) args[3], (boolean) args[4]);
doReply = true;
break;
case "showOptionDialog":
reply = clientGui.showOptionDialog((String) args[0], (String) args[1], (FSkinProp) args[2], (String[]) args[3], (int) args[4]);
doReply = true;
break;
case "showCardOptionDialog":
reply = clientGui.showCardOptionDialog((CardView) args[0], (String) args[1], (String) args[2], (FSkinProp) args[3], (String[]) args[4], (int) args[5]);
doReply = true;
break;
case "showInputDialog":
reply = clientGui.showInputDialog((String) args[0], (String) args[1], (FSkinProp) args[2], (String) args[3], (String[]) args[4]);
doReply = true;
break;
case "confirm":
reply = clientGui.confirm((CardView) args[0], (String) args[1], (boolean) args[2], (String[]) args[3]);
doReply = true;
break;
case "getChoices":
reply = (Serializable) clientGui.getChoices((String) args[0], (int) args[1], (int) args[2], (Collection<Object>) args[3], args[4], (Function<Object, String>) args[5]);
doReply = true;
break;
case "order":
reply = (Serializable) clientGui.order((String) args[0], (String) args[1], (int) args[2], (int) args[3], (List<Object>) args[4], (List<Object>) args[5], (CardView) args[6], (boolean) args[7]);
doReply = true;
break;
case "sideboard":
reply = (Serializable) clientGui.sideboard((CardPool) args[0], (CardPool) args[1]);
doReply = true;
break;
case "chooseSingleEntityForEffect":
reply = clientGui.chooseSingleEntityForEffect((String) args[0], (TrackableCollection<GameEntityView>) args[1], (DelayedReveal) args[2], (boolean) args[3]);
doReply = true;
break;
case "setCard":
clientGui.setCard((CardView) args[0]);
break;
// TODO case "setPlayerAvatar":
case "openZones":
reply = clientGui.openZones((Collection<ZoneType>) args[0], (Map<PlayerView, Object>) args[1]);
doReply = true;
break;
case "restoreOldZones":
clientGui.restoreOldZones((Map<PlayerView, Object>) args[0]);
break;
case "isUiSetToSkipPhase":
reply = clientGui.isUiSetToSkipPhase((PlayerView) args[0], (PhaseType) args[1]);
doReply = true;
break;
// BUTTONS
case "btn_setEnabled":
btn.setEnabled((boolean) args[2]);
break;
case "btn_setVisible":
btn.setVisible((boolean) args[2]);
break;
case "btn_setText":
btn.setText((String) args[2]);
break;
case "btn_isSelected":
reply = btn.isSelected();
doReply = true;
break;
case "btn_setSelected":
btn.setSelected((boolean) args[2]);
break;
case "btn_requestFocusInWindows":
reply = btn.requestFocusInWindow();
doReply = true;
break;
case "btn_setCommand":
btn.setCommand((UiCommand) args[2]);
break;
case "btn_setTextColor":
if (args.length == 3) {
btn.setTextColor((FSkinProp) args[2]);
} else {
btn.setTextColor((int) args[2], (int) args[3], (int) args[4]);
}
default:
System.err.println("Unsupported game event " + event.getMethod());
break;
}
if (doReply) {
send(new ReplyEvent(event.getId(), reply));
}
}
}
@@ -145,7 +376,8 @@ public class FGameClient implements IToServer {
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
if (msg instanceof LobbyUpdateEvent) {
for (final ILobbyListener listener : lobbyListeners) {
listener.update(((LobbyUpdateEvent) msg).getState());
final LobbyUpdateEvent event = (LobbyUpdateEvent) msg;
listener.update(event.getState(), event.getSlot());
}
}
super.channelRead(ctx, msg);

View File

@@ -1,15 +1,27 @@
package forge.net;
import forge.game.GameRules;
import forge.interfaces.ILobby;
import forge.net.game.LobbyState;
import forge.net.game.LobbyUpdateEvent;
import forge.FThreads;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame;
import forge.interfaces.ILobbyListener;
import forge.match.LobbySlot;
import forge.match.NextGameDecision;
import forge.net.game.GuiGameEvent;
import forge.net.game.LobbySlotType;
import forge.net.game.LoginEvent;
import forge.net.game.LogoutEvent;
import forge.net.game.MessageEvent;
import forge.net.game.NetEvent;
import forge.net.game.RegisterDeckEvent;
import forge.net.game.ReplyEvent;
import forge.net.game.UpdateLobbyPlayerEvent;
import forge.net.game.server.RemoteClient;
import forge.util.ITriggerEvent;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@@ -27,8 +39,14 @@ import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
public final class FServerManager {
@@ -36,18 +54,13 @@ public final class FServerManager {
private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private final Map<Integer, NetGame> games = Maps.newTreeMap();
private int id = 0;
private final Map<Channel, RemoteClient> clients = Maps.newTreeMap();
private ILobby localLobby;
private ServerGameLobby localLobby;
private ILobbyListener lobbyListener;
private FServerManager() {
}
private int nextId() {
return id++;
}
public static FServerManager getInstance() {
if (instance == null) {
instance = new FServerManager();
@@ -62,15 +75,14 @@ public final class FServerManager {
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
@Override public final void initChannel(final SocketChannel ch) {
final ChannelPipeline p = ch.pipeline();
p.addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new MessageHandler(),
new RegisterClientHandler(),
new ToLobbyListenersHandler(),
new LobbyInputHandler(),
new DeregisterClientHandler(),
new GameServerHandler());
}
@@ -101,29 +113,53 @@ public final class FServerManager {
}
public void broadcast(final NetEvent event) {
for (final RemoteClient client : clients.values()) {
broadcastTo(event, clients.values());
}
public void broadcastExcept(final NetEvent event, final RemoteClient notTo) {
broadcastExcept(event, Collections.singleton(notTo));
}
public void broadcastExcept(final NetEvent event, final Collection<RemoteClient> notTo) {
broadcastTo(event, Iterables.filter(clients.values(), Predicates.not(Predicates.in(notTo))));
}
private void broadcastTo(final NetEvent event, final Iterable<RemoteClient> to) {
for (final RemoteClient client : to) {
event.updateForClient(client);
client.send(event);
}
}
public void setLobby(final ILobby lobby) {
public void setLobby(final ServerGameLobby lobby) {
this.localLobby = lobby;
}
public NetGame hostGame(final GameRules rules) {
final int id = nextId();
final NetGame game = new NetGame(rules);
games.put(id, game);
return game;
public void setLobbyListener(final ILobbyListener listener) {
this.lobbyListener = listener;
}
public void updateLobbyState() {
final LobbyState state = localLobby.getState();
final LobbyUpdateEvent event = new LobbyUpdateEvent(state);
final LobbyUpdateEvent event = new LobbyUpdateEvent(localLobby.getData());
broadcast(event);
}
public void updateSlot(final int index, final UpdateLobbyPlayerEvent event) {
localLobby.applyToSlot(index, event);
}
public IGuiGame getGui(final int index) {
final LobbySlot slot = localLobby.getSlot(index);
final LobbySlotType type = slot.getType();
if (type == LobbySlotType.LOCAL) {
return GuiBase.getInterface().getNewGuiGame();
} else if (type == LobbySlotType.REMOTE) {
for (final RemoteClient client : clients.values()) {
if (client.getIndex() == index) {
return new NetGuiGame(client);
}
}
}
return null;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
@@ -141,9 +177,187 @@ public final class FServerManager {
}
private class GameServerHandler extends ChannelInboundHandlerAdapter {
@SuppressWarnings("unchecked")
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
System.out.println("Server received: " + msg);
final RemoteClient client = clients.get(ctx.channel());
if (msg instanceof ReplyEvent) {
client.setReply(((ReplyEvent) msg).getIndex(), ((ReplyEvent) msg).getReply());
} else if (msg instanceof GuiGameEvent) {
final GuiGameEvent event = (GuiGameEvent) msg;
final GameView gameView = localLobby.getGameView();
final IGameController controller = localLobby.getController(client.getIndex());
final Object[] args = event.getObjects();
FThreads.invokeInBackgroundThread(new Runnable() {
@Override public final void run() {
Serializable reply = null;
boolean doReply = false;
switch (event.getMethod()) {
// From GameController
case "useMana":
controller.useMana((byte) args[0]);
break;
case "tryUndoLastAction":
reply = controller.tryUndoLastAction();
doReply = true;
break;
case "selectPlayer":
controller.selectPlayer((PlayerView) args[0], (ITriggerEvent) args[1]);
break;
case "selectCard":
reply = controller.selectCard((CardView) args[0], (List<CardView>) args[1], (ITriggerEvent) args[2]);
doReply = true;
break;
case "selectButtonOk":
controller.selectButtonOk();
break;
case "selectButtonCancel":
controller.selectButtonCancel();
break;
case "selectAbility":
controller.selectAbility((SpellAbilityView) args[0]);
break;
case "passPriorityUntilEndOfTurn":
reply = controller.passPriorityUntilEndOfTurn();
doReply = true;
break;
case "passPriority":
reply = controller.passPriority();
doReply = true;
break;
case "nextGameDecision":
controller.nextGameDecision((NextGameDecision) args[0]);
break;
case "mayLookAtAllCards":
reply = controller.mayLookAtAllCards();
doReply = true;
break;
case "getActivateDescription":
reply = controller.getActivateDescription((CardView) args[0]);
doReply = true;
break;
case "concede":
controller.concede();
break;
case "alphaStrike":
controller.alphaStrike();
break;
// From GameView
case "getPlayers":
reply = (Serializable) gameView.getPlayers();
doReply = true;
break;
case "getTitle":
reply = gameView.getTitle();
doReply = true;
break;
case "isCommander":
reply = gameView.isCommander();
doReply = true;
break;
case "getGameType":
reply = gameView.getGameType();
doReply = true;
break;
case "getPoisonCountersToLose":
reply = gameView.getPoisonCountersToLose();
doReply = true;
break;
case "getNumGamesInMatch":
reply = gameView.getNumGamesInMatch();
doReply = true;
break;
case "getTurn":
reply = gameView.getTurn();
doReply = true;
break;
case "getPhase":
reply = gameView.getPhase();
doReply = true;
break;
case "getPlayerTurn":
reply = gameView.getPlayerTurn();
doReply = true;
break;
case "getStack":
reply = (Serializable) gameView.getStack();
doReply = true;
break;
case "peekStack":
reply = gameView.peekStack();
doReply = true;
break;
case "getStormCount":
reply = gameView.getStormCount();
doReply = true;
break;
case "isFirstGameInMatch":
reply = gameView.isFirstGameInMatch();
doReply = true;
break;
case "getNumPlayedGamesInMatch":
reply = gameView.getNumPlayedGamesInMatch();
doReply = true;
break;
case "isGameOver":
reply = gameView.isGameOver();
doReply = true;
break;
case "isMatchOver":
reply = gameView.isMatchOver();
doReply = true;
break;
case "getWinningTeam":
reply = gameView.getWinningTeam();
doReply = true;
break;
case "getGameLog":
reply = gameView.getGameLog();
doReply = true;
break;
case "getCombat":
reply = gameView.getCombat();
doReply = true;
break;
case "isMatchWonBy":
reply = gameView.isMatchWonBy((LobbyPlayer) args[0]);
doReply = true;
break;
case "getOutcomesOfMatch":
reply = (Serializable) gameView.getOutcomesOfMatch();
doReply = true;
break;
// TODO case "getWinningPlayer":
case "isWinner":
reply = gameView.isWinner((LobbyPlayer) args[0]);
doReply = true;
break;
case "getGamesWonBy":
reply = gameView.getGamesWonBy((LobbyPlayer) args[0]);
doReply = true;
break;
case "getDeck":
reply = gameView.getDeck((String) args[0]);
doReply = true;
break;
case "getAnteResult":
reply = gameView.getAnteResult((PlayerView) args[0]);
doReply = true;
break;
default:
System.err.println(String.format("Unknown incoming client command %s", event.getMethod()));
break;
}
if (doReply) {
client.send(new ReplyEvent(event.getId(), reply));
}
}
});
}
}
@Override
@@ -158,8 +372,7 @@ public final class FServerManager {
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
final RemoteClient client = new RemoteClient(ctx.channel());
clients.put(ctx.channel(), client);
games.get(0).addClient(client);
System.out.println("User connected to server at " + ctx.channel().remoteAddress());
System.out.println("Client connected to server at " + ctx.channel().remoteAddress());
updateLobbyState();
super.channelActive(ctx);
}
@@ -168,47 +381,45 @@ public final class FServerManager {
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
final RemoteClient client = clients.get(ctx.channel());
if (msg instanceof LoginEvent) {
client.setUsername(((LoginEvent) msg).getUsername());
final String username = ((LoginEvent) msg).getUsername();
client.setUsername(username);
broadcast(new MessageEvent(null, String.format("%s joined the room", username)));
updateLobbyState();
} else if (msg instanceof RegisterDeckEvent) {
games.get(0).registerDeck(client, ((RegisterDeckEvent) msg).getDeck());
} else if (msg instanceof UpdateLobbyPlayerEvent) {
localLobby.applyToSlot(client.getIndex(), (UpdateLobbyPlayerEvent) msg);
}
super.channelRead(ctx, msg);
}
}
private class ToLobbyListenersHandler extends ChannelInboundHandlerAdapter {
private class LobbyInputHandler extends ChannelInboundHandlerAdapter {
@Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
final RemoteClient client = clients.get(ctx.channel());
if (msg instanceof LoginEvent) {
final LoginEvent event = (LoginEvent) msg;
final int index = localLobby.login(client);
final int index = localLobby.connectPlayer(event.getUsername(), event.getAvatarIndex());
if (index == -1) {
ctx.close();
} else {
client.setIndex(index);
broadcast(event);
updateLobbyState();
}
} else if (msg instanceof UpdateLobbyPlayerEvent) {
updateSlot(client.getIndex(), (UpdateLobbyPlayerEvent) msg);
} else if (msg instanceof MessageEvent) {
final MessageEvent event = (MessageEvent) msg;
broadcast(event);
lobbyListener.message(event.getSource(), event.getMessage());
}
super.channelRead(ctx, msg);
}
@Override public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
final RemoteClient client = clients.get(ctx.channel());
localLobby.logout(client);
updateLobbyState();
super.channelInactive(ctx);
}
}
private class DeregisterClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
final RemoteClient client = clients.remove(ctx.channel());
// TODO remove client from games
localLobby.disconnectPlayer(client.getIndex());
broadcast(new LogoutEvent(client.getUsername()));
super.channelInactive(ctx);
}

View File

@@ -0,0 +1,28 @@
package forge.net;
import forge.match.GameLobby.GameLobbyData;
import forge.net.game.NetEvent;
import forge.net.game.server.RemoteClient;
public class LobbyUpdateEvent implements NetEvent {
private static final long serialVersionUID = 7114918637727047985L;
private final GameLobbyData state;
private int slot;
public LobbyUpdateEvent(final GameLobbyData state) {
this.state = state;
}
@Override
public void updateForClient(final RemoteClient client) {
this.slot = client.getIndex();
}
public GameLobbyData getState() {
return state;
}
public int getSlot() {
return slot;
}
}

View File

@@ -1,69 +0,0 @@
package forge.net;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.LobbyPlayerAi;
import forge.deck.Deck;
import forge.game.GameRules;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IGuiGame;
import forge.match.HostedMatch;
import forge.match.NetGuiGame;
import forge.net.game.server.RemoteClient;
import forge.player.LobbyPlayerHuman;
public final class NetGame {
private final Map<RemoteClient, NetPlayer> clients = Maps.newHashMap();
private final GameRules rules;
private final HostedMatch match = new HostedMatch();
public NetGame(final GameRules rules) {
this.rules = rules;
}
public void addClient(final RemoteClient client) {
clients.put(client, new NetPlayer(client, new NetGuiGame(client)));
}
public void startMatch() {
final List<RegisteredPlayer> registeredPlayers = Lists.newArrayListWithCapacity(clients.size());
final Map<RegisteredPlayer, IGuiGame> guis = Maps.newHashMap();
for (final NetPlayer np : clients.values()) {
if (np.player == null) {
System.err.println("No deck registered for player " + np.client.getUsername());
return;
}
registeredPlayers.add(np.player);
guis.put(np.player, np.gui);
}
// DEBUG
if (registeredPlayers.size() == 1) {
RegisteredPlayer r = new RegisteredPlayer(new Deck());
registeredPlayers.add(r);
r.setPlayer(new LobbyPlayerAi("AI", new HashMap<String, String>()));
}
match.startMatch(rules, null, registeredPlayers, guis);
}
public void registerDeck(final RemoteClient client, final Deck deck) {
final RegisteredPlayer r = new RegisteredPlayer(deck);
clients.get(client).player = r;
r.setPlayer(new LobbyPlayerHuman(client.getUsername()));
}
private static final class NetPlayer {
private final RemoteClient client;
private RegisteredPlayer player = null;
private final IGuiGame gui;
private NetPlayer(final RemoteClient client, final IGuiGame gui) {
this.client = client;
this.gui = gui;
}
}
}

View File

@@ -0,0 +1,129 @@
package forge.net;
import java.util.List;
import java.util.concurrent.TimeoutException;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGameController;
import forge.match.NextGameDecision;
import forge.net.game.GuiGameEvent;
import forge.net.game.client.IToServer;
import forge.util.ITriggerEvent;
public class NetGameController implements IGameController {
private final IToServer server;
public NetGameController(final IToServer server) {
this.server = server;
}
private String methodName() {
boolean passedFirst = false;
for (final StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (ste.getClassName() == getClass().getName()) {
if (passedFirst) {
return ste.getMethodName();
}
passedFirst = true;
}
}
return null;
}
private void send(final String method, final Object... args) {
server.send(new GuiGameEvent(method, args));
}
@SuppressWarnings("unchecked")
private <T> T sendAndWait(final String method, final Object... args) {
try {
return (T) server.sendAndWait(new GuiGameEvent(method, args));
} catch (final TimeoutException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean useMana(final byte color) {
return sendAndWait(methodName(), color);
}
@Override
public boolean tryUndoLastAction() {
return sendAndWait(methodName());
}
@Override
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
send(methodName(), playerView, triggerEvent);
}
@Override
public boolean selectCard(final CardView cardView, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
return sendAndWait(methodName(), cardView, otherCardViewsToSelect, triggerEvent);
}
@Override
public void selectButtonOk() {
send(methodName());
}
@Override
public void selectButtonCancel() {
send(methodName());
}
@Override
public void selectAbility(final SpellAbilityView sa) {
send(methodName(), sa);
}
@Override
public boolean passPriorityUntilEndOfTurn() {
return sendAndWait(methodName());
}
@Override
public boolean passPriority() {
return sendAndWait(methodName());
}
@Override
public void nextGameDecision(final NextGameDecision decision) {
send(methodName(), decision);
}
@Override
public boolean mayLookAtAllCards() {
// Don't do this over network
return false;
}
public String getActivateDescription(final CardView card) {
return sendAndWait(methodName(), card);
}
@Override
public void concede() {
send(methodName());
}
@Override
public IDevModeCheats cheat() {
return IDevModeCheats.NO_CHEAT;
}
@Override
public boolean canPlayUnlimitedLands() {
return false;
}
@Override
public void alphaStrike() {
send(methodName());
}
}

View File

@@ -0,0 +1,380 @@
package forge.net;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import forge.LobbyPlayer;
import forge.UiCommand;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.item.PaperCard;
import forge.match.AbstractGuiGame;
import forge.match.MatchButtonType;
import forge.net.game.GuiGameEvent;
import forge.net.game.server.IToClient;
import forge.trackable.TrackableCollection;
import forge.util.ITriggerEvent;
public class NetGuiGame extends AbstractGuiGame {
private final IToClient client;
private final Map<MatchButtonType, Map<PlayerView, NetButton>> btns = new EnumMap<MatchButtonType, Map<PlayerView,NetButton>>(MatchButtonType.class);
public NetGuiGame(final IToClient client) {
this.client = client;
for (final MatchButtonType type : MatchButtonType.values()) {
btns.put(type, Maps.<PlayerView, NetButton>newHashMap());
}
}
private String methodName() {
boolean passedFirst = false;
for (final StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (ste.getClassName() == getClass().getName()) {
if (passedFirst) {
return ste.getMethodName();
}
passedFirst = true;
}
}
return null;
}
private void send(final String method, final Object... args) {
client.send(new GuiGameEvent(method, args));
}
@SuppressWarnings("unchecked")
private <T> T sendAndWait(final String method, final Object... args) {
try {
return (T) client.sendAndWait(new GuiGameEvent(method, args));
} catch (final TimeoutException e) {
e.printStackTrace();
}
return null;
}
private void updateGameView() {
send("setGameView", getGameView());
}
@Override
public void setGameView(final GameView gameView) {
super.setGameView(gameView);
updateGameView();
}
@Override
public void openView(final TrackableCollection<PlayerView> myPlayers) {
for (final MatchButtonType type : MatchButtonType.values()) {
btns.get(type).clear();
for (final PlayerView player : myPlayers) {
btns.get(type).put(player, new NetButton(player, type));
}
}
send(methodName(), myPlayers);
updateGameView();
}
@Override
public void afterGameEnd() {
send(methodName());
}
@Override
public void showCombat() {
send(methodName());
}
@Override
public void showPromptMessage(final PlayerView playerView, final String message) {
updateGameView();
send(methodName(), playerView, message);
}
@Override
public boolean stopAtPhase(final PlayerView playerTurn, final PhaseType phase) {
return sendAndWait(methodName(), playerTurn, phase);
}
@Override
public IButton getBtnOK(final PlayerView playerView) {
return btns.get(MatchButtonType.OK).get(playerView);
}
@Override
public IButton getBtnCancel(final PlayerView playerView) {
return btns.get(MatchButtonType.CANCEL).get(playerView);
}
@Override
public void focusButton(final MatchButtonType button) {
send(methodName(), button);
}
@Override
public void flashIncorrectAction() {
send(methodName());
}
@Override
public void updatePhase() {
updateGameView();
send(methodName());
}
@Override
public void updateTurn(final PlayerView player) {
updateGameView();
send(methodName(), player);
}
@Override
public void updatePlayerControl() {
updateGameView();
send(methodName());
}
@Override
public void enableOverlay() {
send(methodName());
}
@Override
public void disableOverlay() {
send(methodName());
}
@Override
public void finishGame() {
send(methodName());
}
@Override
public Object showManaPool(final PlayerView player) {
send(methodName(), player);
return null;
}
@Override
public void hideManaPool(final PlayerView player, final Object zoneToRestore) {
send(methodName(), player, zoneToRestore);
}
@Override
public void updateStack() {
updateGameView();
send(methodName());
}
@Override
public void updateZones(final List<Pair<PlayerView, ZoneType>> zonesToUpdate) {
updateGameView();
send(methodName(), zonesToUpdate);
}
@Override
public void updateSingleCard(final CardView card) {
updateGameView();
send(methodName(), card);
}
@Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
updateGameView();
send(methodName(), manaPoolUpdate);
}
@Override
public void updateLives(final Iterable<PlayerView> livesUpdate) {
updateGameView();
send(methodName(), livesUpdate);
}
@Override
public void setPanelSelection(final CardView hostCard) {
updateGameView();
send(methodName(), hostCard);
}
@Override
public SpellAbilityView getAbilityToPlay(final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
return sendAndWait(methodName(), abilities, triggerEvent);
}
@Override
public Map<CardView, Integer> assignDamage(final CardView attacker, final List<CardView> blockers, final int damage, final GameEntityView defender, final boolean overrideOrder) {
return sendAndWait(methodName(), attacker, blockers, damage, defender, overrideOrder);
}
@Override
public void message(final String message, final String title) {
send(methodName(), message, title);
}
@Override
public void showErrorDialog(final String message, final String title) {
send(methodName(), message, title);
}
@Override
public boolean showConfirmDialog(final String message, final String title, final String yesButtonText, final String noButtonText, final boolean defaultYes) {
return sendAndWait(methodName(), message, title, yesButtonText, noButtonText, defaultYes);
}
@Override
public int showOptionDialog(final String message, final String title, final FSkinProp icon, final String[] options, final int defaultOption) {
return sendAndWait(methodName(), message, title, icon, options, defaultOption);
}
@Override
public int showCardOptionDialog(final CardView card, final String message, final String title, final FSkinProp icon, final String[] options, final int defaultOption) {
return sendAndWait(methodName(), card, message, title, icon, options, defaultOption);
}
@Override
public String showInputDialog(final String message, final String title, final FSkinProp icon, final String initialInput, final String[] inputOptions) {
return sendAndWait(methodName(), message, title, icon, initialInput, inputOptions);
}
@Override
public boolean confirm(final CardView c, final String question, final boolean defaultIsYes, final String[] options) {
return sendAndWait(methodName(), c, question, defaultIsYes, options);
}
@Override
public <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
return sendAndWait(methodName(), message, min, max, choices, selected, display);
}
@Override
public <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax, final List<T> sourceChoices, final List<T> destChoices, final CardView referenceCard, final boolean sideboardingMode) {
return sendAndWait(methodName(), title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, referenceCard, sideboardingMode);
}
@Override
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
return sendAndWait(methodName(), sideboard, main);
}
@Override
public GameEntityView chooseSingleEntityForEffect(final String title, final Collection<? extends GameEntityView> optionList, final DelayedReveal delayedReveal, final boolean isOptional) {
return sendAndWait(methodName(), title, optionList, delayedReveal, isOptional);
}
@Override
public void setCard(final CardView card) {
updateGameView();
send(methodName(), card);
}
@Override
public void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi) {
// TODO Auto-generated method stub
}
@Override
public boolean openZones(final Collection<ZoneType> zones, final Map<PlayerView, Object> players) {
updateGameView();
return sendAndWait(methodName(), zones, players);
}
@Override
public void restoreOldZones(final Map<PlayerView, Object> playersToRestoreZonesFor) {
send(methodName(), playersToRestoreZonesFor);
}
@Override
public boolean isUiSetToSkipPhase(final PlayerView playerTurn, final PhaseType phase) {
return sendAndWait(methodName(), playerTurn, phase);
}
@Override
protected void updateCurrentPlayer(final PlayerView player) {
// TODO Auto-generated method stub
}
private final class NetButton implements IButton {
private String methodName() {
boolean passedFirst = false;
for (final StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (ste.getClassName() == getClass().getName()) {
if (passedFirst) {
return ste.getMethodName();
}
passedFirst = true;
}
}
return null;
}
private final PlayerView playerView;
private final MatchButtonType type;
private NetButton(final PlayerView playerView, final MatchButtonType type) {
this.playerView = playerView;
this.type = type;
}
@Override
public void setEnabled(final boolean b0) {
send("btn_" + methodName(), playerView, type, b0);
}
@Override
public void setVisible(final boolean b0) {
send("btn_" + methodName(), playerView, type, b0);
}
@Override
public void setText(final String text0) {
send("btn_" + methodName(), playerView, type, text0);
}
@Override
public boolean isSelected() {
return sendAndWait("btn_" + methodName(), playerView, type);
}
@Override
public void setSelected(final boolean b0) {
send("btn_" + methodName(), playerView, type, b0);
}
@Override
public boolean requestFocusInWindow() {
return sendAndWait("btn_" + methodName(), playerView, type);
}
@Override
public void setCommand(final UiCommand command0) {
send("btn_" + methodName(), playerView, type, command0);
}
@Override
public void setTextColor(final FSkinProp color) {
send("btn_" + methodName(), playerView, type, color);
}
@Override
public void setTextColor(final int r, final int g, final int b) {
send("btn_" + methodName(), playerView, type, r, g, b);
}
}
}

View File

@@ -0,0 +1,65 @@
package forge.net;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
import forge.AIOption;
import forge.interfaces.IGuiGame;
import forge.match.GameLobby;
import forge.match.LobbySlot;
import forge.net.game.LobbySlotType;
public final class ServerGameLobby extends GameLobby {
public ServerGameLobby() {
super(true);
addSlot(new LobbySlot(LobbySlotType.LOCAL, localName(), localAvatarIndices()[0], 0, true, Collections.<AIOption>emptySet()));
addSlot(new LobbySlot(LobbySlotType.OPEN, null, -1, 1, false, Collections.<AIOption>emptySet()));
}
public int connectPlayer(final String name, final int avatarIndex) {
final int nSlots = getNumberOfSlots();
for (int index = 0; index < nSlots; index++) {
final LobbySlot slot = getSlot(index);
if (slot.getType() == LobbySlotType.OPEN) {
connectPlayer(name, avatarIndex, slot);
return index;
}
}
return -1;
}
private void connectPlayer(final String name, final int avatarIndex, final LobbySlot slot) {
slot.setType(LobbySlotType.REMOTE);
slot.setName(name);
slot.setAvatarIndex(avatarIndex);
updateView();
}
public void disconnectPlayer(final int index) {
final LobbySlot slot = getSlot(index);
slot.setType(LobbySlotType.OPEN);
slot.setName(StringUtils.EMPTY);
updateView();
}
@Override public boolean hasControl() {
return true;
}
@Override public boolean mayEdit(final int index) {
final LobbySlotType type = getSlot(index).getType();
return type != LobbySlotType.REMOTE && type != LobbySlotType.OPEN;
}
@Override public boolean mayControl(final int index) {
return getSlot(index).getType() != LobbySlotType.REMOTE;
}
@Override public boolean mayRemove(final int index) {
return true;
}
@Override public IGuiGame getGui(final int index) {
return FServerManager.getInstance().getGui(index);
}
}

View File

@@ -1,7 +1,10 @@
package forge.player;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import forge.AIOption;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.ai.AiProfileUtil;
@@ -13,8 +16,6 @@ import forge.util.GuiDisplayUtil;
import forge.util.MyRandom;
import forge.util.gui.SOptionPane;
import org.apache.commons.lang3.StringUtils;
public final class GamePlayerUtil {
private GamePlayerUtil() { };
@@ -22,8 +23,8 @@ public final class GamePlayerUtil {
public static final LobbyPlayer getGuiPlayer() {
return guiPlayer;
}
public static final LobbyPlayer getGuiPlayer(String name, int index) {
if (index == 0) {
public static final LobbyPlayer getGuiPlayer(final String name, final boolean writePref) {
if (writePref) {
if (!name.equals(guiPlayer.getName())) {
guiPlayer.setName(name);
FModel.getPreferences().setPref(FPref.PLAYER_NAME, name);
@@ -49,7 +50,7 @@ public final class GamePlayerUtil {
public final static LobbyPlayer createAiPlayer(String name, int avatarIndex) {
return createAiPlayer(name, avatarIndex, null);
}
public final static LobbyPlayer createAiPlayer(String name, int avatarIndex, Map<String, String> options) {
public final static LobbyPlayer createAiPlayer(String name, int avatarIndex, Set<AIOption> options) {
LobbyPlayerAi player = new LobbyPlayerAi(name, options);
// TODO: implement specific AI profiles for quest mode.

View File

@@ -1,128 +0,0 @@
package forge.player;
import java.util.Collections;
import java.util.List;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGameController;
import forge.match.NextGameDecision;
import forge.net.game.client.IToServer;
import forge.trackable.TrackableObject;
import forge.util.ITriggerEvent;
public class NetGameController implements IGameController {
private final IToServer server;
public NetGameController(final IToServer server) {
this.server = server;
}
private void send(final String method) {
send(method, Collections.<TrackableObject>emptySet());
}
private void send(final String method, final TrackableObject object) {
send(method, Collections.singleton(object));
}
private void send(final String method, final Iterable<? extends TrackableObject> objects) {
//server.send(new (method, objects));
}
@Override
public boolean mayLookAtAllCards() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean canPlayUnlimitedLands() {
// TODO Auto-generated method stub
return false;
}
@Override
public void concede() {
send("concede");
}
@Override
public void alphaStrike() {
send("alphaStrike");
}
@Override
public boolean useMana(byte color) {
// TODO Auto-generated method stub
return false;
}
@Override
public void selectButtonOk() {
// TODO Auto-generated method stub
}
@Override
public void selectButtonCancel() {
// TODO Auto-generated method stub
}
@Override
public boolean passPriority() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean passPriorityUntilEndOfTurn() {
// TODO Auto-generated method stub
return false;
}
@Override
public void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent) {
// TODO Auto-generated method stub
}
@Override
public boolean selectCard(CardView cardView,
List<CardView> otherCardViewsToSelect, ITriggerEvent triggerEvent) {
// TODO Auto-generated method stub
return false;
}
@Override
public void selectAbility(SpellAbility sa) {
// TODO Auto-generated method stub
}
@Override
public boolean tryUndoLastAction() {
// TODO Auto-generated method stub
return false;
}
@Override
public IDevModeCheats cheat() {
// TODO Auto-generated method stub
return null;
}
@Override
public void nextGameDecision(NextGameDecision decision) {
// TODO Auto-generated method stub
}
@Override
public String getActivateDescription(CardView card) {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -5,6 +5,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -74,6 +75,7 @@ import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.TargetChoices;
import forge.game.trigger.Trigger;
import forge.game.trigger.WrappedAbility;
@@ -217,8 +219,8 @@ public class PlayerControllerHuman
* Uses GUI to learn which spell the player (human in our case) would like to play
*/
public SpellAbility getAbilityToPlay(final List<SpellAbility> abilities, final ITriggerEvent triggerEvent) {
return getGui().getAbilityToPlay(abilities, triggerEvent);
//return HostedMatch.getController().getAbilityToPlay(abilities, triggerEvent);
final SpellAbilityView resultView = getGui().getAbilityToPlay(SpellAbilityView.getCollection(abilities), triggerEvent);
return getGame().getSpellAbility(resultView);
}
@Override
@@ -389,13 +391,13 @@ public class PlayerControllerHuman
// Human is supposed to read the message and understand from it what to choose
if (optionList.isEmpty()) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return null;
}
if (!isOptional && optionList.size() == 1) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
return Iterables.getFirst(optionList, null);
}
@@ -416,7 +418,7 @@ public class PlayerControllerHuman
if (canUseSelectCardsInput) {
if (delayedReveal != null) {
delayedReveal.reveal(this);
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
InputSelectEntitiesFromList<T> input = new InputSelectEntitiesFromList<T>(this, isOptional ? 0 : 1, 1, optionList);
input.setCancelAllowed(isOptional);
@@ -425,8 +427,10 @@ public class PlayerControllerHuman
return Iterables.getFirst(input.getSelected(), null);
}
final GameEntityView result = getGui().chooseSingleEntityForEffect(title, optionList, delayedReveal, isOptional, this);
endTempShowCards(); //assume tempShow called by GuiBase.getInterface().chooseSingleEntityForEffect
tempShow(optionList);
final GameEntityView result = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList), delayedReveal, isOptional);
endTempShowCards();
if (result instanceof CardView) {
return (T) game.getCard((CardView)result);
}
@@ -563,19 +567,22 @@ public class PlayerControllerHuman
@Override
public void reveal(CardCollectionView cards, ZoneType zone, Player owner, String message) {
reveal(CardView.getCollection(cards), zone, PlayerView.get(owner), message);
}
@Override
public void reveal(Collection<CardView> cards, ZoneType zone, PlayerView owner, String message) {
if (StringUtils.isBlank(message)) {
message = "Looking at cards in {player's} " + zone.name().toLowerCase();
}
else {
} else {
message += "{player's} " + zone.name().toLowerCase();
}
String fm = MessageUtil.formatMessage(message, player, owner);
if (!cards.isEmpty()) {
tempShowCards(cards);
getGui().reveal(fm, CardView.getCollection(cards));
tempShowCards(game.getCardList(cards));
getGui().reveal(fm, cards);
endTempShowCards();
}
else {
} else {
getGui().message(MessageUtil.formatMessage("There are no cards in {player's} " +
zone.name().toLowerCase(), player, owner), fm);
}
@@ -993,15 +1000,16 @@ public class PlayerControllerHuman
return Iterables.getFirst(allTargets, null);
}
final Function<Pair<SpellAbilityStackInstance, GameObject>, String> fnToString = new Function<Pair<SpellAbilityStackInstance, GameObject>, String>() {
@Override
public String apply(Pair<SpellAbilityStackInstance, GameObject> targ) {
final List<Pair<SpellAbilityStackInstance, GameObject>> chosen = getGui().getChoices(saSpellskite.getHostCard().getName(), 1, 1, allTargets, null, new FnTargetToString());
return Iterables.getFirst(chosen, null);
}
private final static class FnTargetToString implements Function<Pair<SpellAbilityStackInstance, GameObject>, String>, Serializable {
private static final long serialVersionUID = -4779137632302777802L;
@Override public String apply(final Pair<SpellAbilityStackInstance, GameObject> targ) {
return targ.getRight().toString() + " - " + targ.getLeft().getStackDescription();
}
};
List<Pair<SpellAbilityStackInstance, GameObject>> chosen = getGui().getChoices(saSpellskite.getHostCard().getName(), 1, 1, allTargets, null, fnToString);
return Iterables.getFirst(chosen, null);
}
@Override
@@ -1352,8 +1360,8 @@ public class PlayerControllerHuman
return inputProxy.selectCard(cardView, otherCardViewsToSelect, triggerEvent);
}
public void selectAbility(final SpellAbility sa) {
inputProxy.selectAbility(sa);
public void selectAbility(final SpellAbilityView sa) {
inputProxy.selectAbility(getGame().getSpellAbility(sa));
}
public void alphaStrike() {

View File

@@ -0,0 +1,41 @@
package forge.net;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.google.common.collect.Maps;
public class ReplyPool {
private final Map<Integer, CompletableFuture<Object>> pool = Maps.newHashMap();
public ReplyPool() {
}
public void initialize(final int index) {
synchronized (pool) {
pool.put(Integer.valueOf(index), new CompletableFuture<Object>());
}
}
public void complete(final int index, final Object value) {
synchronized (pool) {
pool.get(Integer.valueOf(index)).complete(value);
}
}
public Object get(final int index) throws TimeoutException {
final CompletableFuture<Object> future;
synchronized (pool) {
future = pool.get(Integer.valueOf(index));
}
try {
return future.get(1, TimeUnit.MINUTES);
} catch (final InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,35 +1,40 @@
package forge.net.game;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import forge.net.game.server.RemoteClient;
import forge.trackable.TrackableObject;
public final class GuiGameEvent implements NetEvent {
public final class GuiGameEvent implements IdentifiableNetEvent {
private static final long serialVersionUID = 6223690008522514574L;
private static int staticId = 0;
private final int id;
private final String method;
private final Iterable<? extends TrackableObject> objects;
private final Object[] objects;
public GuiGameEvent(final String method, final Iterable<? extends TrackableObject> objects) {
public GuiGameEvent(final String method, final Object ... objects) {
this.id = staticId++;
this.method = method;
this.objects = objects == null ? ImmutableSet.<TrackableObject>of() : objects;
this.objects = objects == null ? new Object[0] : objects;
}
@Override
public String toString() {
return String.format("GuiGameEvent %d: %s (%d args)", id, method, objects.length);
}
@Override
public void updateForClient(final RemoteClient client) {
}
@Override
public int getId() {
return id;
}
public String getMethod() {
return method;
}
public TrackableObject getObject() {
return Iterables.getFirst(objects, null);
}
public Iterable<? extends TrackableObject> getObjects() {
public Object[] getObjects() {
return objects;
}
}

View File

@@ -1,5 +1,8 @@
package forge.net.game;
import java.util.concurrent.TimeoutException;
public interface IRemote {
void send(NetEvent event);
Object sendAndWait(IdentifiableNetEvent event) throws TimeoutException;
}

View File

@@ -0,0 +1,6 @@
package forge.net.game;
import forge.game.IIdentifiable;
public interface IdentifiableNetEvent extends NetEvent, IIdentifiable {
}

View File

@@ -1,51 +0,0 @@
package forge.net.game;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.Lists;
public class LobbyState implements Serializable {
private static final long serialVersionUID = 3899410700896996173L;
private final List<LobbyPlayerData> players = Lists.newArrayList();
private int localPlayer = -1;
public int getLocalPlayer() {
return localPlayer;
}
public void setLocalPlayer(final int localPlayer) {
this.localPlayer = localPlayer;
}
public void addPlayer(final LobbyPlayerData data) {
players.add(data);
}
public List<LobbyPlayerData> getPlayers() {
return Collections.unmodifiableList(players);
}
public final static class LobbyPlayerData implements Serializable {
private static final long serialVersionUID = 8642923786206592216L;
private final String name;
private final int avatarIndex;
private final LobbySlotType type;
public LobbyPlayerData(final String name, final int avatarIndex, final LobbySlotType type) {
this.name = name;
this.avatarIndex = avatarIndex;
this.type = type;
}
public String getName() {
return name;
}
public int getAvatarIndex() {
return avatarIndex;
}
public LobbySlotType getType() {
return type;
}
}
}

View File

@@ -1,21 +0,0 @@
package forge.net.game;
import forge.net.game.server.RemoteClient;
public class LobbyUpdateEvent implements NetEvent {
private static final long serialVersionUID = -3176971304173703949L;
private final LobbyState state;
public LobbyUpdateEvent(final LobbyState state) {
this.state = state;
}
@Override
public void updateForClient(final RemoteClient client) {
state.setLocalPlayer(client.getIndex());
}
public LobbyState getState() {
return state;
}
}

View File

@@ -6,8 +6,10 @@ public class LoginEvent implements NetEvent {
private static final long serialVersionUID = -8865183377417377938L;
private final String username;
public LoginEvent(final String username) {
private final int avatarIndex;
public LoginEvent(final String username, final int avatarIndex) {
this.username = username;
this.avatarIndex = avatarIndex;
}
@Override
@@ -17,4 +19,8 @@ public class LoginEvent implements NetEvent {
public String getUsername() {
return username;
}
public int getAvatarIndex() {
return avatarIndex;
}
}

View File

@@ -1,19 +0,0 @@
package forge.net.game;
import forge.deck.Deck;
import forge.net.game.server.RemoteClient;
public class RegisterDeckEvent implements NetEvent {
private static final long serialVersionUID = -6553476654530937343L;
private final Deck deck;
public RegisterDeckEvent(final Deck deck) {
this.deck = deck;
}
public void updateForClient(final RemoteClient client) {
}
public final Deck getDeck() {
return deck;
}
}

View File

@@ -0,0 +1,31 @@
package forge.net.game;
import java.io.Serializable;
import forge.net.game.server.RemoteClient;
public final class ReplyEvent implements NetEvent {
private static final long serialVersionUID = -2814651319617795386L;
private final int index;
private final Serializable reply;
public ReplyEvent(final int index, final Serializable reply) {
this.index = index;
this.reply = reply;
}
public int getIndex() {
return index;
}
public Object getReply() {
return reply;
}
@Override public void updateForClient(final RemoteClient client) {
}
@Override
public String toString() {
return String.format("Reply (%d): %s", index, reply);
}
}

View File

@@ -0,0 +1,77 @@
package forge.net.game;
import java.util.Collections;
import java.util.Set;
import forge.AIOption;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.net.game.server.RemoteClient;
public final class UpdateLobbyPlayerEvent implements NetEvent {
private static final long serialVersionUID = -5073305607515425968L;
private final LobbySlotType type;
private final String name;
private final int avatarIndex;
private final int team;
private final Boolean isArchenemy;
private final Deck deck;
private final DeckSection section;
private final CardPool cards;
private final Set<AIOption> aiOptions;
public static UpdateLobbyPlayerEvent create(final LobbySlotType type, final String name, final int avatarIndex, final int team, final boolean isArchenemy, final Set<AIOption> aiOptions) {
return new UpdateLobbyPlayerEvent(type, name, avatarIndex, team, isArchenemy, null, null, null, aiOptions);
}
public static UpdateLobbyPlayerEvent deckUpdate(final Deck deck) {
return new UpdateLobbyPlayerEvent(null, null, -1, -1, null, deck, null, null, null);
}
public static UpdateLobbyPlayerEvent deckUpdate(final DeckSection section, final CardPool cards) {
return new UpdateLobbyPlayerEvent(null, null, -1, -1, null, null, section, cards, null);
}
private UpdateLobbyPlayerEvent(final LobbySlotType type, final String name, final int avatarIndex, final int team, final Boolean isArchenemy, final Deck deck, final DeckSection section, final CardPool cards, final Set<AIOption> aiOptions) {
this.type = type;
this.name = name;
this.avatarIndex = avatarIndex;
this.team = team;
this.isArchenemy = isArchenemy;
this.deck = deck;
this.section = section;
this.cards = cards;
this.aiOptions = aiOptions;
}
public void updateForClient(final RemoteClient client) {
}
public LobbySlotType getType() {
return type;
}
public String getName() {
return name;
}
public int getAvatarIndex() {
return avatarIndex;
}
public int getTeam() {
return team;
}
public Boolean getArchenemy() {
return isArchenemy;
}
public Deck getDeck() {
return deck;
}
public DeckSection getSection() {
return section;
}
public CardPool getCards() {
return cards;
}
public Set<AIOption> getAiOptions() {
return aiOptions == null ? null : Collections.unmodifiableSet(aiOptions);
}
}

Some files were not shown because too many files have changed in this diff Show More