From 2853af3a10490e99b6eff9653a70a07819ef79b6 Mon Sep 17 00:00:00 2001 From: Myrd Date: Sat, 3 Jan 2015 19:15:44 +0000 Subject: [PATCH] Support saving/restoring game state that has creature tokens. --- .../src/main/java/forge/ai/GameState.java | 31 +++- .../main/java/forge/ai/ability/TokenAi.java | 7 +- forge-core/src/main/java/forge/ImageKeys.java | 7 + .../game/ability/effects/TokenEffect.java | 9 +- .../java/forge/game/card/CardFactory.java | 132 +++++++++++++++--- 5 files changed, 153 insertions(+), 33 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index c68b7efdd1a..0bc66d4c2b7 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -14,6 +14,7 @@ import forge.game.Game; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; +import forge.game.card.CardFactory; import forge.game.card.CounterType; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -94,7 +95,11 @@ public abstract class GameState { if (newText.length() > 0) { newText.append(";"); } - newText.append(c.getName()); + if (c.isToken()) { + newText.append("t:" + new CardFactory.TokenInfo(c).toString()); + } else { + newText.append(c.getName()); + } if (zoneType == ZoneType.Battlefield) { if (c.isTapped()) { newText.append("|Tapped:True"); @@ -206,8 +211,17 @@ public abstract class GameState { if (life > 0) p.setLife(life, null); for (Entry kv : playerCards.entrySet()) { if (kv.getKey() == ZoneType.Battlefield) { - p.getZone(kv.getKey()).setCards(new ArrayList()); + ArrayList cards = new ArrayList(); for (final Card c : kv.getValue()) { + if (c.isToken()) { + cards.add(c); + } + } + p.getZone(kv.getKey()).setCards(cards); + for (final Card c : kv.getValue()) { + if (c.isToken()) { + continue; + } boolean tapped = c.isTapped(); boolean sickness = c.hasSickness(); p.getZone(ZoneType.Hand).add(c); @@ -237,8 +251,15 @@ public abstract class GameState { for (final String element : data) { final String[] cardinfo = element.trim().split("\\|"); - //System.out.println("paper card " + cardinfo[0]); - final Card c = Card.fromPaperCard(getPaperCard(cardinfo[0]), player); + Card c; + if (cardinfo[0].startsWith("t:")) { + String tokenStr = cardinfo[0].substring(2); + // TODO: Use a version of the API that doesn't return a list (i.e. these shouldn't be affected + // by doubling season, etc). + c = CardFactory.makeToken(CardFactory.TokenInfo.fromString(tokenStr), player).get(0); + } else { + c = Card.fromPaperCard(getPaperCard(cardinfo[0]), player); + } boolean hasSetCurSet = false; for (final String info : cardinfo) { @@ -259,7 +280,7 @@ public abstract class GameState { } } - if (!hasSetCurSet) { + if (!hasSetCurSet && !c.isToken()) { c.setSetCode(c.getMostRecentSet()); } diff --git a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java index 256e72e8cd7..32b8e3c06db 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -368,9 +368,10 @@ public class TokenAi extends SpellAbilityAi { } } final String substitutedName = tokenName.equals("ChosenType") ? host.getChosenType() : tokenName; - final List tokens = CardFactory.makeToken(substitutedName, - imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())), - ai, cost, substitutedTypes, finalPower, finalToughness, tokenKeywords); + final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())); + final CardFactory.TokenInfo tokenInfo = new CardFactory.TokenInfo(substitutedName, imageName, + cost, substitutedTypes, tokenKeywords, finalPower, finalToughness); + final List tokens = CardFactory.makeToken(tokenInfo, ai); // Grant rule changes if (tokenHiddenKeywords != null) { diff --git a/forge-core/src/main/java/forge/ImageKeys.java b/forge-core/src/main/java/forge/ImageKeys.java index ab5d6fdeaaa..886f8335a9b 100644 --- a/forge-core/src/main/java/forge/ImageKeys.java +++ b/forge-core/src/main/java/forge/ImageKeys.java @@ -87,6 +87,13 @@ public class ImageKeys { return ImageKeys.TOKEN_PREFIX + tokenName; } + public static String getTokenImageName(String tokenKey) { + if (!tokenKey.startsWith(ImageKeys.TOKEN_PREFIX)) { + return null; + } + return tokenKey.substring(ImageKeys.TOKEN_PREFIX.length()); + } + public static File getImageFile(String key) { if (StringUtils.isEmpty(key)) { return null; diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java index 93cecaf07b1..14d2493cdec 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java @@ -226,10 +226,11 @@ public class TokenEffect extends SpellAbilityEffect { final boolean imprint = sa.hasParam("ImprintTokens"); for (final Player controller : AbilityUtils.getDefinedPlayers(host, this.tokenOwner, sa)) { for (int i = 0; i < finalAmount; i++) { - final List tokens = CardFactory.makeToken(substitutedName, - imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())), - controller, cost, substitutedTypes, finalPower, finalToughness, this.tokenKeywords); - for(Card tok : tokens) { + final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())); + final CardFactory.TokenInfo tokenInfo = new CardFactory.TokenInfo(substitutedName, imageName, + cost, substitutedTypes, this.tokenKeywords, finalPower, finalToughness); + final List tokens = CardFactory.makeToken(tokenInfo, controller); + for (Card tok : tokens) { if (this.tokenTapped) { tok.setTapped(true); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 92d1ec83c83..1af4754b001 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -22,6 +22,7 @@ import forge.card.CardStateName; import forge.card.CardRules; import forge.card.CardSplitType; import forge.card.CardType; +import forge.card.CardType.CoreType; import forge.card.ICardFace; import forge.card.mana.ManaCost; import forge.game.Game; @@ -45,6 +46,9 @@ import java.util.Arrays; import java.util.List; import java.util.Map.Entry; +import com.google.common.base.Joiner; +import com.google.common.collect.Iterables; + /** *

* AbstractCardFactory class. @@ -604,32 +608,117 @@ public class CardFactory { to.setSVar(sVar, from.getSVar(sVar)); } } + + public static class TokenInfo { + final String name; + final String imageName; + final String manaCost; + final String[] types; + final String[] intrinsicKeywords; + final int basePower; + final int baseToughness; - public static List makeToken(final String name, final String imageName, final Player controller, - final String manaCost, final String[] types, final int basePower, final int baseToughness, - final String[] intrinsicKeywords) { - final List list = new ArrayList(); - final Card c = new Card(controller.getGame().nextCardId(), controller.getGame()); - c.setName(name); - c.setImageKey(ImageKeys.getTokenKey(imageName)); - - // TODO - most tokens mana cost is 0, this needs to be fixed - // c.setManaCost(manaCost); - c.addColor(manaCost); - c.setToken(true); - - for (final String t : types) { - c.addType(t); + public TokenInfo(String name, String imageName, String manaCost, String[] types, + String[] intrinsicKeywords, int basePower, int baseToughness) { + this.name = name; + this.imageName = imageName; + this.manaCost = manaCost; + this.types = types; + this.intrinsicKeywords = intrinsicKeywords; + this.basePower = basePower; + this.baseToughness = baseToughness; } - - c.setBasePower(basePower); - c.setBaseToughness(baseToughness); - + + public TokenInfo(Card c) { + this.name = c.getName(); + this.imageName = ImageKeys.getTokenImageName(c.getImageKey()); + this.manaCost = c.getManaCost().toString(); + this.types = getCardTypes(c); + this.intrinsicKeywords = c.getKeywords().toArray(new String[0]); + this.basePower = c.getBasePower(); + this.baseToughness = c.getBaseToughness(); + } + + private static String[] getCardTypes(Card c) { + ArrayList relevantTypes = new ArrayList(); + for (CoreType t : c.getType().getCoreTypes()) { + relevantTypes.add(t.name()); + } + Iterables.addAll(relevantTypes, c.getType().getSubtypes()); + return relevantTypes.toArray(new String[relevantTypes.size()]); + } + + private Card toCard(Game game) { + final Card c = new Card(game.nextCardId(), game); + c.setName(name); + c.setImageKey(ImageKeys.getTokenKey(imageName)); + + // TODO - most tokens mana cost is 0, this needs to be fixed + // c.setManaCost(manaCost); + c.addColor(manaCost); + c.setToken(true); + + for (final String t : types) { + c.addType(t); + } + + c.setBasePower(basePower); + c.setBaseToughness(baseToughness); + return c; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(name).append(','); + sb.append("P:").append(basePower).append(','); + sb.append("T:").append(baseToughness).append(','); + sb.append("Cost:").append(manaCost).append(','); + sb.append("Types:").append(Joiner.on('-').join(types)).append(','); + sb.append("Keywords:").append(Joiner.on('-').join(intrinsicKeywords)).append(','); + sb.append("Image:").append(imageName); + return sb.toString(); + } + + public static TokenInfo fromString(String str) { + final String[] tokenInfo = str.split(","); + int power = 0; + int toughness = 0; + String manaCost = "0"; + String[] types = null; + String[] keywords = null; + String imageName = null; + for (String info : tokenInfo) { + int index = info.indexOf(':'); + if (index == -1) { + continue; + } + String remainder = info.substring(index + 1); + if (info.startsWith("P:")) { + power = Integer.parseInt(remainder); + } else if (info.startsWith("T:")) { + toughness = Integer.parseInt(remainder); + } else if (info.startsWith("Cost:")) { + manaCost = remainder; + } else if (info.startsWith("Types:")) { + types = remainder.split("-"); + } else if (info.startsWith("Keywords:")) { + keywords = remainder.split("-"); + } else if (info.startsWith("Image:")) { + imageName = remainder; + } + } + return new TokenInfo(tokenInfo[0], imageName, manaCost, types, keywords, power, toughness); + } + } + + public static List makeToken(final TokenInfo tokenInfo, final Player controller) { + final List list = new ArrayList(); + final Card c = tokenInfo.toCard(controller.getGame()); final int multiplier = controller.getTokenDoublersMagnitude(); for (int i = 0; i < multiplier; i++) { Card temp = copyStats(c, controller); - - for (final String kw : intrinsicKeywords) { + + for (final String kw : tokenInfo.intrinsicKeywords) { temp.addIntrinsicKeyword(kw); } temp.setOwner(controller); @@ -640,6 +729,7 @@ public class CardFactory { } return list; } + /** * Copy triggered ability *