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

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
@@ -10,10 +13,9 @@ public class GameLogEntry {
type = type0;
message = messageIn;
}
@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 ");
}
}
@@ -765,13 +766,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
if (sa.hasParam("ShareLandType")) {
for (final Card card : chosenCards) {
fetchList = CardLists.filter(fetchList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.sharesLandTypeWith(card);
}
@Override
public boolean apply(final Card c) {
return c.sharesLandTypeWith(card);
}
});
}
}
@@ -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

@@ -75,11 +75,11 @@ public abstract class Ability extends SpellAbility {
return this.getHostCard().isInPlay() && !this.getHostCard().isFaceDown();
}
public static final Ability PLAY_LAND_SURROGATE = new Ability(null, (Cost)null, null){
@Override
@Override
public boolean canPlay() {
return true; //if this ability is added anywhere, it can be assumed that land can be played
return true; //if this ability is added anywhere, it can be assumed that land can be played
}
@Override
public void resolve() {

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