Commit stage 1 of large game view refactoring

This commit is contained in:
drdev
2014-10-06 04:06:42 +00:00
parent e599aef134
commit afcad4e672
92 changed files with 3490 additions and 6023 deletions

11
.gitattributes vendored
View File

@@ -271,6 +271,7 @@ forge-game/src/main/java/forge/game/GameAction.java svneol=native#text/plain
forge-game/src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain
forge-game/src/main/java/forge/game/GameEndReason.java -text
forge-game/src/main/java/forge/game/GameEntity.java -text
forge-game/src/main/java/forge/game/GameEntityView.java -text
forge-game/src/main/java/forge/game/GameFormat.java -text
forge-game/src/main/java/forge/game/GameLog.java -text
forge-game/src/main/java/forge/game/GameLogEntry.java -text
@@ -281,6 +282,7 @@ forge-game/src/main/java/forge/game/GameOutcome.java -text
forge-game/src/main/java/forge/game/GameRules.java -text
forge-game/src/main/java/forge/game/GameStage.java -text
forge-game/src/main/java/forge/game/GameType.java -text
forge-game/src/main/java/forge/game/GameView.java -text
forge-game/src/main/java/forge/game/GlobalRuleChange.java -text
forge-game/src/main/java/forge/game/IHasGameType.java -text
forge-game/src/main/java/forge/game/IIdentifiable.java -text
@@ -441,12 +443,14 @@ forge-game/src/main/java/forge/game/card/CardPredicates.java svneol=native#text/
forge-game/src/main/java/forge/game/card/CardShields.java -text
forge-game/src/main/java/forge/game/card/CardType.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardUtil.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardView.java -text
forge-game/src/main/java/forge/game/card/CounterType.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/package-info.java -text
forge-game/src/main/java/forge/game/combat/AttackingBand.java -text
forge-game/src/main/java/forge/game/combat/Combat.java svneol=native#text/plain
forge-game/src/main/java/forge/game/combat/CombatLki.java -text
forge-game/src/main/java/forge/game/combat/CombatUtil.java svneol=native#text/plain
forge-game/src/main/java/forge/game/combat/CombatView.java -text
forge-game/src/main/java/forge/game/cost/Cost.java svneol=native#text/plain
forge-game/src/main/java/forge/game/cost/CostAddMana.java -text
forge-game/src/main/java/forge/game/cost/CostChooseCreatureType.java -text
@@ -550,6 +554,7 @@ forge-game/src/main/java/forge/game/player/PlayerActionConfirmMode.java -text
forge-game/src/main/java/forge/game/player/PlayerController.java -text
forge-game/src/main/java/forge/game/player/PlayerOutcome.java -text
forge-game/src/main/java/forge/game/player/PlayerStatistics.java -text
forge-game/src/main/java/forge/game/player/PlayerView.java -text
forge-game/src/main/java/forge/game/player/RegisteredPlayer.java -text
forge-game/src/main/java/forge/game/player/package-info.java svneol=native#text/plain
forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java -text
@@ -585,7 +590,9 @@ forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java svne
forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java svneol=native#text/plain
forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java svneol=native#text/plain
forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java svneol=native#text/plain
forge-game/src/main/java/forge/game/spellability/SpellAbilityView.java -text
forge-game/src/main/java/forge/game/spellability/SpellPermanent.java svneol=native#text/plain
forge-game/src/main/java/forge/game/spellability/StackItemView.java -text
forge-game/src/main/java/forge/game/spellability/TargetChoices.java svneol=native#text/plain
forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java svneol=native#text/plain
forge-game/src/main/java/forge/game/spellability/package-info.java svneol=native#text/plain
@@ -663,6 +670,10 @@ forge-game/src/main/java/forge/game/zone/PlayerZoneBattlefield.java svneol=nativ
forge-game/src/main/java/forge/game/zone/Zone.java -text
forge-game/src/main/java/forge/game/zone/ZoneType.java -text
forge-game/src/main/java/forge/game/zone/package-info.java svneol=native#text/plain
forge-game/src/main/java/forge/trackable/TrackableCollection.java -text
forge-game/src/main/java/forge/trackable/TrackableIndex.java -text
forge-game/src/main/java/forge/trackable/TrackableObject.java -text
forge-game/src/main/java/forge/trackable/TrackableProperty.java -text
forge-game/src/main/java/forge/util/Expressions.java -text
forge-game/src/main/java/forge/util/MessageUtil.java -text
forge-gui-android/.classpath -text

View File

@@ -27,7 +27,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
@@ -1147,11 +1146,14 @@ public class AiController {
List<Card> list = player.getCardsIn(ZoneType.Battlefield);
list = CardLists.filter(list, CardPredicates.Presets.PLANEWALKERS);
List<String> type = card.getType();
final String subtype = type.get(type.size() - 1);
final List<Card> cl = CardLists.getType(list, subtype);
if (!cl.isEmpty()) {
return AiPlayDecision.WouldDestroyOtherPlaneswalker;
for (String type : card.getType()) { //determine planewalker subtype
if (!type.equals("Planeswalker")) {
final List<Card> cl = CardLists.getType(list, type);
if (!cl.isEmpty()) {
return AiPlayDecision.WouldDestroyOtherPlaneswalker;
}
break;
}
}
}
if (card.isType("World")) {

View File

@@ -172,7 +172,7 @@ public class AiCostDecision extends CostDecisionMakerBase implements ICostVisito
}
List<SpellAbility> chosen = new ArrayList<SpellAbility>();
for (SpellAbilityStackInstance si :source.getGame().getStack()) {
SpellAbility sp = si.getSpellAbility().getRootAbility();
SpellAbility sp = si.getSpellAbility(true).getRootAbility();
if (si.getSourceCard().isValid(cost.getType().split(";"), source.getController(), source)) {
chosen.add(sp);
}

View File

@@ -1196,7 +1196,7 @@ public class ComputerUtil {
for (StaticAbility stAb : c.getStaticAbilities()) {
Map<String, String> params = stAb.getMapParams();
if ("Continuous".equals(params.get("Mode")) && params.containsKey("AddKeyword")
&& params.get("AddKeyword").contains("Haste") && c.getEquippingCard() == null) {
&& params.get("AddKeyword").contains("Haste") && c.getEquipping() == null) {
return true;
}
}
@@ -1376,7 +1376,7 @@ public class ComputerUtil {
}
// already regenerated
if (!c.getShield().isEmpty()) {
if (c.getShieldCount() > 0) {
continue;
}
@@ -1431,13 +1431,13 @@ public class ComputerUtil {
if (o instanceof Card) {
final Card c = (Card) o;
final boolean canRemove = (c.getNetDefense() <= dmg)
|| (!c.hasKeyword("Indestructible") && c.getShield().isEmpty() && (dmg >= ComputerUtilCombat.getDamageToKill(c)));
|| (!c.hasKeyword("Indestructible") && c.getShieldCount() == 0 && (dmg >= ComputerUtilCombat.getDamageToKill(c)));
if (!canRemove) {
continue;
}
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll ) {
final boolean cantSave = c.getNetDefense() + defense <= dmg
|| (!c.hasKeyword("Indestructible") && c.getShield().isEmpty() && !grantIndestructible
|| (!c.hasKeyword("Indestructible") && c.getShieldCount() == 0 && !grantIndestructible
&& (dmg >= defense + ComputerUtilCombat.getDamageToKill(c)));
if (cantSave && (tgt == null || !grantShroud)) {
continue;
@@ -1473,7 +1473,7 @@ public class ComputerUtil {
}
// already regenerated
if (!c.getShield().isEmpty()) {
if (c.getShieldCount() > 0) {
continue;
}

View File

@@ -784,9 +784,7 @@ public class ComputerUtilCard {
final Map<String, Integer> map = new HashMap<String, Integer>();
for (final Card c : list) {
final ArrayList<String> typeList = c.getType();
for (final String var : typeList) {
for (final String var : c.getType()) {
if (CardType.isACreatureType(var)) {
if (!map.containsKey(var)) {
map.put(var, 1);
@@ -987,7 +985,7 @@ public class ComputerUtilCard {
Combat currCombat = game.getCombat();
if (currCombat != null && !currCombat.getAllBlockers().isEmpty() && currCombat.getAllBlockers().contains(c)) {
for (Card attacker : currCombat.getAttackersBlockedBy(c)) {
if (attacker.getShield().isEmpty() && ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)) {
if (attacker.getShieldCount() == 0 && ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)) {
List<Card> blockers = currCombat.getBlockers(attacker);
ComputerUtilCard.sortByEvaluateCreature(blockers);
Combat combat = new Combat(ai);

View File

@@ -1978,7 +1978,7 @@ public class ComputerUtilCombat {
final boolean noPrevention) {
final int killDamage = ComputerUtilCombat.getDamageToKill(c);
if (c.hasKeyword("Indestructible") || !c.getShield().isEmpty()) {
if (c.hasKeyword("Indestructible") || c.getShieldCount() > 0) {
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
return maxDamage + 1;
}

View File

@@ -1118,7 +1118,7 @@ public class ComputerUtilMana {
&& replacementEffect.zonesCheck(game.getZoneOf(crd))) {
String repType = crd.getSVar(replacementEffect.getMapParams().get("ManaReplacement"));
if (repType.contains("Chosen")) {
repType = repType.replace("Chosen", MagicColor.toShortString(crd.getChosenColor().get(0)));
repType = repType.replace("Chosen", MagicColor.toShortString(crd.getChosenColors().get(0)));
}
mp.setManaReplaceType(repType);
}

View File

@@ -634,7 +634,7 @@ public class PlayerControllerAi extends PlayerController {
String choice = choices.get(0);
if (game.stack.size() > 1) {
for (SpellAbilityStackInstance si : game.getStack()) {
SpellAbility spell = si.getSpellAbility();
SpellAbility spell = si.getSpellAbility(true);
if (sa != spell) {
String s = ProtectAi.toProtectFrom(spell.getHostCard(), sa);
if (s != null) {
@@ -786,7 +786,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public CardShields chooseRegenerationShield(Card c) {
return Iterables.getFirst(c.getShield(), null);
return Iterables.getFirst(c.getShields(), null);
}
@Override

View File

@@ -326,7 +326,7 @@ public class AnimateAi extends SpellAbilityAi {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(source.getChosenColor());
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(","))));
}

View File

@@ -678,7 +678,7 @@ public class AttachAi extends SpellAbilityAi {
//don't equip a worse creature
if (card.isEquipping()) {
Card oldTarget = card.getEquipping().get(0);
Card oldTarget = card.getEquipping();
if (ComputerUtilCard.evaluateCreature(oldTarget) > ComputerUtilCard.evaluateCreature(newTarget)) {
return false;
}
@@ -990,7 +990,7 @@ public class AttachAi extends SpellAbilityAi {
return null;
}
// Don't fortify if already fortifying
if (attachSource.getFortifyingCard() != null && attachSource.getFortifyingCard().getController() == aiPlayer) {
if (attachSource.getFortifying() != null && attachSource.getFortifying().getController() == aiPlayer) {
return null;
}
@@ -1019,14 +1019,14 @@ public class AttachAi extends SpellAbilityAi {
AiController aic = ((PlayerControllerAi)aiPlayer.getController()).getAi();
if (c != null && attachSource.getType().contains("Equipment")
&& attachSource.getEquippingCard() != null
&& attachSource.getEquippingCard().getController() == aiPlayer) {
if (c.equals(attachSource.getEquippingCard())) {
&& attachSource.isEquipping()
&& attachSource.getEquipping().getController() == aiPlayer) {
if (c.equals(attachSource.getEquipping())) {
// Do not equip if equipping the same card already
return null;
}
boolean uselessCreature = isUselessCreature(aiPlayer, attachSource.getEquippingCard());
boolean uselessCreature = isUselessCreature(aiPlayer, attachSource.getEquipping());
if (aic.getProperty(AiProps.MOVE_EQUIPMENT_TO_BETTER_CREATURES).equals("never")) {
// Do not equip other creatures if the AI profile does not allow moving equipment around
@@ -1330,9 +1330,7 @@ public class AttachAi extends SpellAbilityAi {
return true;
}
ArrayList<String> cardTypes = sa.getHostCard().getType();
if (cardTypes.contains("Equipment") && isUselessCreature(ai, c)) {
if (sa.getHostCard().getType().contains("Equipment") && isUselessCreature(ai, c)) {
// useless to equip a creature that can't attack or block.
return false;
}

View File

@@ -736,7 +736,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
for (Card attacker : attackers) {
List<Card> blockers = currCombat.getBlockers(attacker);
// Save my attacker by bouncing a blocker
if (attacker.getController().equals(ai) && attacker.getShield().isEmpty()
if (attacker.getController().equals(ai) && attacker.getShieldCount() == 0
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
&& !currCombat.getBlockers(attacker).isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(blockers);
@@ -799,7 +799,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
ComputerUtilCard.sortByEvaluateCreature(combatants);
for (final Card c : combatants) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && c.getOwner() == ai && !c.isToken()) {
if (c.getShieldCount() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && c.getOwner() == ai && !c.isToken()) {
sa.getTargets().add(c);
return true;
}

View File

@@ -157,7 +157,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
private Card chooseCardOnStack(SpellAbility sa, Player ai, Game game) {
for (SpellAbilityStackInstance si : game.getStack()) {
final Card source = si.getSourceCard();
final SpellAbility abilityOnStack = si.getSpellAbility();
final SpellAbility abilityOnStack = si.getSpellAbility(true);
if (sa.hasParam("Choices") && !abilityOnStack.getHostCard().isValid(sa.getParam("Choices"), ai, sa.getHostCard())) {
continue;

View File

@@ -287,7 +287,7 @@ public class DamageDealAi extends DamageAiBase {
}
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(humanCreature, dmg, source, false, noPrevention);
if (assignedDamage <= dmg
&& humanCreature.getShield().isEmpty() && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
tcs.add(humanCreature);
tgt.addDividedAllocation(humanCreature, assignedDamage);
lastTgt = humanCreature;

View File

@@ -113,7 +113,7 @@ public class DestroyAi extends SpellAbilityAi {
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return (c.getShield().isEmpty() && !ComputerUtil.canRegenerate(ai, c));
return (c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c));
}
});
}
@@ -237,7 +237,7 @@ public class DestroyAi extends SpellAbilityAi {
preferred = CardLists.filter(preferred, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getShield().isEmpty();
return c.getShieldCount() == 0;
}
});
}

View File

@@ -118,10 +118,11 @@ public class EffectAi extends SpellAbilityAi {
return false;
}
boolean threatened = false;
for (final SpellAbilityStackInstance stackSA : game.getStack()) {
if (!stackSA.isSpell()) { continue; }
if (stackSA.getSpellAbility().getApi() == ApiType.DealDamage) {
final SpellAbility saTargeting = stackSA.getSpellAbility().getSATargetingPlayer();
for (final SpellAbilityStackInstance stackInst : game.getStack()) {
if (!stackInst.isSpell()) { continue; }
SpellAbility stackSpellAbility = stackInst.getSpellAbility(true);
if (stackSpellAbility.getApi() == ApiType.DealDamage) {
final SpellAbility saTargeting = stackSpellAbility.getSATargetingPlayer();
if (saTargeting != null && Iterables.contains(saTargeting.getTargets().getTargetPlayers(), ai)) {
threatened = true;
}

View File

@@ -153,7 +153,7 @@ public class FightAi extends SpellAbilityAi {
return true;
}
if (opponent.hasProtectionFrom(fighter) || !opponent.canBeDestroyed()
|| !opponent.getShield().isEmpty() || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
|| opponent.getShieldCount() > 0 || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
return false;
}
if (fighter.hasKeyword("Deathtouch") || ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetAttack() + pumpAttack) {

View File

@@ -138,7 +138,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return false;
}
} else if (keyword.endsWith("CARDNAME can't be regenerated.")) {
if (!card.getShield().isEmpty()) {
if (card.getShieldCount() > 0) {
return true;
}
if (card.hasKeyword("If CARDNAME would be destroyed, regenerate it.") && combat != null

View File

@@ -99,7 +99,7 @@ public class RegenerateAi extends SpellAbilityAi {
boolean flag = false;
for (final Card c : list) {
if (c.getShield().isEmpty()) {
if (c.getShieldCount() == 0) {
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat);
}
}
@@ -128,7 +128,7 @@ public class RegenerateAi extends SpellAbilityAi {
final List<Card> threatenedTargets = new ArrayList<Card>();
for (final Card c : targetables) {
if (objects.contains(c) && c.getShield().isEmpty() && !ComputerUtil.canRegenerate(ai, c)) {
if (objects.contains(c) && c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c)) {
threatenedTargets.add(c);
}
}
@@ -144,7 +144,7 @@ public class RegenerateAi extends SpellAbilityAi {
ComputerUtilCard.sortByEvaluateCreature(combatants);
for (final Card c : combatants) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
if (c.getShieldCount() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
sa.getTargets().add(c);
chance = true;
break;
@@ -200,7 +200,7 @@ public class RegenerateAi extends SpellAbilityAi {
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
Combat combat = game.getCombat();
for (final Card c : combatants) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
if (c.getShieldCount() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
sa.getTargets().add(c);
return true;
}
@@ -213,7 +213,7 @@ public class RegenerateAi extends SpellAbilityAi {
// choose my best X without regen
if (CardLists.getNotType(compTargetables, "Creature").isEmpty()) {
for (final Card c : combatants) {
if (c.getShield().isEmpty()) {
if (c.getShieldCount() == 0) {
sa.getTargets().add(c);
return true;
}
@@ -223,7 +223,7 @@ public class RegenerateAi extends SpellAbilityAi {
} else {
CardLists.sortByCmcDesc(compTargetables);
for (final Card c : compTargetables) {
if (c.getShield().isEmpty()) {
if (c.getShieldCount() == 0) {
sa.getTargets().add(c);
return true;
}

View File

@@ -61,7 +61,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
for (final Card c : list) {
if (objects.contains(c) && c.getShield().isEmpty()) {
if (objects.contains(c) && c.getShieldCount() == 0) {
numSaved++;
}
}
@@ -70,7 +70,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
final List<Card> combatants = CardLists.filter(list, CardPredicates.Presets.CREATURES);
final Combat combat = game.getCombat();
for (final Card c : combatants) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
if (c.getShieldCount() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
numSaved++;
}
}

View File

@@ -296,7 +296,7 @@ public class TokenAi extends SpellAbilityAi {
for (int i = 0; i < substitutedColors.length; i++) {
if (substitutedColors[i].equals("ChosenColor")) {
// this currently only supports 1 chosen color
substitutedColors[i] = host.getChosenColor().get(0);
substitutedColors[i] = host.getChosenColors().get(0);
}
}
String colorDesc = "";

View File

@@ -83,7 +83,7 @@ public class UnattachAllAi extends SpellAbilityAi {
//don't equip a worse creature
if (card.isEquipping()) {
Card oldTarget = card.getEquipping().get(0);
Card oldTarget = card.getEquipping();
if (ComputerUtilCard.evaluateCreature(oldTarget) > ComputerUtilCard.evaluateCreature(newTarget)) {
return false;
}

View File

@@ -100,6 +100,8 @@ public class Game implements IGameStateObject {
private GameOutcome outcome;
private boolean disableAutoYields;
private final GameView view = new GameView();
@Override
public void loadState(GameStateDeserializer gsd) {
gsd.readObject(rules);
@@ -202,6 +204,10 @@ public class Game implements IGameStateObject {
subscribeToEvents(gameLog.getEventVisitor());
}
public GameView getView() {
return view;
}
/**
* Gets the players who are still fighting to win, in turn order.
*

View File

@@ -354,14 +354,14 @@ public class GameAction {
}
// equipment moving off battlefield
if (copied.isEquipping()) {
final Card equippedCreature = copied.getEquipping().get(0);
final Card equippedCreature = copied.getEquipping();
if (equippedCreature.isInPlay()) {
copied.unEquipCard(equippedCreature);
}
}
// fortifications moving off battlefield
if (copied.isFortifying()) {
final Card fortifiedLand = copied.getFortifying().get(0);
final Card fortifiedLand = copied.getFortifying();
if (fortifiedLand.isInPlay()) {
copied.unFortifyCard(fortifiedLand);
}
@@ -1040,7 +1040,7 @@ public class GameAction {
} // if isFortified()
if (c.isEquipping()) {
final Card equippedCreature = c.getEquipping().get(0);
final Card equippedCreature = c.getEquipping();
if (!equippedCreature.isCreature() || !equippedCreature.isInPlay()
|| !equippedCreature.canBeEquippedBy(c)
|| (equippedCreature.isPhasedOut() && !c.isPhasedOut())
@@ -1056,7 +1056,7 @@ public class GameAction {
} // if isEquipping()
if (c.isFortifying()) {
final Card fortifiedLand = c.getFortifying().get(0);
final Card fortifiedLand = c.getFortifying();
if (!fortifiedLand.isLand() || !fortifiedLand.isInPlay()
|| (fortifiedLand.isPhasedOut() && !c.isPhasedOut())) {
c.unFortifyCard(fortifiedLand);
@@ -1071,11 +1071,6 @@ public class GameAction {
return checkAgain;
}
/**
* TODO: Write javadoc for this method.
* @param c
* @return
*/
private boolean stateBasedAction704_5r(Card c) {
boolean checkAgain = false;
int plusOneCounters = c.getCounters(CounterType.P1P1);
@@ -1334,7 +1329,7 @@ public class GameAction {
}
if (c.canBeShielded() && (!c.isCreature() || c.getNetDefense() > 0)
&& (!c.getShield().isEmpty() || c.hasKeyword("If CARDNAME would be destroyed, regenerate it."))) {
&& (c.getShieldCount() > 0 || c.hasKeyword("If CARDNAME would be destroyed, regenerate it."))) {
c.subtractShield(c.getController().getController().chooseRegenerationShield(c));
c.setDamage(0);
c.tap();
@@ -1483,7 +1478,7 @@ public class GameAction {
final Card persistCard = newCard;
String effect = String.format("AB$ ChangeZone | Cost$ 0 | Defined$ CardUID_%d" +
" | Origin$ Graveyard | Destination$ Battlefield | WithCounters$ M1M1_1",
persistCard.getUniqueNumber());
persistCard.getId());
SpellAbility persistAb = AbilityFactory.getAbility(effect, c);
persistAb.setTrigger(true);
persistAb.setStackDescription(newCard.getName() + " - Returning from Persist");
@@ -1497,7 +1492,7 @@ public class GameAction {
final Card undyingCard = newCard;
String effect = String.format("AB$ ChangeZone | Cost$ 0 | Defined$ CardUID_%d |" +
" Origin$ Graveyard | Destination$ Battlefield | WithCounters$ P1P1_1",
undyingCard.getUniqueNumber());
undyingCard.getId());
SpellAbility undyingAb = AbilityFactory.getAbility(effect, c);
undyingAb.setTrigger(true);
undyingAb.setStackDescription(newCard.getName() + " - Returning from Undying");

View File

@@ -438,4 +438,5 @@ public abstract class GameEntity extends GameObject {
}
public abstract Game getGame();
public abstract GameEntityView<?> getView();
}

View File

@@ -0,0 +1,22 @@
package forge.game;
import forge.trackable.TrackableObject;
public abstract class GameEntityView<E extends Enum<E>> extends TrackableObject<E> {
public static GameEntityView<?> get(GameEntity e) {
return e == null ? null : e.getView();
}
protected GameEntityView(int id0, Class<E> propEnum0) {
super(id0, propEnum0);
}
protected abstract E preventNextDamageProp();
public int getPreventNextDamage() {
return get(preventNextDamageProp());
}
void updatePreventNextDamage(GameEntity e) {
set(preventNextDamageProp(), e.getPreventNextDamageTotalShields());
}
}

View File

@@ -0,0 +1,50 @@
package forge.game;
import java.util.List;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.combat.AttackingBand;
import forge.game.combat.Combat;
import forge.game.combat.CombatView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.StackItemView;
import forge.trackable.TrackableIndex;
public class GameView {
private final TrackableIndex<CardView> cards = new TrackableIndex<CardView>();
private final TrackableIndex<PlayerView> players = new TrackableIndex<PlayerView>();
private final TrackableIndex<SpellAbilityView> spellAbilities = new TrackableIndex<SpellAbilityView>();
private final TrackableIndex<StackItemView> stackItems = new TrackableIndex<StackItemView>();
private CombatView combatView;
public GameView() {
}
public CombatView getCombatView() {
return combatView;
}
public void refreshCombat(Game game) {
final Combat combat = game.getCombat();
if (combat == null) {
combatView = null;
return;
}
combatView = new CombatView();
for (final AttackingBand b : combat.getAttackingBands()) {
if (b == null) continue;
final GameEntity defender = combat.getDefenderByAttacker(b);
final List<Card> blockers = combat.getBlockers(b);
final boolean isBlocked = b.isBlocked() == Boolean.TRUE;
combatView.addAttackingBand(
CardView.getCollection(b.getAttackers()),
GameEntityView.get(defender),
isBlocked ? CardView.getCollection(blockers) : null,
CardView.getCollection(blockers));
}
}
}

View File

@@ -146,7 +146,7 @@ public class StaticEffects implements IGameStateObject {
if (params.containsKey("AddColor")) {
final String colors = params.get("AddColor");
if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColor());
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColors());
} else {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(" & "))));
}
@@ -155,7 +155,7 @@ public class StaticEffects implements IGameStateObject {
if (params.containsKey("SetColor")) {
final String colors = params.get("SetColor");
if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColor());
addColors = CardUtil.getShortColorsString(se.getSource().getChosenColors());
} else {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(" & "))));
}

View File

@@ -89,19 +89,16 @@ public class AbilityUtils {
if (defined.equals("Self")) {
c = hostCard;
}
else if (defined.equals("OriginalHost")) {
c = sa.getRootAbility().getOriginalHost();
}
else if (defined.equals("EffectSource")) {
if (hostCard.isType("Effect")) {
c = AbilityUtils.findEffectRoot(hostCard);
}
}
else if (defined.equals("Equipped")) {
c = hostCard.getEquippingCard();
c = hostCard.getEquipping();
}
else if (defined.equals("Enchanted")) {
@@ -112,7 +109,6 @@ public class AbilityUtils {
c = sa.getRootAbility().getPaidList("Sacrificed").get(0).getEnchantingCard();
}
}
else if (defined.endsWith("OfLibrary")) {
final List<Card> lib = hostCard.getController().getCardsIn(ZoneType.Library);
if (lib.size() > 0) { // TopOfLibrary or BottomOfLibrary
@@ -121,22 +117,26 @@ public class AbilityUtils {
// we don't want this to fall through and return the "Self"
return cards;
}
} else if (defined.equals("Targeted")) {
}
else if (defined.equals("Targeted")) {
final SpellAbility saTargeting = sa.getSATargetingCard();
if (saTargeting != null) {
Iterables.addAll(cards, saTargeting.getTargets().getTargetCards());
}
} else if (defined.equals("ThisTargetedCard")) { // do not add parent targeted
}
else if (defined.equals("ThisTargetedCard")) { // do not add parent targeted
if (sa != null && sa.getTargets() != null) {
Iterables.addAll(cards, sa.getTargets().getTargetCards());
}
} else if (defined.equals("ParentTarget")) {
}
else if (defined.equals("ParentTarget")) {
final SpellAbility parent = sa.getParentTargetingCard();
if (parent != null) {
Iterables.addAll(cards, parent.getTargets().getTargetCards());
}
} else if (defined.startsWith("Triggered") && (sa != null)) {
}
else if (defined.startsWith("Triggered") && (sa != null)) {
final SpellAbility root = sa.getRootAbility();
if (defined.contains("LKICopy")) { //TriggeredCardLKICopy
final Object crd = root.getTriggeringObject(defined.substring(9, 13));
@@ -154,7 +154,8 @@ public class AbilityUtils {
}
}
}
} else if (defined.startsWith("Replaced") && (sa != null)) {
}
else if (defined.startsWith("Replaced") && (sa != null)) {
final SpellAbility root = sa.getRootAbility();
final Object crd = root.getReplacingObject(defined.substring(8));
if (crd instanceof Card) {
@@ -164,7 +165,8 @@ public class AbilityUtils {
cards.add(cardItem);
}
}
} else if (defined.equals("Remembered")) {
}
else if (defined.equals("Remembered")) {
if (hostCard.getRemembered().isEmpty()) {
final Card newCard = game.getCardState(hostCard);
for (final Object o : newCard.getRemembered()) {
@@ -179,7 +181,8 @@ public class AbilityUtils {
cards.add(game.getCardState((Card) o));
}
}
} else if (defined.equals("DirectRemembered")) {
}
else if (defined.equals("DirectRemembered")) {
if (hostCard.getRemembered().isEmpty()) {
final Card newCard = game.getCardState(hostCard);
for (final Object o : newCard.getRemembered()) {
@@ -194,7 +197,8 @@ public class AbilityUtils {
cards.add((Card) o);
}
}
} else if (defined.equals("DelayTriggerRemembered")) {
}
else if (defined.equals("DelayTriggerRemembered")) {
if (sa.getRootAbility().isTrigger()) {
for (Object o : sa.getRootAbility().getTriggerRemembered()) {
if (o instanceof Card) {
@@ -202,20 +206,24 @@ public class AbilityUtils {
}
}
}
} else if (defined.equals("FirstRemembered")) {
}
else if (defined.equals("FirstRemembered")) {
Object o = Iterables.getFirst(hostCard.getRemembered(), null);
if (o != null && o instanceof Card) {
cards.add(game.getCardState((Card) o));
}
} else if (defined.equals("Clones")) {
}
else if (defined.equals("Clones")) {
for (final Card clone : hostCard.getClones()) {
cards.add(game.getCardState(clone));
}
} else if (defined.equals("Imprinted")) {
}
else if (defined.equals("Imprinted")) {
for (final Card imprint : hostCard.getImprinted()) {
cards.add(game.getCardState(imprint));
}
} else if (defined.startsWith("ThisTurnEntered")) {
}
else if (defined.startsWith("ThisTurnEntered")) {
final String[] workingCopy = defined.split("_");
ZoneType destination, origin;
String validFilter;
@@ -231,7 +239,8 @@ public class AbilityUtils {
for (final Card cl : CardUtil.getThisTurnEntered(destination, origin, validFilter, hostCard)) {
cards.add(game.getCardState(cl));
}
} else if (defined.equals("ChosenCard")) {
}
else if (defined.equals("ChosenCard")) {
for (final Card chosen : hostCard.getChosenCard()) {
cards.add(game.getCardState(chosen));
}
@@ -239,11 +248,12 @@ public class AbilityUtils {
else if (defined.startsWith("CardUID_")) {
String idString = defined.substring(8);
for (final Card cardByID : game.getCardsInGame()) {
if (cardByID.getUniqueNumber() == Integer.valueOf(idString)) {
if (cardByID.getId() == Integer.valueOf(idString)) {
cards.add(game.getCardState(cardByID));
}
}
} else {
}
else {
List<Card> list = null;
if (defined.startsWith("SacrificedCards")) {
list = sa.getRootAbility().getPaidList("SacrificedCards");
@@ -251,50 +261,42 @@ public class AbilityUtils {
else if (defined.startsWith("Sacrificed")) {
list = sa.getRootAbility().getPaidList("Sacrificed");
}
else if (defined.startsWith("DiscardedCards")) {
list = sa.getRootAbility().getPaidList("DiscardedCards");
}
else if (defined.startsWith("Discarded")) {
list = sa.getRootAbility().getPaidList("Discarded");
}
else if (defined.startsWith("ExiledCards")) {
list = sa.getRootAbility().getPaidList("ExiledCards");
}
else if (defined.startsWith("Exiled")) {
list = sa.getRootAbility().getPaidList("Exiled");
}
else if (defined.startsWith("TappedCards")) {
list = sa.getRootAbility().getPaidList("TappedCards");
}
else if (defined.startsWith("Tapped")) {
list = sa.getRootAbility().getPaidList("Tapped");
}
else if (defined.startsWith("UntappedCards")) {
list = sa.getRootAbility().getPaidList("UntappedCards");
}
else if (defined.startsWith("Untapped")) {
list = sa.getRootAbility().getPaidList("Untapped");
}
else if (defined.startsWith("Valid ")) {
String validDefined = defined.substring("Valid ".length());
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), validDefined.split(","), hostCard.getController(), hostCard);
}
else if (defined.startsWith("ValidHand ")) {
String validDefined = defined.substring("ValidHand ".length());
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Hand), validDefined.split(","), hostCard.getController(), hostCard);
}
else if (defined.startsWith("ValidAll ")) {
String validDefined = defined.substring("ValidAll ".length());
list = CardLists.getValidCards(game.getCardsInGame(), validDefined.split(","), hostCard.getController(), hostCard);
}
else {
return cards;
}
@@ -307,7 +309,6 @@ public class AbilityUtils {
if (c != null) {
cards.add(c);
}
return cards;
}
@@ -1151,7 +1152,7 @@ public class AbilityUtils {
for (SpellAbility targetSpell : saTargeting.getTargets().getTargetSpells()) {
SpellAbilityStackInstance stackInstance = game.getStack().getInstanceFromSpellAbility(targetSpell);
if (stackInstance != null) {
SpellAbility instanceSA = stackInstance.getSpellAbility();
SpellAbility instanceSA = stackInstance.getSpellAbility(true);
if (instanceSA != null) {
sas.add(instanceSA);
}

View File

@@ -89,7 +89,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(host.getChosenColor());
tmpDesc = CardUtil.getShortColorsString(host.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(","))));
}

View File

@@ -109,7 +109,7 @@ public class AnimateEffect extends AnimateEffectBase {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(source.getChosenColor());
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(","))));
}

View File

@@ -50,21 +50,21 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
// Redirect rules read 'you MAY choose new targets' ... okay!
// TODO: Don't even ask to change targets, if the SA and subs don't actually have targets
boolean isOptional = sa.hasParam("Optional");
if( isOptional && !chooser.getController().confirmAction(sa, null, "Do you want to change targets of " + tgtSA.getHostCard() + "?"))
if (isOptional && !chooser.getController().confirmAction(sa, null, "Do you want to change targets of " + tgtSA.getHostCard() + "?")) {
continue;
if( changesOneTarget ) {
}
if (changesOneTarget) {
// 1. choose a target of target spell
List<Pair<SpellAbilityStackInstance, GameObject>> allTargets = new ArrayList<>();
while(changingTgtSI != null) {
SpellAbility changedSa = changingTgtSI.getSpellAbility();
if(changedSa.usesTargeting()) {
SpellAbility changedSa = changingTgtSI.getSpellAbility(true);
if (changedSa.usesTargeting()) {
for(GameObject it : changedSa.getTargets().getTargets())
allTargets.add(ImmutablePair.of(changingTgtSI, it));
}
changingTgtSI = changingTgtSI.getSubInstance();
}
if( allTargets.isEmpty() ) {
if (allTargets.isEmpty()) {
// is it an error or not?
System.err.println("Player managed to target a spell without targets with Spellskite's ability.");
return;
@@ -80,28 +80,33 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
replaceIn.updateTarget(newTargetBlock);
// 3. test if updated choices would be correct.
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa), null);
if(replaceIn.getSpellAbility().canTarget(newTarget)) {
if (replaceIn.getSpellAbility(true).canTarget(newTarget)) {
newTargetBlock.add(newTarget);
replaceIn.updateTarget(newTargetBlock);
} else
}
else {
replaceIn.updateTarget(oldTargetBlock);
} else {
}
}
else {
while(changingTgtSI != null) {
SpellAbility changingTgtSA = changingTgtSI.getSpellAbility();
SpellAbility changingTgtSA = changingTgtSI.getSpellAbility(true);
if (sa.hasParam("RandomTarget")){
changingTgtSA.resetTargets();
List<GameEntity> candidates = changingTgtSA.getTargetRestrictions().getAllCandidates(changingTgtSA, true);
GameEntity choice = Aggregates.random(candidates);
changingTgtSA.getTargets().add(choice);
changingTgtSI.updateTarget(changingTgtSA.getTargets());
} else if (sa.hasParam("DefinedMagnet")){
}
else if (sa.hasParam("DefinedMagnet")){
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
if(changingTgtSA.canTarget(newTarget)) {
if (changingTgtSA.canTarget(newTarget)) {
changingTgtSA.resetTargets();
changingTgtSA.getTargets().add(newTarget);
changingTgtSI.updateTarget(changingTgtSA.getTargets());
}
} else {
}
else {
// Update targets, with a potential new target
TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseNewTargetsFor(changingTgtSA);
if (null != newTarget) {

View File

@@ -471,14 +471,14 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
tgtC.enchantEntity(attachedTo);
} else if (tgtC.isEquipment()) { //Equipment
if (tgtC.isEquipping()) {
final Card oldEquiped = tgtC.getEquippingCard();
final Card oldEquiped = tgtC.getEquipping();
if ( null != oldEquiped )
tgtC.unEquipCard(oldEquiped);
}
tgtC.equipCard(attachedTo);
} else { // fortification
if (tgtC.isFortifying()) {
final Card oldFortified = tgtC.getFortifyingCard();
final Card oldFortified = tgtC.getFortifying();
if( oldFortified != null )
tgtC.unFortifyCard(oldFortified);
}
@@ -856,7 +856,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
else if (c.isEquipment()) { //Equipment
if (c.isEquipping()) {
final Card oldEquiped = c.getEquippingCard();
final Card oldEquiped = c.getEquipping();
if ( null != oldEquiped )
c.unEquipCard(oldEquiped);
}
@@ -864,7 +864,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
else {
if (c.isFortifying()) {
final Card oldFortified = c.getFortifyingCard();
final Card oldFortified = c.getFortifying();
if ( null != oldFortified )
c.unFortifyCard(oldFortified);
}

View File

@@ -68,7 +68,7 @@ public class ChooseColorEffect extends SpellAbilityEffect {
if (chosenColors.isEmpty()) {
return;
}
card.setChosenColor(chosenColors);
card.setChosenColors(chosenColors);
p.getGame().getAction().nofityOfValue(sa, card, p.getName() + " picked " + Lang.joinHomogenous(chosenColors), p);
}
}

View File

@@ -61,8 +61,9 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
stackSources.add(stackinst.getSourceCard());
}
// Get the list of cards that are referenced by effects on the stack
if (null != stackinst.getSpellAbility().getTriggeringObjects()) {
for (Object c : stackinst.getSpellAbility().getTriggeringObjects().values()) {
SpellAbility siSpellAbility = stackinst.getSpellAbility(true);
if (siSpellAbility.getTriggeringObjects() != null) {
for (Object c : siSpellAbility.getTriggeringObjects().values()) {
if (c instanceof Card) {
if (!stackSources.contains((Card) c)) {
referencedSources.add((Card) c);
@@ -70,12 +71,12 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
}
}
}
if (null != stackinst.getSpellAbility().getTargetCard()) {
referencedSources.add(stackinst.getSpellAbility().getTargetCard());
if (siSpellAbility.getTargetCard() != null) {
referencedSources.add(siSpellAbility.getTargetCard());
}
// TODO: is this necessary?
if (null != stackinst.getSpellAbility().getReplacingObjects()) {
for (Object c : stackinst.getSpellAbility().getReplacingObjects().values()) {
if (siSpellAbility.getReplacingObjects() != null) {
for (Object c : siSpellAbility.getReplacingObjects().values()) {
if (c instanceof Card) {
if (!stackSources.contains((Card) c)) {
referencedSources.add((Card) c);
@@ -85,7 +86,6 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
}
}
if (sa.hasParam("Choices")) {
permanentSources = CardLists.getValidCards(permanentSources, sa.getParam("Choices"), host.getController(), host);

View File

@@ -106,12 +106,12 @@ public class CloneEffect extends SpellAbilityEffect {
tgtCard.clearStates(CardCharacteristicName.Cloner);
}
// add "Cloner" state to clone
tgtCard.addAlternateState(CardCharacteristicName.Cloner);
tgtCard.addAlternateState(CardCharacteristicName.Cloner, false);
tgtCard.switchStates(CardCharacteristicName.Original, CardCharacteristicName.Cloner);
tgtCard.setState(CardCharacteristicName.Original);
} else {
//copy Original state to Cloned
tgtCard.addAlternateState(CardCharacteristicName.Cloned);
tgtCard.addAlternateState(CardCharacteristicName.Cloned, false);
tgtCard.switchStates(CardCharacteristicName.Original, CardCharacteristicName.Cloned);
if (tgtCard.isFlipCard()) {
tgtCard.setState(CardCharacteristicName.Original);
@@ -285,7 +285,7 @@ public class CloneEffect extends SpellAbilityEffect {
if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
shortColors = CardUtil.getShortColorsString(tgtCard.getChosenColor());
shortColors = CardUtil.getShortColorsString(tgtCard.getChosenColors());
} else {
shortColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(",")));
}

View File

@@ -37,7 +37,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
for (final Card c : getDefinedCards(sa)) {
sb.append(" ");
if (c.isFaceDown()) {
sb.append("Face-down creature (").append(c.getUniqueNumber()).append(')');
sb.append("Face-down creature (").append(c.getId()).append(')');
} else {
sb.append(c);
}

View File

@@ -26,7 +26,7 @@ public class CounterEffect extends SpellAbilityEffect {
if (sa.hasParam("AllType")) {
sas = new ArrayList<SpellAbility>();
for (SpellAbilityStackInstance si : game.getStack()) {
SpellAbility spell = si.getSpellAbility();
SpellAbility spell = si.getSpellAbility(true);
if (sa.getParam("AllType").equals("Spell") && !spell.isSpell()) {
continue;
}
@@ -71,7 +71,7 @@ public class CounterEffect extends SpellAbilityEffect {
if (sa.hasParam("AllType")) {
sas = new ArrayList<SpellAbility>();
for (SpellAbilityStackInstance si : game.getStack()) {
SpellAbility spell = si.getSpellAbility();
SpellAbility spell = si.getSpellAbility(true);
if (sa.getParam("AllType").equals("Spell") && !spell.isSpell()) {
continue;
}

View File

@@ -104,7 +104,7 @@ public class DamagePreventEffect extends SpellAbilityEffect {
effTgts = AbilityUtils.getDefinedObjects(host, sa.getParam("ShieldEffectTarget"), sa);
for (final Object effTgt : effTgts) {
if (effTgt instanceof Card) {
effTgtString = String.valueOf(((Card) effTgt).getUniqueNumber());
effTgtString = String.valueOf(((Card) effTgt).getId());
effectMap.put("ShieldEffectTarget", "CardUID_" + effTgtString);
} else if (effTgt instanceof Player) {
effTgtString = ((Player) effTgt).getName();
@@ -131,7 +131,7 @@ public class DamagePreventEffect extends SpellAbilityEffect {
effTgts = AbilityUtils.getDefinedObjects(host, sa.getParam("ShieldEffectTarget"), sa);
for (final Object effTgt : effTgts) {
if (effTgt instanceof Card) {
effTgtString = String.valueOf(((Card) effTgt).getUniqueNumber());
effTgtString = String.valueOf(((Card) effTgt).getId());
effectMap.put("ShieldEffectTarget", "CardUID_" + effTgtString);
} else if (effTgt instanceof Player) {
effTgtString = ((Player) effTgt).getName();

View File

@@ -32,7 +32,7 @@ public class DestroyEffect extends SpellAbilityEffect {
while (it.hasNext()) {
final Card tgtC = it.next();
if (tgtC.isFaceDown()) {
sb.append("Morph ").append("(").append(tgtC.getUniqueNumber()).append(")");
sb.append("Morph ").append("(").append(tgtC.getId()).append(")");
} else {
sb.append(tgtC);
}

View File

@@ -189,8 +189,8 @@ public class EffectEffect extends SpellAbilityEffect {
}
// Set Chosen Color(s)
if (!hostCard.getChosenColor().isEmpty()) {
eff.setChosenColor(hostCard.getChosenColor());
if (!hostCard.getChosenColors().isEmpty()) {
eff.setChosenColors(hostCard.getChosenColors());
}
// Set Chosen name

View File

@@ -75,7 +75,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
}
String imageFileName = game.getRules().canCloneUseTargetsImage ? source.getImageKey() : random.getImageKey();
source.addAlternateState(CardCharacteristicName.Cloner);
source.addAlternateState(CardCharacteristicName.Cloner, false);
source.switchStates(CardCharacteristicName.Original, CardCharacteristicName.Cloner);
source.setState(CardCharacteristicName.Original);
CardCharacteristicName stateToCopy = random.getCurState();

View File

@@ -56,7 +56,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
game.getAction().nofityOfValue(sa, choser, Lang.joinHomogenous(gains), choser);
} else {
if (sa.getParam("Gains").equals("ChosenColor")) {
for (final String color : host.getChosenColor()) {
for (final String color : host.getChosenColors()) {
gains.add(color.toLowerCase());
}
} else if (sa.getParam("Gains").equals("TargetedCardColor")) {

View File

@@ -114,7 +114,7 @@ public class ProtectEffect extends SpellAbilityEffect {
game.getAction().nofityOfValue(sa, choser, Lang.joinHomogenous(gains), choser);
} else {
if (sa.getParam("Gains").equals("ChosenColor")) {
for (final String color : host.getChosenColor()) {
for (final String color : host.getChosenColors()) {
gains.add(color.toLowerCase());
}
} else {

View File

@@ -209,7 +209,7 @@ public class PumpEffect extends SpellAbilityEffect {
if (defined.equals("ChosenType")) {
replaced = host.getChosenType();
} else if (defined.equals("CardUIDSource")) {
replaced = "CardUID_" + String.valueOf(host.getUniqueNumber());
replaced = "CardUID_" + String.valueOf(host.getId());
}
for (int i = 0; i < keywords.size(); i++) {
keywords.set(i, keywords.get(i).replaceAll(defined, replaced));

View File

@@ -73,7 +73,7 @@ public class RegenerateEffect extends SpellAbilityEffect {
} else if (sa.hasParam("ReplaceCardUID")) { // Debt of Loyalty
String def = sa.getParam("ReplaceCardUID");
List<Card> replaced = AbilityUtils.getDefinedCards(sourceCard, def, sa);
abString = abString.replace(def, replaced.isEmpty() ? "" : Integer.toString(replaced.get(0).getUniqueNumber()));
abString = abString.replace(def, replaced.isEmpty() ? "" : Integer.toString(replaced.get(0).getId()));
}
triggerSA = AbilityFactory.getAbility(abString, sourceCard);
triggerSA.setActivatingPlayer(sa.getActivatingPlayer());

View File

@@ -28,7 +28,7 @@ public class SetStateEffect extends SpellAbilityEffect {
while (it.hasNext()) {
final Card tgtC = it.next();
if (tgtC.isFaceDown()) {
sb.append("Morph ").append("(").append(tgtC.getUniqueNumber()).append(")");
sb.append("Morph ").append("(").append(tgtC.getId()).append(")");
} else {
sb.append(tgtC);
}

View File

@@ -170,7 +170,7 @@ public class TokenEffect extends SpellAbilityEffect {
for (int i = 0; i < substitutedColors.length; i++) {
if (substitutedColors[i].equals("ChosenColor")) {
// this currently only supports 1 chosen color
substitutedColors[i] = host.getChosenColor().get(0);
substitutedColors[i] = host.getChosenColors().get(0);
}
}
String colorDesc = "";

View File

@@ -24,11 +24,11 @@ public class UnattachAllEffect extends SpellAbilityEffect {
//AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl);
} else if (cardToUnattach.isEquipment()) {
if (cardToUnattach.isEquipping() && c.getEquippedBy().contains(cardToUnattach)) {
cardToUnattach.unEquipCard(cardToUnattach.getEquipping().get(0));
cardToUnattach.unEquipCard(cardToUnattach.getEquipping());
}
} else if (cardToUnattach.isFortification()) {
if (cardToUnattach.isFortifying() && c.getFortifiedBy().contains(cardToUnattach)) {
cardToUnattach.unFortifyCard(cardToUnattach.getFortifying().get(0));
cardToUnattach.unFortifyCard(cardToUnattach.getFortifying());
}
}
} else if (o instanceof Player) {

View File

@@ -33,11 +33,11 @@ public class UnattachEffect extends SpellAbilityEffect {
//AbilityFactoryAttach.handleUnattachAura(cardToUnattach, c, gainControl);
} else if (cardToUnattach.isEquipment()) {
if (cardToUnattach.isEquipping()) {
cardToUnattach.unEquipCard(cardToUnattach.getEquipping().get(0));
cardToUnattach.unEquipCard(cardToUnattach.getEquipping());
}
} else if (cardToUnattach.isFortification()) {
if (cardToUnattach.isFortifying()) {
cardToUnattach.unFortifyCard(cardToUnattach.getFortifying().get(0));
cardToUnattach.unFortifyCard(cardToUnattach.getFortifying());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -31,8 +31,10 @@ import forge.game.trigger.Trigger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* TODO: Write javadoc for this type.
@@ -40,7 +42,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public class CardCharacteristics {
private String name = "";
private List<String> type = new CopyOnWriteArrayList<String>();
private Set<String> type = new CopyOnWriteArraySet<String>();
private ManaCost manaCost = ManaCost.NO_COST;
private List<CardColor> cardColor = new ArrayList<CardColor>();
private String oracleText = "";
@@ -84,7 +86,7 @@ public class CardCharacteristics {
*
* @return the type
*/
public final List<String> getType() {
public final Set<String> getType() {
return this.type;
}
@@ -438,7 +440,7 @@ public class CardCharacteristics {
// String name : just copy reference
this.name = source.getName();
// ArrayList<String> type : list of String objects so use copy constructor
this.type = new CopyOnWriteArrayList<String>(source.getType());
this.type = new CopyOnWriteArraySet<String>(source.getType());
// CardManaCost manaCost : not sure if a deep copy is needed
this.manaCost = source.getManaCost();
// ArrayList<CardColor> cardColor : not sure if a deep copy is needed

View File

@@ -75,9 +75,9 @@ public class CardFactory {
Card out;
if (!(in.isToken() || in.getCopiedPermanent() != null)) {
out = assignNewId ? getCard(in.getPaperCard(), in.getOwner())
: getCard(in.getPaperCard(), in.getOwner(), in.getUniqueNumber());
: getCard(in.getPaperCard(), in.getOwner(), in.getId());
} else { // token
out = assignNewId ? new Card(in.getGame().nextCardId(), in.getPaperCard()) : new Card(in.getUniqueNumber(), in.getPaperCard());
out = assignNewId ? new Card(in.getGame().nextCardId(), in.getPaperCard()) : new Card(in.getId(), in.getPaperCard());
out = CardFactory.copyStats(in, in.getController());
out.setToken(true);
@@ -148,7 +148,7 @@ public class CardFactory {
String tmp = "";
final String newColor = sourceSA.getParam("CopyIsColor");
if (newColor.equals("ChosenColor")) {
tmp = CardUtil.getShortColorsString(source.getChosenColor());
tmp = CardUtil.getShortColorsString(source.getChosenColors());
} else {
tmp = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(newColor.split(","))));
}
@@ -356,20 +356,19 @@ public class CardFactory {
}
private static Card readCard(final CardRules rules, final IPaperCard paperCard, int cardId) {
final Card card = new Card(cardId, paperCard);
// 1. The states we may have:
CardSplitType st = rules.getSplitType();
if ( st == CardSplitType.Split) {
card.addAlternateState(CardCharacteristicName.LeftSplit);
if (st == CardSplitType.Split) {
card.addAlternateState(CardCharacteristicName.LeftSplit, false);
card.setState(CardCharacteristicName.LeftSplit);
}
readCardFace(card, rules.getMainPart());
if ( st != CardSplitType.None) {
card.addAlternateState(st.getChangedStateName());
if (st != CardSplitType.None) {
card.addAlternateState(st.getChangedStateName(), false);
card.setState(st.getChangedStateName());
readCardFace(card, rules.getOtherPart());
}
@@ -570,7 +569,7 @@ public class CardFactory {
// get CardCharacteristics for desired state
if (!to.getStates().contains(toState)) {
to.addAlternateState(toState);
to.addAlternateState(toState, true);
}
final CardCharacteristics toCharacteristics = to.getState(toState),
fromCharacteristics = from.getState(fromState);
@@ -590,7 +589,7 @@ public class CardFactory {
final CardCharacteristics fromCharacteristics = from.getState(fromState);
final CardCharacteristicName oldToState = to.getCurState();
if (!to.getStates().contains(toState)) {
to.addAlternateState(toState);
to.addAlternateState(toState, false);
}
to.setState(toState);
@@ -688,7 +687,7 @@ public class CardFactory {
t.setTriggeringObjects(trig);
trig.setTriggerRemembered(t.getTriggerRemembered());
if (t.getStoredTriggeredObjects() != null) {
trig.setAllTriggeringObjects(t.getStoredTriggeredObjects());
trig.setTriggeringObjects(t.getStoredTriggeredObjects());
}
trig.setActivatingPlayer(sa.getActivatingPlayer());

View File

@@ -1159,7 +1159,7 @@ public class CardFactoryUtil {
ZoneType sourceZone = sq[0].contains("ChromaInGrave") ? ZoneType.Graveyard : ZoneType.Battlefield;
String colorName = sq[1];
if (colorName.contains("Chosen")) {
colorName = MagicColor.toShortString(c.getChosenColor().get(0));
colorName = MagicColor.toShortString(c.getChosenColors().get(0));
}
final List<Card> cards;
if (sq[0].contains("ChromaSource")) { // Runs Chroma for passed in Source card
@@ -1250,7 +1250,7 @@ public class CardFactoryUtil {
if (sq[0].contains("CardManaCost")) {
Card ce;
if (sq[0].contains("Equipped") && c.isEquipping()) {
ce = c.getEquipping().get(0);
ce = c.getEquipping();
}
else if (sq[0].contains("Remembered")) {
ce = (Card) c.getRemembered().get(0);
@@ -2027,7 +2027,7 @@ public class CardFactoryUtil {
final int magnitude = Integer.parseInt(s);
String description = String.format("Bushido %d (When this blocks or becomes blocked, it gets +%d/+%d until end of turn).", magnitude, magnitude, magnitude);
String regularPart = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ +%d | NumDef$ +%d | StackDescription$ %s", c.getUniqueNumber(), magnitude, magnitude, description);
String regularPart = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ +%d | NumDef$ +%d | StackDescription$ %s", c.getId(), magnitude, magnitude, description);
SpellAbility ability = AbilityFactory.getAbility(regularPart, c);
ability.setDescription(ability.getStackDescription());

View File

@@ -213,14 +213,13 @@ public final class CardUtil {
* @return a copy of C with LastKnownInfo stuff retained.
*/
public static Card getLKICopy(final Card in) {
final Card newCopy = new Card(in.getUniqueNumber(), in.getPaperCard());
final Card newCopy = new Card(in.getId(), in.getPaperCard());
newCopy.setCurSetCode(in.getCurSetCode());
newCopy.setOwner(in.getOwner());
newCopy.setController(in.getController(), 0);
newCopy.getCharacteristics().copyFrom(in.getState(in.getCurState()));
if (in.isCloned()) {
newCopy.addAlternateState(CardCharacteristicName.Cloner);
newCopy.addAlternateState(CardCharacteristicName.Cloner, false);
}
newCopy.setType(new ArrayList<String>(in.getType()));
newCopy.setToken(in.isToken());
@@ -245,9 +244,9 @@ public final class CardUtil {
newCopy.getDamageHistory().setCreatureGotBlockedThisTurn(in.getDamageHistory().getCreatureGotBlockedThisTurn());
newCopy.setEnchanting(in.getEnchanting());
newCopy.setEnchantedBy(new ArrayList<Card> (in.getEnchantedBy()));
newCopy.setEquipping(new ArrayList<Card> (in.getEquipping()));
newCopy.setEquipping(in.getEquipping());
newCopy.setEquippedBy(new ArrayList<Card> (in.getEquippedBy()));
newCopy.setFortifying(new ArrayList<Card> (in.getFortifying()));
newCopy.setFortifying(in.getFortifying());
newCopy.setFortifiedBy(new ArrayList<Card> (in.getFortifiedBy()));
newCopy.setClones(in.getClones());
newCopy.setHaunting(in.getHaunting());

View File

@@ -0,0 +1,625 @@
package forge.game.card;
import java.util.Set;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.card.CardCharacteristicName;
import forge.card.CardRarity;
import forge.card.CardType;
import forge.card.ColorSet;
import forge.card.mana.ManaCost;
import forge.game.GameEntityView;
import forge.game.player.PlayerView;
import forge.game.zone.ZoneType;
import forge.trackable.TrackableCollection;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty.CardProp;
import forge.trackable.TrackableProperty.CardStateProp;
public class CardView extends GameEntityView<CardProp> {
public static CardView get(Card c) {
return c == null ? null : c.getView();
}
public static TrackableCollection<CardView> getCollection(Iterable<Card> cards) {
if (cards == null) {
return null;
}
TrackableCollection<CardView> collection = new TrackableCollection<CardView>();
for (Card c : cards) {
collection.add(c.getView());
}
return collection;
}
public CardView(int id0) {
super(id0, CardProp.class);
set(CardProp.Original, new CardStateView(id0));
}
public PlayerView getOwner() {
return get(CardProp.Owner);
}
void updateOwner(Card c) {
set(CardProp.Owner, PlayerView.get(c.getOwner()));
}
public PlayerView getController() {
return get(CardProp.Controller);
}
void updateController(Card c) {
set(CardProp.Owner, PlayerView.get(c.getController()));
}
public ZoneType getZone() {
return get(CardProp.Zone);
}
void updateZone(Card c) {
set(CardProp.Zone, c.getZone() == null ? null : c.getZone().getZoneType());
}
public boolean isCloned() {
return get(CardProp.Cloned);
}
public boolean isFaceDown() {
return get(CardProp.FaceDown);
}
public boolean isFlipCard() {
return get(CardProp.FlipCard);
}
public boolean isFlipped() {
return get(CardProp.Flipped);
}
public boolean isSplitCard() {
return get(CardProp.SplitCard);
}
public boolean isTransformed() {
return get(CardProp.Transformed);
}
public String getSetCode() {
return get(CardProp.SetCode);
}
void updateSetCode(Card c) {
set(CardProp.SetCode, c.getCurSetCode());
}
public CardRarity getRarity() {
return get(CardProp.Rarity);
}
void updateRarity(Card c) {
set(CardProp.Rarity, c.getRarity());
}
public boolean isAttacking() {
return get(CardProp.Attacking);
}
void updateAttacking(Card c) {
set(CardProp.Attacking, c.getGame().getCombat().isAttacking(c));
}
public boolean isBlocking() {
return get(CardProp.Blocking);
}
void updateBlocking(Card c) {
set(CardProp.Blocking, c.getGame().getCombat().isBlocking(c));
}
public boolean isPhasedOut() {
return get(CardProp.PhasedOut);
}
void updatePhasedOut(Card c) {
set(CardProp.PhasedOut, c.isPhasedOut());
}
public boolean isFirstTurnControlled() {
return get(CardProp.Sickness);
}
public boolean hasSickness() {
return isFirstTurnControlled() && !getOriginal().hasHaste();
}
public boolean isSick() {
return getZone() == ZoneType.Battlefield && hasSickness();
}
void updateSickness(Card c) {
set(CardProp.Sickness, c.isInPlay() && c.isSick());
}
public boolean isTapped() {
return get(CardProp.Tapped);
}
void updateTapped(Card c) {
set(CardProp.Tapped, c.isTapped());
}
public boolean isToken() {
return get(CardProp.Token);
}
void updateToken(Card c) {
set(CardProp.Token, c.isToken());
}
public Map<CounterType, Integer> getCounters() {
return get(CardProp.Counters);
}
void updateCounters(Card c) {
set(CardProp.Counters, c.getCounters());
}
public int getDamage() {
return get(CardProp.Damage);
}
void updateDamage(Card c) {
set(CardProp.Damage, c.getDamage());
}
public int getAssignedDamage() {
return get(CardProp.AssignedDamage);
}
void updateAssignedDamage(Card c) {
set(CardProp.AssignedDamage, c.getTotalAssignedDamage());
}
public int getLethalDamage() {
return getOriginal().getToughness() - getDamage() - getAssignedDamage();
}
public int getShieldCount() {
return get(CardProp.ShieldCount);
}
void updateShieldCount(Card c) {
set(CardProp.ShieldCount, c.getShieldCount());
}
public String getChosenType() {
return get(CardProp.ChosenType);
}
void updateChosenType(Card c) {
set(CardProp.ChosenType, c.getChosenType());
}
public List<String> getChosenColors() {
return get(CardProp.ChosenColors);
}
void updateChosenColors(Card c) {
set(CardProp.ChosenColors, c.getChosenColors());
}
public PlayerView getChosenPlayer() {
return get(CardProp.ChosenPlayer);
}
void updateChosenPlayer(Card c) {
set(CardProp.ChosenPlayer, c.getChosenPlayer());
}
public String getNamedCard() {
return get(CardProp.NamedCard);
}
void updateNamedCard(Card c) {
set(CardProp.NamedCard, c.getNamedCard());
}
public CardView getEquipping() {
return get(CardProp.Equipping);
}
void updateEquipping(Card c) {
set(CardProp.Equipping, CardView.get(c.getEquipping()));
}
public Iterable<CardView> getEquippedBy() {
return get(CardProp.EquippedBy);
}
void updateEquippedBy(Card c) {
set(CardProp.EquippedBy, CardView.getCollection(c.getEquippedBy()));
}
public boolean isEquipped() {
return getEquippedBy() != null;
}
public GameEntityView<?> getEnchanting() {
return get(CardProp.Enchanting);
}
void updateEnchanting(Card c) {
set(CardProp.Owner, GameEntityView.get(c.getEnchanting()));
}
public CardView getEnchantingCard() {
GameEntityView<?> enchanting = getEnchanting();
if (enchanting instanceof CardView) {
return (CardView) enchanting;
}
return null;
}
public PlayerView getEnchantingPlayer() {
GameEntityView<?> enchanting = getEnchanting();
if (enchanting instanceof PlayerView) {
return (PlayerView) enchanting;
}
return null;
}
public Iterable<CardView> getEnchantedBy() {
return get(CardProp.EnchantedBy);
}
void updateEnchantedBy(Card c) {
set(CardProp.EnchantedBy, CardView.getCollection(c.getEnchantedBy()));
}
public boolean isEnchanted() {
return getEnchantedBy() != null;
}
public CardView getFortifying() {
return get(CardProp.Fortifying);
}
void updateFortifying(Card c) {
set(CardProp.Fortifying, c.getFortifying());
}
public Iterable<CardView> getFortifiedBy() {
return get(CardProp.FortifiedBy);
}
void updateFortifiedBy(Card c) {
set(CardProp.FortifiedBy, CardView.getCollection(c.getFortifiedBy()));
}
public boolean isFortified() {
return getFortifiedBy() != null;
}
public Iterable<CardView> getGainControlTargets() {
return get(CardProp.GainControlTargets);
}
void updateGainControlTargets(Card c) {
set(CardProp.GainControlTargets, CardView.getCollection(c.getGainControlTargets()));
}
public CardView getCloneOrigin() {
return get(CardProp.CloneOrigin);
}
void updateCloneOrigin(Card c) {
set(CardProp.CloneOrigin, CardView.get(c.getCloneOrigin()));
}
public Iterable<CardView> getImprinted() {
return get(CardProp.Imprinted);
}
void updateImprinted(Card c) {
set(CardProp.Imprinted, CardView.getCollection(c.getImprinted()));
}
public Iterable<CardView> getHauntedBy() {
return get(CardProp.HauntedBy);
}
void updateHauntedBy(Card c) {
set(CardProp.HauntedBy, CardView.getCollection(c.getHauntedBy()));
}
public CardView getHaunting() {
return get(CardProp.Haunting);
}
void updateHaunting(Card c) {
set(CardProp.Haunting, CardView.get(c.getHaunting()));
}
public Iterable<CardView> getMustBlock() {
return get(CardProp.MustBlock);
}
void updateHaunting(CardView haunting) {
set(CardProp.MustBlock, haunting);
}
public CardView getPairedWith() {
return get(CardProp.PairedWith);
}
void updatePairedWith(Card c) {
set(CardProp.PairedWith, CardView.get(c.getPairedWith()));
}
public CardStateView getOriginal() {
return get(CardProp.Original);
}
public CardStateView getAlternate() {
return get(CardProp.Alternate);
}
public CardStateView getState(final boolean alternate0) {
return alternate0 ? getAlternate() : getOriginal();
}
void updateState(Card c, boolean fromCurStateChange) {
boolean isDoubleFaced = c.isDoubleFaced();
boolean isFaceDown = c.isFaceDown();
boolean isFlipCard = c.isFlipCard();
boolean isFlipped = c.getCurState() == CardCharacteristicName.Flipped;
boolean isSplitCard = c.isSplitCard();
boolean isTransformed = c.getCurState() == CardCharacteristicName.Transformed;
boolean hasAltState = isDoubleFaced || isFlipCard || isSplitCard || (isFaceDown/* && mayShowCardFace*/);
set(CardProp.Alternate, hasAltState ? new CardStateView(getId()) : null);
set(CardProp.Cloned, c.isCloned());
set(CardProp.FaceDown, isFaceDown);
set(CardProp.SplitCard, isSplitCard);
set(CardProp.FlipCard, isFlipCard);
if (fromCurStateChange) {
set(CardProp.Flipped, isFlipped);
set(CardProp.Transformed, isTransformed);
updateRarity(c); //rarity and set based on current state
updateSetCode(c);
}
if (isSplitCard) {
final CardCharacteristicName orig, alt;
if (c.getCurState() == CardCharacteristicName.RightSplit) {
// If right half on stack, place it first
orig = CardCharacteristicName.RightSplit;
alt = CardCharacteristicName.LeftSplit;
}
else {
orig = CardCharacteristicName.LeftSplit;
alt = CardCharacteristicName.RightSplit;
}
updateState(c, getOriginal(), orig);
updateState(c, getAlternate(), alt);
return;
}
final CardStateView origView = getOriginal();
origView.updateName(c);
origView.updateColors(c);
origView.updateImageKey(c);
origView.updateType(c);
origView.updateManaCost(c);
origView.updatePower(c);
origView.updateToughness(c);
origView.updateLoyalty(c);
origView.updateText(c);
origView.updateChangedColorWords(c);
origView.updateChangedTypes(c);
origView.updateManaCost(c);
origView.updateKeywords(c);
origView.updateFoilIndex(c);
if (hasAltState) {
if (isFlipCard && !isFlipped) {
updateState(c, getAlternate(), CardCharacteristicName.Flipped);
}
else if (isDoubleFaced && !isTransformed) {
updateState(c, getAlternate(), CardCharacteristicName.Transformed);
}
else {
updateState(c, getAlternate(), CardCharacteristicName.Original);
}
}
}
private void updateState(Card c, CardStateView view, CardCharacteristicName state) {
final CardCharacteristics chars = c.getState(state);
view.updateName(chars);
view.updateColors(chars);
view.updateImageKey(chars);
view.updateType(chars);
view.updateManaCost(chars);
view.updatePower(chars);
view.updateToughness(chars);
view.updateLoyalty(chars);
view.updateText(chars);
view.updateFoilIndex(chars);
}
@Override
public String toString() {
if (getId() <= 0) { //if fake card, just return name
return getOriginal().getName();
}
/*if (!mayBeShown) {
return "(Unknown card)";
}*/
if (StringUtils.isEmpty(getOriginal().getName())) {
CardStateView alternate = getAlternate();
if (alternate != null) {
return "Face-down card (" + getAlternate().getName() + ")";
}
return "(" + getId() + ")";
}
return getOriginal().getName() + " (" + getId() + ")";
}
public String determineName(final CardStateView state) {
if (state == getOriginal()) {
return toString();
}
return getAlternate().getName() + " (" + getId() + ")";
}
public class CardStateView extends TrackableObject<CardStateProp> {
public CardStateView(int id0) {
super(id0, CardStateProp.class);
}
@Override
public String toString() {
return getCard().determineName(this);
}
public CardView getCard() {
return CardView.this;
}
public String getName() {
return get(CardStateProp.Name);
}
void updateName(Card c) {
set(CardStateProp.Name, c.getName());
}
void updateName(CardCharacteristics c) {
set(CardStateProp.Name, c.getName());
}
public ColorSet getColors() {
return get(CardStateProp.Colors);
}
void updateColors(Card c) {
set(CardStateProp.Colors, c.determineColor());
}
void updateColors(CardCharacteristics c) {
set(CardStateProp.Colors, c.determineColor());
}
public String getImageKey() {
return get(CardStateProp.ImageKey);
}
void updateImageKey(Card c) {
set(CardStateProp.ImageKey, c.getImageKey());
}
void updateImageKey(CardCharacteristics c) {
set(CardStateProp.ImageKey, c.getImageKey());
}
public Set<String> getType() {
return get(CardStateProp.Type);
}
void updateType(Card c) {
set(CardStateProp.Type, c.getType());
}
void updateType(CardCharacteristics c) {
set(CardStateProp.Type, c.getType());
}
public ManaCost getManaCost() {
return get(CardStateProp.ManaCost);
}
void updateManaCost(Card c) {
set(CardStateProp.ManaCost, c.getManaCost());
}
void updateManaCost(CardCharacteristics c) {
set(CardStateProp.ManaCost, c.getManaCost());
}
public int getPower() {
return get(CardStateProp.Power);
}
void updatePower(Card c) {
set(CardStateProp.Power, c.getNetAttack());
}
void updatePower(CardCharacteristics c) {
set(CardStateProp.Power, c.getBaseAttack());
}
public int getToughness() {
return get(CardStateProp.Toughness);
}
void updateToughness(Card c) {
set(CardStateProp.Toughness, c.getNetDefense());
}
void updateToughness(CardCharacteristics c) {
set(CardStateProp.Toughness, c.getBaseDefense());
}
public int getLoyalty() {
return get(CardStateProp.Loyalty);
}
void updateLoyalty(Card c) {
set(CardStateProp.Loyalty, c.getCurrentLoyalty());
}
void updateLoyalty(CardCharacteristics c) {
set(CardStateProp.Loyalty, 0); // Q why is loyalty not a property of CardCharacteristic? A: because no alt states have a base loyalty (only candidate is Garruk Relentless).
}
public String getText() {
return get(CardStateProp.Text);
}
void updateText(Card c) {
set(CardStateProp.Text, c.getText());
}
void updateText(CardCharacteristics c) {
set(CardStateProp.Text, c.getOracleText());
}
public int getFoilIndex() {
return get(CardStateProp.FoilIndex);
}
void updateFoilIndex(Card c) {
set(CardStateProp.FoilIndex, c.getCharacteristics().getFoil());
}
void updateFoilIndex(CardCharacteristics c) {
set(CardStateProp.FoilIndex, c.getFoil());
}
public Map<String, String> getChangedColorWords() {
return get(CardStateProp.ChangedColorWords);
}
void updateChangedColorWords(Card c) {
set(CardStateProp.ChangedColorWords, c.getChangedTextColorWords());
}
public Map<String, String> getChangedTypes() {
return get(CardStateProp.ChangedTypes);
}
void updateChangedTypes(Card c) {
set(CardStateProp.ChangedTypes, c.getChangedTextTypeWords());
}
public boolean hasDeathtouch() {
return get(CardStateProp.HasDeathtouch);
}
public boolean hasHaste() {
return get(CardStateProp.HasHaste);
}
public boolean hasInfect() {
return get(CardStateProp.HasInfect);
}
public boolean hasStorm() {
return get(CardStateProp.HasStorm);
}
public boolean hasTrample() {
return get(CardStateProp.HasTrample);
}
void updateKeywords(Card c) {
set(CardStateProp.HasDeathtouch, c.hasKeyword("Deathtouch"));
set(CardStateProp.HasHaste, c.hasKeyword("Haste"));
set(CardStateProp.HasInfect, c.hasKeyword("Infect"));
set(CardStateProp.HasStorm, c.hasKeyword("Storm"));
set(CardStateProp.HasTrample, c.hasKeyword("Trample"));
}
public boolean isBasicLand() {
return isLand() && Iterables.any(getType(), Predicates.in(CardType.getBasicTypes()));
}
public boolean isCreature() {
return getType().contains("Creature");
}
public boolean isLand() {
return getType().contains("Land");
}
public boolean isPlane() {
return getType().contains("Plane");
}
public boolean isPhenomenon() {
return getType().contains("Phenomenon");
}
public boolean isPlaneswalker() {
return getType().contains("Planeswalker");
}
}
@Override
protected CardProp preventNextDamageProp() {
return CardProp.PreventNextDamage;
}
}

View File

@@ -278,9 +278,9 @@ public class Combat {
if (source.isAura()) {
attacker = source.getEnchantingCard();
} else if (source.isEquipment()) {
attacker = source.getEquippingCard();
attacker = source.getEquipping();
} else if (source.isFortification()) {
attacker = source.getFortifyingCard();
attacker = source.getFortifying();
}
// return the corresponding defender

View File

@@ -634,7 +634,7 @@ public class CombatUtil {
final String parse = blocker.getKeyword().get(keywordPosition).toString();
if (parse.startsWith("CantBlockCardUID")) {
final String[] k = parse.split("_", 2);
if (attacker.getUniqueNumber() == Integer.parseInt(k[1])) {
if (attacker.getId() == Integer.parseInt(k[1])) {
return false;
}
} else {
@@ -999,7 +999,7 @@ public class CombatUtil {
// Rule 702.23b: If a creature has multiple instances of flanking, each triggers separately.
for( int i = 0; i < flankingMagnitude; i++ ) {
String effect = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ -1 | NumDef$ -1 | ", blocker.getUniqueNumber());
String effect = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ -1 | NumDef$ -1 | ", blocker.getId());
String desc = String.format("StackDescription$ Flanking (The blocking %s gets -1/-1 until end of turn)", blocker.getName());
SpellAbility ability = AbilityFactory.getAbility(effect + desc, attacker);
@@ -1031,7 +1031,7 @@ public class CombatUtil {
private static void executeRampageAbility(final Game game, final Card c, final int magnitude, final int numBlockers) {
// numBlockers starts with 1 since it is for every creature beyond the first
for (int i = 1; i < numBlockers; i++) {
String effect = "AB$ Pump | Cost$ 0 | " + c.getUniqueNumber() + " | NumAtt$ " + magnitude + " | NumDef$ " + magnitude + " | ";
String effect = "AB$ Pump | Cost$ 0 | " + c.getId() + " | NumAtt$ " + magnitude + " | NumDef$ " + magnitude + " | ";
String desc = "StackDescription$ Rampage " + magnitude + " (Whenever CARDNAME becomes blocked, it gets +" + magnitude + "/+"
+ magnitude + " until end of turn for each creature blocking it beyond the first.)";

View File

@@ -0,0 +1,150 @@
package forge.game.combat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.game.GameEntityView;
import forge.game.card.CardView;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty.CombatProp;
public class CombatView extends TrackableObject<CombatProp> {
public CombatView() {
super(-1, CombatProp.class); //ID not needed
}
private Map<CardView, GameEntityView<?>> getAttackersWithDefenders() {
return get(CombatProp.AttackersWithDefenders);
}
private Map<CardView, Iterable<CardView>> getAttackersWithBlockers() {
return get(CombatProp.AttackersWithBlockers);
}
private Map<Iterable<CardView>, GameEntityView<?>> getBandsWithDefenders() {
return get(CombatProp.BandsWithDefenders);
}
private Map<Iterable<CardView>, Iterable<CardView>> getBandsWithBlockers() {
return get(CombatProp.BandsWithBlockers);
}
private Map<CardView, Iterable<CardView>> getAttackersWithPlannedBlockers() {
return get(CombatProp.AttackersWithPlannedBlockers);
}
private Map<Iterable<CardView>, Iterable<CardView>> getBandsWithPlannedBlockers() {
return get(CombatProp.BandsWithPlannedBlockers);
}
public int getNumAttackers() {
return getAttackersWithDefenders().size();
}
public boolean isAttacking(final CardView card) {
return getAttackersWithDefenders().containsKey(card);
}
public Iterable<CardView> getAttackers() {
return getAttackersWithDefenders().keySet();
}
public Iterable<GameEntityView<?>> getDefenders() {
return Sets.newHashSet(getAttackersWithDefenders().values());
}
public GameEntityView<?> getDefender(final CardView attacker) {
return getAttackersWithDefenders().get(attacker);
}
public boolean isBlocking(final CardView card) {
for (final Iterable<CardView> blockers : getAttackersWithBlockers().values()) {
if (blockers == null) {
continue;
}
if (Iterables.contains(blockers, card)) {
return true;
}
}
return false;
}
/**
* @param attacker
* @return the blockers associated with an attacker, or {@code null} if the
* attacker is unblocked.
*/
public Iterable<CardView> getBlockers(final CardView attacker) {
return getAttackersWithBlockers().get(attacker);
}
/**
* @param attacker
* @return the blockers associated with an attacker, or {@code null} if the
* attacker is unblocked (planning stage, for targeting overlay).
*/
public Iterable<CardView> getPlannedBlockers(final CardView attacker) {
return getAttackersWithPlannedBlockers().get(attacker);
}
/**
* Get an {@link Iterable} of the blockers of the specified band, or
* {@code null} if that band is unblocked.
*
* @param attackingBand
* an {@link Iterable} representing an attacking band.
* @return an {@link Iterable} of {@link CardView} objects, or {@code null}.
*/
public Iterable<CardView> getBlockers(final Iterable<CardView> attackingBand) {
return getBandsWithBlockers().get(attackingBand);
}
/**
* Get an {@link Iterable} of the blockers of the specified band, or
* {@code null} if that band is unblocked (planning stage, for targeting overlay).
*
* @param attackingBand
* an {@link Iterable} representing an attacking band.
* @return an {@link Iterable} of {@link CardView} objects, or {@code null}.
*/
public Iterable<CardView> getPlannedBlockers(final Iterable<CardView> attackingBand) {
return getBandsWithPlannedBlockers().get(attackingBand);
}
public Iterable<CardView> getAttackersOf(final GameEntityView<?> defender) {
ArrayList<CardView> views = new ArrayList<CardView>();
for (Entry<CardView, GameEntityView<?>> entry : getAttackersWithDefenders().entrySet()) {
if (entry.getValue().equals(defender)) {
views.add(entry.getKey());
}
}
return views;
}
public Iterable<Iterable<CardView>> getAttackingBandsOf(final GameEntityView<?> defender) {
ArrayList<Iterable<CardView>> views = new ArrayList<Iterable<CardView>>();
for (Entry<Iterable<CardView>, GameEntityView<?>> entry : getBandsWithDefenders().entrySet()) {
if (entry.getValue().equals(defender)) {
views.add(entry.getKey());
}
}
return views;
}
public void addAttackingBand(final Iterable<CardView> attackingBand, final GameEntityView<?> defender, final Iterable<CardView> blockers, final Iterable<CardView> plannedBlockers) {
final List<CardView> attackingBandCopy = Lists.newArrayList(attackingBand),
blockersCopy, plannedBlockersCopy;
blockersCopy = blockers == null ? null : Lists.newArrayList(blockers);
plannedBlockersCopy = plannedBlockers == null ? null : Lists.newArrayList(plannedBlockers);
for (final CardView attacker : attackingBandCopy) {
this.getAttackersWithDefenders().put(attacker, defender);
this.getAttackersWithBlockers().put(attacker, blockersCopy);
this.getAttackersWithPlannedBlockers().put(attacker, plannedBlockersCopy);
}
this.getBandsWithDefenders().put(attackingBandCopy, defender);
this.getBandsWithBlockers().put(attackingBandCopy, blockersCopy);
this.getBandsWithPlannedBlockers().put(attackingBandCopy, plannedBlockersCopy);
}
}

View File

@@ -111,7 +111,7 @@ public class CostUnattach extends CostPartWithList {
*/
@Override
protected Card doPayment(SpellAbility ability, Card targetCard) {
targetCard.unEquipCard(targetCard.getEquipping().get(0));
targetCard.unEquipCard(targetCard.getEquipping());
return targetCard;
}

View File

@@ -58,7 +58,7 @@ public class GameStateSerializer {
public void write(Card card) {
if (card == null) { return; }
int key = card.getUniqueNumber();
int key = card.getId();
cards.put(key, card);
write(key); //only write info for each card once at end of file
}

View File

@@ -518,7 +518,7 @@ public class PhaseHandler implements java.io.Serializable, IGameStateObject {
int exaltedMagnitude = card.getKeywordAmount("Exalted");
for (int i = 0; i < exaltedMagnitude; i++) {
String abScript = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ +1 | NumDef$ +1 | StackDescription$ Exalted for attacker {c:CardUID_%d} (Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn).", attacker.getUniqueNumber(), attacker.getUniqueNumber());
String abScript = String.format("AB$ Pump | Cost$ 0 | Defined$ CardUID_%d | NumAtt$ +1 | NumDef$ +1 | StackDescription$ Exalted for attacker {c:CardUID_%d} (Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn).", attacker.getId(), attacker.getId());
SpellAbility ability = AbilityFactory.getAbility(abScript, card);
ability.setActivatingPlayer(card.getController());
ability.setDescription(ability.getStackDescription());

View File

@@ -257,11 +257,11 @@ public class Untap extends Phase {
continue;
}
} else if (c.isEquipment() && c.isEquipping()) {
if (list.contains(c.getEquippingCard())) {
if (list.contains(c.getEquipping())) {
continue;
}
} else if (c.isFortification() && c.isFortifying()) {
if (list.contains(c.getFortifyingCard())) {
if (list.contains(c.getFortifying())) {
continue;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
package forge.game.player;
import java.util.Map;
import com.google.common.collect.Maps;
import forge.LobbyPlayer;
import forge.card.MagicColor;
import forge.game.GameEntityView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardView;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.trackable.TrackableCollection;
import forge.trackable.TrackableProperty.PlayerProp;
public class PlayerView extends GameEntityView<PlayerProp> {
public static PlayerView get(Player p) {
return p == null ? null : p.getView();
}
public static TrackableCollection<PlayerView> getCollection(Iterable<Player> players) {
if (players == null) {
return null;
}
TrackableCollection<PlayerView> collection = new TrackableCollection<PlayerView>();
for (Player p : players) {
collection.add(p.getView());
}
return collection;
}
public PlayerView(int id0) {
super(id0, PlayerProp.class);
set(PlayerProp.Mana, Maps.newHashMapWithExpectedSize(MagicColor.NUMBER_OR_COLORS + 1));
}
public LobbyPlayer getLobbyPlayer() {
return get(PlayerProp.LobbyPlayer);
}
void updateLobbyPlayer(Player p) {
set(PlayerProp.LobbyPlayer, p.getLobbyPlayer());
}
public Iterable<PlayerView> getOpponents() {
return get(PlayerProp.Opponents);
}
private TrackableCollection<PlayerView> getOpponentCollection() {
return get(PlayerProp.Opponents);
}
public boolean isOpponentOf(final PlayerView other) {
return getOpponentCollection().contains(other);
}
public String getName() {
return getLobbyPlayer().getName();
}
@Override
public String toString() {
return getName();
}
public int getLife() {
return get(PlayerProp.Life);
}
void updateLife(Player p) {
set(PlayerProp.Life, p.getLife());
}
public int getPoisonCounters() {
return get(PlayerProp.PoisonCounters);
}
void updatePoisonCounters(Player p) {
set(PlayerProp.PoisonCounters, p.getPoisonCounters());
}
public int getMaxHandSize() {
return get(PlayerProp.MaxHandSize);
}
void updateMaxHandSize(Player p) {
set(PlayerProp.MaxHandSize, p.getMaxHandSize());
}
public boolean hasUnlimitedHandSize() {
return get(PlayerProp.HasUnlimitedHandSize);
}
void updateUnlimitedHandSize(Player p) {
set(PlayerProp.HasUnlimitedHandSize, p.isUnlimitedHandSize());
}
public int getNumDrawnThisTurn() {
return get(PlayerProp.NumDrawnThisTurn);
}
void updateNumDrawnThisTurn(Player p) {
set(PlayerProp.NumDrawnThisTurn, p.getNumDrawnThisTurn());
}
public Iterable<String> getKeywords() {
return get(PlayerProp.Keywords);
}
void updateKeywords(Player p) {
set(PlayerProp.Keywords, p.getKeywords());
}
public String getCommanderInfo() {
return get(PlayerProp.CommanderInfo);
}
void updateCommanderInfo(Player p) {
set(PlayerProp.CommanderInfo, CardFactoryUtil.getCommanderInfo(p).trim().replace("\r\n", "; "));
}
public Iterable<CardView> getAnte() {
return get(PlayerProp.Ante);
}
public Iterable<CardView> getBattlefield() {
return get(PlayerProp.Battlefield);
}
public Iterable<CardView> getCommand() {
return get(PlayerProp.Command);
}
public Iterable<CardView> getExile() {
return get(PlayerProp.Exile);
}
public Iterable<CardView> getFlashback() {
return get(PlayerProp.Flashback);
}
public Iterable<CardView> getGraveyard() {
return get(PlayerProp.Graveyard);
}
public Iterable<CardView> getHand() {
return get(PlayerProp.Hand);
}
public Iterable<CardView> getLibrary() {
return get(PlayerProp.Library);
}
public Iterable<CardView> getCards(final ZoneType zone) {
PlayerProp prop = getZoneProp(zone);
if (prop != null) {
return get(prop);
}
return null;
}
private PlayerProp getZoneProp(final ZoneType zone) {
switch (zone) {
case Ante:
return PlayerProp.Ante;
case Battlefield:
return PlayerProp.Battlefield;
case Command:
return PlayerProp.Command;
case Exile:
return PlayerProp.Exile;
case Graveyard:
return PlayerProp.Graveyard;
case Hand:
return PlayerProp.Hand;
case Library:
return PlayerProp.Library;
default:
return null; //other zones not represented
}
}
void updateZone(PlayerZone zone) {
PlayerProp prop = getZoneProp(zone.getZoneType());
if (prop == null) { return; }
set(prop, CardView.getCollection(zone.getCards()));
}
private Map<Byte, Integer> getMana() {
return get(PlayerProp.Mana);
}
void updateMana(Player p) {
boolean changed = false;
Map<Byte, Integer> mana = getMana();
for (byte b : MagicColor.WUBRGC) {
int value = p.getManaPool().getAmountOfColor(b);
if (mana.put(b, value) != value) {
changed = true;
}
}
if (changed) {
flagAsChanged(PlayerProp.Mana);
}
}
public int getMana(final byte color) {
Integer count = getMana().get(color);
return count != null ? count.intValue() : 0;
}
@Override
protected PlayerProp preventNextDamageProp() {
return PlayerProp.PreventNextDamage;
}
}

View File

@@ -233,8 +233,8 @@ public class ReplacementHandler implements IGameStateObject {
// Replaced mana type
final Card repHost = replacementEffect.getHostCard();
String repType = repHost.getSVar(mapParams.get("ManaReplacement"));
if (repType.contains("Chosen") && !repHost.getChosenColor().isEmpty()) {
repType = repType.replace("Chosen", MagicColor.toShortString(repHost.getChosenColor().get(0)));
if (repType.contains("Chosen") && !repHost.getChosenColors().isEmpty()) {
repType = repType.replace("Chosen", MagicColor.toShortString(repHost.getChosenColors().get(0)));
}
manaAb.getManaPart().setManaReplaceType(repType);
manaAb.getManaPart().produceMana(rep, player1, manaAb);

View File

@@ -341,9 +341,9 @@ public class AbilityManaPart implements java.io.Serializable {
*/
public final String mana() {
if (this.getOrigProduced().contains("Chosen")) {
if (this.getSourceCard() != null && !this.getSourceCard().getChosenColor().isEmpty()) {
if (this.getSourceCard() != null && !this.getSourceCard().getChosenColors().isEmpty()) {
return MagicColor.toShortString(this.getSourceCard()
.getChosenColor().get(0));
.getChosenColors().get(0));
}
}
return this.getOrigProduced();
@@ -473,7 +473,7 @@ public class AbilityManaPart implements java.io.Serializable {
}
if (this.getOrigProduced().contains("Chosen") && sourceCard != null ) {
List<String> chosenCol = this.getSourceCard().getChosenColor();
List<String> chosenCol = this.getSourceCard().getChosenColors();
if ( !chosenCol.isEmpty() && MagicColor.toShortString(chosenCol.get(0)).contains(s)) {
return true;
}

View File

@@ -275,7 +275,7 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
}
if (this.getColorToCheck() != null) {
if (!sa.getHostCard().getChosenColor().contains(this.getColorToCheck())) {
if (!sa.getHostCard().getChosenColors().contains(this.getColorToCheck())) {
return false;
}
}

View File

@@ -302,7 +302,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
}
if (this.getColorToCheck() != null) {
if (!sa.getHostCard().getChosenColor().contains(this.getColorToCheck())) {
if (!sa.getHostCard().getChosenColors().contains(this.getColorToCheck())) {
return false;
}
}

View File

@@ -51,10 +51,10 @@ public class SpellAbilityStackInstance implements IIdentifiable {
// Coming off the Stack would work similarly, except it would just add the
// full active SI instead of each of the parts
private int id;
private SpellAbility ability = null;
private final int id;
private final SpellAbility ability;
private SpellAbilityStackInstance subInstance = null;
private final SpellAbilityStackInstance subInstance;
private Player activator;
// When going to a SubAbility that SA has a Instance Choice object
@@ -73,67 +73,58 @@ public class SpellAbilityStackInstance implements IIdentifiable {
private int xManaPaid = 0;
// Other Paid things
private HashMap<String, List<Card>> paidHash = new HashMap<String, List<Card>>();
private final HashMap<String, List<Card>> paidHash;
// Additional info
// is Kicked, is Buyback
// Triggers
private HashMap<String, Object> triggeringObjects = new HashMap<String, Object>();
private List<Object> triggerRemembered = new ArrayList<Object>();
private final HashMap<String, Object> triggeringObjects;
private final List<Object> triggerRemembered;
private final HashMap<String, String> storedSVars = new HashMap<String, String>();
private final List<ZoneType> zonesToOpen;
private final Map<Player, Object> playersWithValidTargets;
/**
* <p>
* Constructor for SpellAbility_StackInstance.
* </p>
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
*/
private final StackItemView view;
public SpellAbilityStackInstance(final SpellAbility sa) {
// Base SA info
this.id = nextId();
this.ability = sa;
this.stackDescription = this.ability.getStackDescription();
this.activator = sa.getActivatingPlayer();
id = nextId();
ability = sa;
stackDescription = sa.getStackDescription();
activator = sa.getActivatingPlayer();
// Payment info
this.paidHash = this.ability.getPaidHash();
this.ability.resetPaidHash();
this.splicedCards = sa.getSplicedCards();
paidHash = ability.getPaidHash();
ability.resetPaidHash();
splicedCards = sa.getSplicedCards();
// TODO getXManaCostPaid should be on the SA, not the Card
this.xManaPaid = sa.getHostCard().getXManaCostPaid();
xManaPaid = sa.getHostCard().getXManaCostPaid();
// Triggering info
this.triggeringObjects = sa.getTriggeringObjects();
this.triggerRemembered = sa.getTriggerRemembered();
triggeringObjects = sa.getTriggeringObjects();
triggerRemembered = sa.getTriggerRemembered();
final AbilitySub subAb = this.ability.getSubAbility();
if (subAb != null) {
this.subInstance = new SpellAbilityStackInstance(subAb);
}
subInstance = ability.getSubAbility() == null ? null : new SpellAbilityStackInstance(ability.getSubAbility());
// Targeting info -- 29/06/11 Moved to after taking care of SubAbilities
// because otherwise AF_DealDamage SubAbilities that use Defined$
// Targeted breaks (since it's parents target is reset)
if (sa.usesTargeting()) {
this.tc = ability.getTargets();
this.ability.resetTargets();
tc = ability.getTargets();
ability.resetTargets();
}
final Card source = this.ability.getHostCard();
final Card source = ability.getHostCard();
// Store SVars and Clear
for (final String store : Card.getStorableSVars()) {
final String value = source.getSVar(store);
if (value.length() > 0) {
this.storedSVars.put(store, value);
storedSVars.put(store, value);
source.setSVar(store, "");
}
}
@@ -156,154 +147,107 @@ public class SpellAbilityStackInstance implements IIdentifiable {
}
}
}
view = new StackItemView(this);
}
@Override
public int getId() {
return this.id;
return id;
}
/**
* <p>
* getSpellAbility.
* </p>
*
* @return a {@link forge.game.spellability.SpellAbility} object.
*/
public final SpellAbility getSpellAbility() {
this.ability.resetTargets();
this.ability.setTargets(tc);
this.ability.setActivatingPlayer(activator);
//TODO: See if refresh actually needed for most places this is being called
// Perhaps lets move the refresh logic to a separate function called only when necessary
public final SpellAbility getSpellAbility(boolean refresh) {
if (refresh) {
ability.resetTargets();
ability.setTargets(tc);
ability.setActivatingPlayer(activator);
// Saved sub-SA needs to be reset on the way out
if (this.subInstance != null) {
this.ability.setSubAbility((AbilitySub) this.subInstance.getSpellAbility());
}
// Saved sub-SA needs to be reset on the way out
if (subInstance != null) {
ability.setSubAbility((AbilitySub) subInstance.getSpellAbility(true));
}
// Set Cost specific things here
this.ability.resetPaidHash();
this.ability.setPaidHash(this.paidHash);
this.ability.setSplicedCards(splicedCards);
this.ability.getHostCard().setXManaCostPaid(this.xManaPaid);
// Set Cost specific things here
ability.resetPaidHash();
ability.setPaidHash(paidHash);
ability.setSplicedCards(splicedCards);
ability.getHostCard().setXManaCostPaid(xManaPaid);
// Triggered
this.ability.setAllTriggeringObjects(this.triggeringObjects);
this.ability.setTriggerRemembered(this.triggerRemembered);
// Triggered
ability.setTriggeringObjects(triggeringObjects);
ability.setTriggerRemembered(triggerRemembered);
// Add SVars back in
final Card source = this.ability.getHostCard();
for (final String store : this.storedSVars.keySet()) {
final String value = this.storedSVars.get(store);
if (value.length() > 0) {
source.setSVar(store, value);
// Add SVars back in
final Card source = ability.getHostCard();
for (final String store : storedSVars.keySet()) {
final String value = storedSVars.get(store);
if (value.length() > 0) {
source.setSVar(store, value);
}
}
}
return this.ability;
return ability;
}
// A bit of SA shared abilities to restrict conflicts
/**
* <p>
* Getter for the field <code>stackDescription</code>.
* </p>
*
* @return a {@link java.lang.String} object.
*/
public final String getStackDescription() {
return this.stackDescription;
return stackDescription;
}
/**
* <p>
* getHostCard.
* </p>
*
* @return a {@link forge.game.card.Card} object.
*/
public final Card getSourceCard() {
return this.ability.getHostCard();
return ability.getHostCard();
}
/**
* <p>
* isSpell.
* </p>
*
* @return a boolean.
*/
public final boolean isSpell() {
return this.ability.isSpell();
return ability.isSpell();
}
/**
* <p>
* isAbility.
* </p>
*
* @return a boolean.
*/
public final boolean isAbility() {
return this.ability.isAbility();
return ability.isAbility();
}
/**
* <p>
* isTrigger.
* </p>
*
* @return a boolean.
*/
public final boolean isTrigger() {
return this.ability.isTrigger();
return ability.isTrigger();
}
/**
* <p>
* isStateTrigger.
* </p>
*
* @param id
* a int.
* @return a boolean.
*/
public final boolean isStateTrigger(final int id) {
return this.ability.getSourceTrigger() == id;
return ability.getSourceTrigger() == id;
}
/**
* Checks if is optional trigger.
*
* @return true, if is optional trigger
*/
public final boolean isOptionalTrigger() {
return this.ability.isOptionalTrigger();
return ability.isOptionalTrigger();
}
public final SpellAbilityStackInstance getSubInstance() {
return this.subInstance;
return subInstance;
}
public final TargetChoices getTargetChoices() {
return this.tc;
return tc;
}
public final List<ZoneType> getZonesToOpen() {
return this.zonesToOpen;
return zonesToOpen;
}
public final Map<Player, Object> getPlayersWithValidTargets() {
return this.playersWithValidTargets;
return playersWithValidTargets;
}
public void updateTarget(TargetChoices target) {
if (target != null) {
this.tc = target;
this.ability.setTargets(tc);
this.stackDescription = this.ability.getStackDescription();
tc = target;
ability.setTargets(tc);
stackDescription = ability.getStackDescription();
view.updateTargetCards(this);
view.updateTargetPlayers(this);
view.updateText(this);
// Run BecomesTargetTrigger
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("SourceSA", this.ability);
runParams.put("SourceSA", ability);
HashSet<Object> distinctObjects = new HashSet<Object>();
for (final Object tgt : target.getTargets()) {
if (distinctObjects.contains(tgt)) {
@@ -315,7 +259,7 @@ public class SpellAbilityStackInstance implements IIdentifiable {
((Card) tgt).setBecameTargetThisTurn(true);
}
runParams.put("Target", tgt);
this.getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
}
}
}
@@ -347,12 +291,18 @@ public class SpellAbilityStackInstance implements IIdentifiable {
return activator;
}
public void setActivator(Player activator) {
this.activator = activator;
public void setActivator(Player activator0) {
if (activator == activator0) { return; }
activator = activator0;
view.updateActivator(this);
}
@Override
public String toString() {
return String.format("%s->%s", getSourceCard(), stackDescription);
return String.format("%s->%s", getSourceCard(), getStackDescription());
}
public StackItemView getView() {
return view;
}
}

View File

@@ -0,0 +1,49 @@
package forge.game.spellability;
import forge.game.card.CardView;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty.SpellAbilityProp;
public class SpellAbilityView extends TrackableObject<SpellAbilityProp> {
SpellAbilityView(SpellAbility sa) {
super(sa.getId(), SpellAbilityProp.class);
updateHostCard(sa);
updateDescription(sa);
updateCanPlay(sa);
updatePromptIfOnlyPossibleAbility(sa);
}
@Override
public String toString() {
return this.getDescription();
}
public CardView getHostCard() {
return get(SpellAbilityProp.HostCard);
}
void updateHostCard(SpellAbility sa) {
set(SpellAbilityProp.HostCard, CardView.get(sa.getHostCard()));
}
public String getDescription() {
return get(SpellAbilityProp.Description);
}
void updateDescription(SpellAbility sa) {
set(SpellAbilityProp.Description, sa.toUnsuppressedString());
}
public boolean canPlay() {
return get(SpellAbilityProp.CanPlay);
}
void updateCanPlay(SpellAbility sa) {
set(SpellAbilityProp.CanPlay, sa.canPlay());
}
public boolean promptIfOnlyPossibleAbility() {
return get(SpellAbilityProp.PromptIfOnlyPossibleAbility);
}
void updatePromptIfOnlyPossibleAbility(SpellAbility sa) {
set(SpellAbilityProp.PromptIfOnlyPossibleAbility, sa.promptIfOnlyPossibleAbility());
}
}

View File

@@ -0,0 +1,98 @@
package forge.game.spellability;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty.StackItemProp;
public class StackItemView extends TrackableObject<StackItemProp> {
public StackItemView(SpellAbilityStackInstance si) {
super(si.getId(), StackItemProp.class);
updateKey(si);
updateSourceTrigger(si);
updateText(si);
updateSourceCard(si);
updateActivator(si);
updateTargetCards(si);
updateTargetPlayers(si);
updateAbility(si);
updateOptionalTrigger(si);
updateSubInstance(si);
}
public String getKey() {
return get(StackItemProp.Key);
}
void updateKey(SpellAbilityStackInstance si) {
set(StackItemProp.Key, si.getSpellAbility(false).toUnsuppressedString());
}
public int getSourceTrigger() {
return get(StackItemProp.SourceTrigger);
}
void updateSourceTrigger(SpellAbilityStackInstance si) {
set(StackItemProp.SourceTrigger, si.getSpellAbility(false).getSourceTrigger());
}
public String getText() {
return get(StackItemProp.Text);
}
void updateText(SpellAbilityStackInstance si) {
set(StackItemProp.Text, si.getStackDescription());
}
public CardView getSourceCard() {
return get(StackItemProp.SourceCard);
}
void updateSourceCard(SpellAbilityStackInstance si) {
set(StackItemProp.SourceCard, si.getSourceCard());
}
public PlayerView getActivator() {
return get(StackItemProp.Activator);
}
void updateActivator(SpellAbilityStackInstance si) {
set(StackItemProp.Activator, PlayerView.get(si.getActivator()));
}
public Iterable<CardView> getTargetCards() {
return get(StackItemProp.TargetCards);
}
void updateTargetCards(SpellAbilityStackInstance si) {
set(StackItemProp.TargetCards, CardView.getCollection(si.getTargetChoices().getTargetCards()));
}
public Iterable<PlayerView> getTargetPlayers() {
return get(StackItemProp.TargetPlayers);
}
void updateTargetPlayers(SpellAbilityStackInstance si) {
set(StackItemProp.TargetPlayers, PlayerView.getCollection(si.getTargetChoices().getTargetPlayers()));
}
public boolean isAbility() {
return get(StackItemProp.Ability);
}
void updateAbility(SpellAbilityStackInstance si) {
set(StackItemProp.Ability, si.isAbility());
}
public boolean isOptionalTrigger() {
return get(StackItemProp.OptionalTrigger);
}
void updateOptionalTrigger(SpellAbilityStackInstance si) {
set(StackItemProp.OptionalTrigger, si.isOptionalTrigger());
}
public StackItemView getSubInstance() {
return get(StackItemProp.SubInstance);
}
void updateSubInstance(SpellAbilityStackInstance si) {
set(StackItemProp.SubInstance, si.getSubInstance() == null ? null : new StackItemView(si.getSubInstance()));
}
@Override
public String toString() {
return getText();
}
}

View File

@@ -159,7 +159,7 @@ public class StaticAbilityContinuous {
if (params.containsKey("AddKeyword")) {
addKeywords = params.get("AddKeyword").split(" & ");
final List<String> chosencolors = hostCard.getChosenColor();
final List<String> chosencolors = hostCard.getChosenColors();
for (final String color : chosencolors) {
for (int w = 0; w < addKeywords.length; w++) {
addKeywords[w] = addKeywords[w].replaceAll("ChosenColor", color.substring(0, 1).toUpperCase().concat(color.substring(1, color.length())));
@@ -170,7 +170,7 @@ public class StaticAbilityContinuous {
addKeywords[w] = addKeywords[w].replaceAll("ChosenType", chosenType);
}
final String chosenName = hostCard.getNamedCard();
final String hostCardUID = Integer.toString(hostCard.getUniqueNumber()); // Protection with "doesn't remove" effect
final String hostCardUID = Integer.toString(hostCard.getId()); // Protection with "doesn't remove" effect
for (int w = 0; w < addKeywords.length; w++) {
if (addKeywords[w].startsWith("Protection:")) {
addKeywords[w] = addKeywords[w].replaceAll("ChosenName", "Card.named" + chosenName).replace("HostCardUID", hostCardUID);
@@ -223,7 +223,7 @@ public class StaticAbilityContinuous {
addTypes[0] = chosenType;
se.setChosenType(chosenType);
} else if (addTypes[0].equals("ImprintedCreatureType")) {
final ArrayList<String> imprint = hostCard.getImprinted().get(0).getType();
final Set<String> imprint = hostCard.getImprinted().get(0).getType();
ArrayList<String> imprinted = new ArrayList<String>();
for (String t : imprint) {
if (CardType.isACreatureType(t) || t.equals("AllCreatureTypes")) {
@@ -262,7 +262,7 @@ public class StaticAbilityContinuous {
if (params.containsKey("AddColor")) {
final String colors = params.get("AddColor");
if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(hostCard.getChosenColor());
addColors = CardUtil.getShortColorsString(hostCard.getChosenColors());
} else {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(
" & "))));
@@ -272,7 +272,7 @@ public class StaticAbilityContinuous {
if (params.containsKey("SetColor")) {
final String colors = params.get("SetColor");
if (colors.equals("ChosenColor")) {
addColors = CardUtil.getShortColorsString(hostCard.getChosenColor());
addColors = CardUtil.getShortColorsString(hostCard.getChosenColors());
} else {
addColors = CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(
colors.split(" & "))));
@@ -381,8 +381,8 @@ public class StaticAbilityContinuous {
if (changeColorWordsTo != null) {
final byte color;
if (changeColorWordsTo.equals("ChosenColor")) {
if (hostCard.getChosenColor().size() > 0) {
color = MagicColor.fromName(hostCard.getChosenColor().get(0));
if (hostCard.getChosenColors().size() > 0) {
color = MagicColor.fromName(hostCard.getChosenColors().get(0));
} else {
color = 0;
}
@@ -653,7 +653,7 @@ public class StaticAbilityContinuous {
} else if (params.get("Affected").contains("EnchantedBy")) {
affectedCards = Lists.newArrayList(hostCard.getEnchantingCard());
} else if (params.get("Affected").contains("EquippedBy")) {
affectedCards = Lists.newArrayList(hostCard.getEquippingCard());
affectedCards = Lists.newArrayList(hostCard.getEquipping());
} else if (params.get("Affected").equals("EffectSource")) {
affectedCards = new ArrayList<Card>(AbilityUtils.getDefinedCards(hostCard, params.get("Affected"), null));
return affectedCards;

View File

@@ -190,7 +190,7 @@ public class TriggerHandler implements IGameStateObject {
while(itr.hasNext()) {
t = (Trigger)itr.next();
if (c.getUniqueNumber() == t.getHostCard().getUniqueNumber() && t.isIntrinsic()) {
if (c.getId() == t.getHostCard().getId() && t.isIntrinsic()) {
toBeRemoved.add(t);
}
}
@@ -420,7 +420,7 @@ public class TriggerHandler implements IGameStateObject {
Card host = regtrig.getHostCard();
Card trigCard = regtrig.getRunParams().containsKey("Card") ? (Card)regtrig.getRunParams().get("Card") : null;
if (trigCard != null && (host.getUniqueNumber() == trigCard.getUniqueNumber())) {
if (trigCard != null && (host.getId() == trigCard.getId())) {
host = trigCard;
} else {
host = game.getCardState(regtrig.getHostCard());
@@ -445,7 +445,7 @@ public class TriggerHandler implements IGameStateObject {
regtrig.setTriggeringObjects(sa);
sa.setTriggerRemembered(regtrig.getTriggerRemembered());
if (regtrig.getStoredTriggeredObjects() != null) {
sa.setAllTriggeringObjects(regtrig.getStoredTriggeredObjects());
sa.setTriggeringObjects(regtrig.getStoredTriggeredObjects());
}
if (sa.getActivatingPlayer() == null) { // overriding delayed trigger should have set activator
sa.setActivatingPlayer(host.getController());

View File

@@ -95,7 +95,7 @@ public class TriggerSpellAbilityCast extends Trigger {
}
if (this.mapParams.containsKey("ValidActivatingPlayer")) {
if (si == null || !matchesValid(si.getSpellAbility().getActivatingPlayer(), this.mapParams.get("ValidActivatingPlayer")
if (si == null || !matchesValid(si.getSpellAbility(true).getActivatingPlayer(), this.mapParams.get("ValidActivatingPlayer")
.split(","), this.getHostCard())) {
return false;
}
@@ -103,7 +103,7 @@ public class TriggerSpellAbilityCast extends Trigger {
String compare = this.mapParams.get("ActivatorThisTurnCast");
List<Card> thisTurnCast = CardUtil.getThisTurnCast(this.mapParams.containsKey("ValidCard") ? this.mapParams.get("ValidCard") : "Card",
this.getHostCard());
thisTurnCast = CardLists.filterControlledBy(thisTurnCast, si.getSpellAbility().getActivatingPlayer());
thisTurnCast = CardLists.filterControlledBy(thisTurnCast, si.getSpellAbility(true).getActivatingPlayer());
int left = thisTurnCast.size();
int right = Integer.parseInt(compare.substring(2));
if (!Expressions.compare(left, compare, right)) {
@@ -121,7 +121,7 @@ public class TriggerSpellAbilityCast extends Trigger {
if (this.mapParams.containsKey("TargetsValid")) {
SpellAbility sa = spellAbility;
if (si != null) {
sa = si.getSpellAbility();
sa = si.getSpellAbility(true);
}
boolean validTgtFound = false;
@@ -211,7 +211,7 @@ public class TriggerSpellAbilityCast extends Trigger {
final SpellAbilityStackInstance si = sa.getHostCard().getGame().getStack().getInstanceFromSpellAbility(castSA);
sa.setTriggeringObject("Card", castSA.getHostCard());
sa.setTriggeringObject("SpellAbility", castSA);
sa.setTriggeringObject("SpellAbilityTargetingCards", (si != null ? si.getSpellAbility() : castSA).getTargets().getTargetCards());
sa.setTriggeringObject("SpellAbilityTargetingCards", (si != null ? si.getSpellAbility(true) : castSA).getTargets().getTargetCards());
sa.setTriggeringObject("Player", this.getRunParams().get("Player"));
sa.setTriggeringObject("Activator", this.getRunParams().get("Activator"));
sa.setTriggeringObject("CurrentStormCount", this.getRunParams().get("CurrentStormCount"));

View File

@@ -93,7 +93,7 @@ public class TriggerTapsForMana extends Trigger {
}
String produced = (String) prod;
if ("ChosenColor".equals(mapParams.get("Produced"))) {
List<String> colors = this.getHostCard().getChosenColor();
List<String> colors = this.getHostCard().getChosenColors();
if (colors.isEmpty() || !produced.contains(MagicColor.toShortString(colors.get(0)))) {
return false;
}

View File

@@ -31,7 +31,7 @@ public class WrappedAbility extends Ability implements ISpellAbility {
boolean mandatory = false;
public WrappedAbility(final Trigger regTrig, final SpellAbility sa0, final Player decider0) {
super(regTrig.getHostCard(), ManaCost.ZERO);
super(sa0.getHostCard(), ManaCost.ZERO);
regtrig = regTrig;
sa = sa0;
decider = decider0;
@@ -41,7 +41,6 @@ public class WrappedAbility extends Ability implements ISpellAbility {
return sa;
}
@Override
public boolean isWrapper() {
return true;
@@ -109,8 +108,8 @@ public class WrappedAbility extends Ability implements ISpellAbility {
}
@Override
public void setAllTriggeringObjects(final HashMap<String, Object> triggeredObjects) {
sa.setAllTriggeringObjects(triggeredObjects);
public void setTriggeringObjects(final HashMap<String, Object> triggeredObjects) {
sa.setTriggeringObjects(triggeredObjects);
}
@Override
@@ -178,11 +177,6 @@ public class WrappedAbility extends Ability implements ISpellAbility {
return sa.getRestrictions();
}
@Override
public Card getHostCard() {
return sa.getHostCard();
}
@Override
public String getStackDescription() {
final StringBuilder sb = new StringBuilder(regtrig.toString());

View File

@@ -191,7 +191,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
// Add all Frozen Abilities onto the stack
while (!this.frozenStack.isEmpty()) {
final SpellAbility sa = this.frozenStack.pop().getSpellAbility();
final SpellAbility sa = this.frozenStack.pop().getSpellAbility(true);
this.add(sa);
}
// Add all waiting triggers onto the stack
@@ -427,15 +427,15 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
runParams.put("Cost", sp.getPayCosts());
runParams.put("Player", sp.getHostCard().getController());
runParams.put("Activator", sp.getActivatingPlayer());
runParams.put("CastSA", si.getSpellAbility());
runParams.put("CastSACMC", si.getSpellAbility().getHostCard().getCMC());
runParams.put("CastSA", si.getSpellAbility(true));
runParams.put("CastSACMC", si.getSpellAbility(true).getHostCard().getCMC());
runParams.put("CurrentStormCount", game.getStack().getCardsCastThisTurn().size());
game.getTriggerHandler().runTrigger(TriggerType.SpellAbilityCast, runParams, true);
// Run SpellCast triggers
if (sp.isSpell()) {
game.getTriggerHandler().runTrigger(TriggerType.SpellCast, runParams, true);
this.executeCastCommand(si.getSpellAbility().getHostCard());
this.executeCastCommand(si.getSpellAbility(true).getHostCard());
}
// Run AbilityCast triggers
@@ -458,7 +458,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
runParams = new HashMap<String, Object>();
SpellAbility s = sp;
if (si != null) {
s = si.getSpellAbility();
s = si.getSpellAbility(true);
}
runParams.put("SourceSA", s);
HashSet<Object> distinctObjects = new HashSet<Object>();
@@ -780,13 +780,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
}
public final SpellAbility peekAbility() {
return this.stack.peekFirst().getSpellAbility();
return this.stack.peekFirst().getSpellAbility(true);
}
public final void remove(final SpellAbilityStackInstance si) {
this.stack.remove(si);
this.frozenStack.remove(si);
game.fireEvent(new GameEventSpellRemovedFromStack(si.getSpellAbility()));
game.fireEvent(new GameEventSpellRemovedFromStack(si.getSpellAbility(true)));
}
public final SpellAbilityStackInstance getInstanceFromSpellAbility(final SpellAbility sa) {

View File

@@ -0,0 +1,21 @@
package forge.trackable;
import java.util.LinkedHashSet;
@SuppressWarnings("serial")
public class TrackableCollection<T extends TrackableObject<?>> extends LinkedHashSet<T> { //use linked hash set so order is maintained
public TrackableCollection() {
}
@Override
public boolean add(T item) {
//TODO: Track change
return super.add(item);
}
@Override
public boolean remove(Object item) {
//TODO: Track change
return super.remove(item);
}
}

View File

@@ -0,0 +1,9 @@
package forge.trackable;
import java.util.HashMap;
@SuppressWarnings("serial")
public class TrackableIndex<T extends TrackableObject<?>> extends HashMap<Integer, T> {
public TrackableIndex() {
}
}

View File

@@ -0,0 +1,48 @@
package forge.trackable;
import java.util.EnumMap;
import java.util.EnumSet;
import forge.game.IIdentifiable;
//base class for objects that can be tracked and synced between game server and GUI
public abstract class TrackableObject<E extends Enum<E>> implements IIdentifiable {
private final int id;
private final EnumMap<E, Object> props;
private final EnumSet<E> changedProps;
protected TrackableObject(int id0, Class<E> propEnum0) {
id = id0;
props = new EnumMap<E, Object>(propEnum0);
changedProps = EnumSet.noneOf(propEnum0);
}
public int getId() {
return id;
}
@Override
public int hashCode() {
return id;
}
@SuppressWarnings("unchecked")
protected <T> T get(E key) {
return (T)props.get(key);
}
protected <T> void set(E key, T value) {
if (value == null) {
if (props.remove(key) != null) {
changedProps.add(key);
}
}
else if (!value.equals(props.put(key, value))) {
changedProps.add(key);
}
}
//use when updating collection type properties with using set
protected void flagAsChanged(E key) {
changedProps.add(key);
}
}

View File

@@ -0,0 +1,118 @@
package forge.trackable;
public class TrackableProperty {
public enum CardProp {
Owner,
Controller,
Zone,
Cloned,
FaceDown,
FlipCard,
Flipped,
SplitCard,
Transformed,
SetCode,
Rarity,
Attacking,
Blocking,
PhasedOut,
Sickness,
Tapped,
Token,
Counters,
Damage,
AssignedDamage,
ShieldCount,
PreventNextDamage,
ChosenType,
ChosenColors,
ChosenPlayer,
NamedCard,
Equipping,
EquippedBy,
Enchanting,
EnchantedBy,
Fortifying,
FortifiedBy,
GainControlTargets,
CloneOrigin,
Imprinted,
HauntedBy,
Haunting,
MustBlock,
PairedWith,
Original,
Alternate
}
public enum CardStateProp {
Name,
Colors,
ImageKey,
Type,
ManaCost,
Power,
Toughness,
Loyalty,
Text,
ChangedColorWords,
ChangedTypes,
HasDeathtouch,
HasHaste,
HasInfect,
HasStorm,
HasTrample,
FoilIndex
}
public enum PlayerProp {
LobbyPlayer,
Opponents,
Life,
PoisonCounters,
MaxHandSize,
HasUnlimitedHandSize,
NumDrawnThisTurn,
PreventNextDamage,
Keywords,
CommanderInfo,
Ante,
Battlefield,
Command,
Exile,
Flashback,
Graveyard,
Hand,
Library,
Mana
}
public enum SpellAbilityProp {
HostCard,
Description,
CanPlay,
PromptIfOnlyPossibleAbility
}
public enum StackItemProp {
Key,
SourceTrigger,
Text,
SourceCard,
Activator,
TargetCards,
TargetPlayers,
SubInstance,
Ability,
OptionalTrigger
}
public enum CombatProp {
AttackersWithDefenders,
AttackersWithBlockers,
BandsWithDefenders,
BandsWithBlockers,
AttackersWithPlannedBlockers,
BandsWithPlannedBlockers
}
}

View File

@@ -575,7 +575,7 @@ public class PlayerControllerForTests extends PlayerController {
@Override
public CardShields chooseRegenerationShield(Card c) {
return Iterables.getFirst(c.getShield(), null);
return Iterables.getFirst(c.getShields(), null);
}
@Override

View File

@@ -5,6 +5,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
@@ -167,7 +168,7 @@ public class CardDetailUtil {
}
public static String formatCardType(final CardStateView card) {
final List<String> list = card.getType();
final Set<String> list = card.getType();
final StringBuilder sb = new StringBuilder();
final List<String> superTypes = new ArrayList<String>();

View File

@@ -82,7 +82,7 @@ public class InputBlock extends InputSyncronizedBase {
showMessage("Select another attacker to declare blockers for.");
}
else {
String attackerName = currentAttacker.isFaceDown() ? "Morph" : currentAttacker.getName() + " (" + currentAttacker.getUniqueNumber() + ")";
String attackerName = currentAttacker.isFaceDown() ? "Morph" : currentAttacker.getName() + " (" + currentAttacker.getId() + ")";
String message = "Select creatures to block " + attackerName + " or select another attacker to declare blockers for.";
showMessage(message);
}

View File

@@ -327,7 +327,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
for (SpellAbilityStackInstance si : game.getStack()) {
final Card stC = si.getSourceCard();
final SpellAbility stSA = si.getSpellAbility().getRootAbility();
final SpellAbility stSA = si.getSpellAbility(true).getRootAbility();
if (stC.isValid(cost.getType().split(";"), ability.getActivatingPlayer(), source) && stSA.isSpell()) {
saList.add(stSA);
if (stC.isCopiedSpell()) {

View File

@@ -1275,10 +1275,14 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public CardShields chooseRegenerationShield(Card c) {
if (c.getShield().size() < 2) {
return Iterables.getFirst(c.getShield(), null);
if (c.getShieldCount() < 2) {
return Iterables.getFirst(c.getShields(), null);
}
return SGuiChoose.one(getGui(), "Choose a regeneration shield:", c.getShield());
ArrayList<CardShields> shields = new ArrayList<CardShields>();
for (CardShields shield : c.getShields()) {
shields.add(shield);
}
return SGuiChoose.one(getGui(), "Choose a regeneration shield:", shields);
}
@Override

View File

@@ -318,7 +318,7 @@ public class TargetSelection {
final Game game = ability.getActivatingPlayer().getGame();
for (final SpellAbilityStackInstance si : game.getStack()) {
SpellAbility abilityOnStack = si.getSpellAbility();
SpellAbility abilityOnStack = si.getSpellAbility(true);
if (ability.equals(abilityOnStack)) {
// By peeking at stack item, target is set to its SI state. So set it back before adding targets
ability.resetTargets();
@@ -346,7 +346,7 @@ public class TargetSelection {
return false;
}
if (madeChoice instanceof StackItemView) {
ability.getTargets().add(controller.getStackItem((StackItemView)madeChoice).getSpellAbility());
ability.getTargets().add(controller.getStackItem((StackItemView)madeChoice).getSpellAbility(true));
} else // 'FINISH TARGETING' chosen
bTargetingDone = true;
}

View File

@@ -3,6 +3,7 @@ package forge.view;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
@@ -751,7 +752,7 @@ public class CardView extends GameEntityView {
private String name;
private ColorSet colors;
private String imageKey;
private List<String> type;
private Set<String> type;
private ManaCost manaCost;
private int power, toughness, loyalty;
private String text;
@@ -768,7 +769,7 @@ public class CardView extends GameEntityView {
this.name = "";
this.colors = ColorSet.getNullColor();
this.imageKey = ImageKeys.HIDDEN_CARD;
this.type = Collections.emptyList();
this.type = Collections.emptySet();
this.manaCost = ManaCost.NO_COST;
this.power = 0;
this.toughness = 0;
@@ -837,15 +838,15 @@ public class CardView extends GameEntityView {
/**
* @return the type
*/
public List<String> getType() {
public Set<String> getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(final List<String> type) {
this.type = Collections.unmodifiableList(type);
public void setType(final Set<String> type) {
this.type = type;
}
/**

View File

@@ -484,7 +484,7 @@ public abstract class LocalGameView implements IGameView {
view.setAttacking(combat != null && combat.isAttacking(c));
view.setBlocking(combat != null && combat.isBlocking(c));
view.setChosenPlayer(getPlayerView(c.getChosenPlayer(), false));
view.setEquipping(getCardView(Iterables.getFirst(c.getEquipping(), null), false));
view.setEquipping(getCardView(c.getEquipping(), false));
view.setEquippedBy(getCardViews(c.getEquippedBy(), false));
view.setEnchantingCard(getCardView(c.getEnchantingCard(), false));
view.setEnchantingPlayer(getPlayerView(c.getEnchantingPlayer(), false));

View File

@@ -27,8 +27,8 @@ public class StackItemView implements IIdentifiable {
public StackItemView(SpellAbilityStackInstance si, LocalGameView gameView) {
id = si.getId();
key = si.getSpellAbility().toUnsuppressedString();
sourceTrigger = si.getSpellAbility().getSourceTrigger();
key = si.getSpellAbility(false).toUnsuppressedString();
sourceTrigger = si.getSpellAbility(false).getSourceTrigger();
text = si.getStackDescription();
source = gameView.getCardView(si.getSourceCard(), true);
activatingPlayer = gameView.getPlayerView(si.getActivator(), false);

View File

@@ -1,6 +1,5 @@
package forge.view;
import java.util.Collections;
import java.util.List;
import com.google.common.base.Function;
@@ -53,10 +52,10 @@ public final class ViewUtil {
view.setCounters(c.getCounters());
view.setDamage(c.getDamage());
view.setAssignedDamage(c.getTotalAssignedDamage());
view.setRegenerationShields(c.getShield().size());
view.setRegenerationShields(c.getShieldCount());
view.setPreventNextDamage(c.getPreventNextDamageTotalShields());
view.setChosenType(c.getChosenType());
view.setChosenColors(c.getChosenColor());
view.setChosenColors(c.getChosenColors());
view.setNamedCard(c.getNamedCard());
if (c.isSplitCard()) {
@@ -78,7 +77,7 @@ public final class ViewUtil {
origView.setName(c.getName());
origView.setColors(c.determineColor());
origView.setImageKey(c.getImageKey() );
origView.setType(Collections.unmodifiableList(c.getType()));
origView.setType(c.getType());
origView.setManaCost(c.getManaCost());
origView.setPower(c.getNetAttack());
origView.setToughness(c.getNetDefense());
@@ -119,7 +118,7 @@ public final class ViewUtil {
view.setName(chars.getName());
view.setColors(chars.determineColor());
view.setImageKey(chars.getImageKey());
view.setType(Collections.unmodifiableList(chars.getType()));
view.setType(chars.getType());
view.setManaCost(chars.getManaCost());
view.setPower(chars.getBaseAttack());
view.setToughness(chars.getBaseDefense());