mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
CloneRewrite
This commit is contained in:
@@ -1142,7 +1142,7 @@ public abstract class GameState {
|
||||
} else if (info.startsWith("SummonSick")) {
|
||||
c.setSickness(true);
|
||||
} else if (info.startsWith("FaceDown")) {
|
||||
c.setState(CardStateName.FaceDown, true);
|
||||
c.turnFaceDown(true);
|
||||
if (info.endsWith("Manifested")) {
|
||||
c.setManifested(true);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -21,7 +20,6 @@ public class CloneAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
@@ -59,7 +57,7 @@ public class CloneAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null == tgt) {
|
||||
if (!sa.usesTargeting()) {
|
||||
final List<Card> defined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||
|
||||
boolean bFlag = false;
|
||||
@@ -186,9 +184,13 @@ public class CloneAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
|
||||
final Card host = sa.getHostCard();
|
||||
final Player ctrl = host.getController();
|
||||
|
||||
final Card cloneTarget = getCloneTarget(sa);
|
||||
final boolean isOpp = cloneTarget.getController().isOpponentOf(sa.getActivatingPlayer());
|
||||
|
||||
final boolean isVesuva = "Vesuva".equals(host.getName());
|
||||
|
||||
final String filter = !isVesuva ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
|
||||
@@ -198,7 +200,8 @@ public class CloneAi extends SpellAbilityAi {
|
||||
if (!newOptions.isEmpty()) {
|
||||
options = newOptions;
|
||||
}
|
||||
Card choice = ComputerUtilCard.getBestAI(options);
|
||||
Card choice = isOpp ? ComputerUtilCard.getWorstAI(options) : ComputerUtilCard.getBestAI(options);
|
||||
|
||||
if (isVesuva && "Vesuva".equals(choice.getName())) {
|
||||
choice = null;
|
||||
}
|
||||
@@ -206,4 +209,18 @@ public class CloneAi extends SpellAbilityAi {
|
||||
return choice;
|
||||
}
|
||||
|
||||
protected Card getCloneTarget(final SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
Card tgtCard = host;
|
||||
if (sa.hasParam("CloneTarget")) {
|
||||
final List<Card> cloneTargets = AbilityUtils.getDefinedCards(host, sa.getParam("CloneTarget"), sa);
|
||||
if (!cloneTargets.isEmpty()) {
|
||||
tgtCard = cloneTargets.get(0);
|
||||
}
|
||||
} else if (sa.hasParam("Choices") && sa.usesTargeting()) {
|
||||
tgtCard = sa.getTargets().getFirstTargetedCard();
|
||||
}
|
||||
|
||||
return tgtCard;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class ManifestAi extends SpellAbilityAi {
|
||||
// check to ensure that there are no replacement effects that prevent creatures ETBing from library
|
||||
// (e.g. Grafdigger's Cage)
|
||||
Card topCopy = CardUtil.getLKICopy(library.getFirst());
|
||||
topCopy.setState(CardStateName.FaceDown, false);
|
||||
topCopy.turnFaceDownNoUpdate();
|
||||
topCopy.setManifested(true);
|
||||
|
||||
final Map<String, Object> repParams = Maps.newHashMap();
|
||||
|
||||
@@ -305,7 +305,7 @@ public class GameCopier {
|
||||
if (c.isFaceDown()) {
|
||||
boolean isCreature = newCard.isCreature();
|
||||
boolean hasManaCost = !newCard.getManaCost().isNoCost();
|
||||
newCard.setState(CardStateName.FaceDown, true);
|
||||
newCard.turnFaceDown(true);
|
||||
if (c.isManifested()) {
|
||||
newCard.setManifested(true);
|
||||
// TODO: Should be able to copy other abilities...
|
||||
|
||||
@@ -5,13 +5,13 @@ public enum CardStateName {
|
||||
Original,
|
||||
FaceDown,
|
||||
Flipped,
|
||||
Cloner,
|
||||
Transformed,
|
||||
Meld,
|
||||
Cloned,
|
||||
LeftSplit,
|
||||
RightSplit,
|
||||
OriginalText; // backup state for cards like Volrath's Shapeshifter
|
||||
OriginalText, // backup state for cards like Volrath's Shapeshifter
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
|
||||
@@ -194,17 +194,7 @@ public class GameAction {
|
||||
|
||||
if (!c.isToken()) {
|
||||
if (c.isCloned()) {
|
||||
c.switchStates(CardStateName.Original, CardStateName.Cloner, false);
|
||||
c.setState(CardStateName.Original, false);
|
||||
c.clearStates(CardStateName.Cloner, false);
|
||||
if (c.isFlipCard()) {
|
||||
c.clearStates(CardStateName.Flipped, false);
|
||||
}
|
||||
if (c.getStates().contains(CardStateName.OriginalText)) {
|
||||
c.clearStates(CardStateName.OriginalText, false);
|
||||
c.removeSVar("GainingTextFrom");
|
||||
c.removeSVar("GainingTextFromTimestamp");
|
||||
}
|
||||
c.removeCloneStates();
|
||||
c.updateStateForView();
|
||||
} else if (c.getStates().contains(CardStateName.OriginalText)) {
|
||||
// Volrath's Shapeshifter
|
||||
|
||||
@@ -178,7 +178,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
||||
movedCard.setExiledWith(host);
|
||||
}
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
movedCard.setState(CardStateName.FaceDown, true);
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
if (sa.hasParam("Tapped")) {
|
||||
movedCard.setTapped(true);
|
||||
|
||||
@@ -542,7 +542,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
movedCard.updateStateForView();
|
||||
}
|
||||
if (sa.hasParam("FaceDown")) {
|
||||
movedCard.setState(CardStateName.FaceDown, true);
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
if (sa.hasParam("Attacking")) {
|
||||
// What should they attack?
|
||||
@@ -592,7 +592,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
movedCard.setState(CardStateName.FaceDown, true);
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
|
||||
if (sa.hasParam("TrackDiscarded")) {
|
||||
@@ -1050,24 +1050,22 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
// need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger
|
||||
if (sa.hasParam("FaceDown") && ZoneType.Battlefield.equals(destination)) {
|
||||
c.setState(CardStateName.FaceDown, true);
|
||||
c.turnFaceDown(true);
|
||||
|
||||
// set New Pt doesn't work because this values need to be copyable for clone effects
|
||||
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")) {
|
||||
if (sa.hasParam("FaceDownPower")) {
|
||||
c.setBasePower(AbilityUtils.calculateAmount(
|
||||
source, sa.getParam("FaceDownPower"), sa));
|
||||
}
|
||||
if (sa.hasParam("FaceDownToughness")) {
|
||||
c.setBaseToughness(AbilityUtils.calculateAmount(
|
||||
source, sa.getParam("FaceDownToughness"), sa));
|
||||
}
|
||||
if (sa.hasParam("FaceDownPower")) {
|
||||
c.setBasePower(AbilityUtils.calculateAmount(
|
||||
source, sa.getParam("FaceDownPower"), sa));
|
||||
}
|
||||
if (sa.hasParam("FaceDownToughness")) {
|
||||
c.setBaseToughness(AbilityUtils.calculateAmount(
|
||||
source, sa.getParam("FaceDownToughness"), sa));
|
||||
}
|
||||
|
||||
if (sa.hasParam("FaceDownAddType")) {
|
||||
CardType t = new CardType(c.getCurrentState().getType());
|
||||
t.addAll(Arrays.asList(sa.getParam("FaceDownAddType").split(",")));
|
||||
c.getCurrentState().setType(t);
|
||||
for (String type : sa.getParam("FaceDownAddType").split(",")) {
|
||||
c.addType(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|
||||
@@ -1091,7 +1089,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
// need to do that again?
|
||||
if (sa.hasParam("FaceDown") && !ZoneType.Battlefield.equals(destination)) {
|
||||
movedCard.setState(CardStateName.FaceDown, true);
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
movedCard.setTimestamp(ts);
|
||||
}
|
||||
@@ -1105,7 +1103,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
movedCard.setExiledWith(host);
|
||||
}
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
movedCard.setState(CardStateName.FaceDown, true);
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.*;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CloneEffect extends SpellAbilityEffect {
|
||||
// TODO update this method
|
||||
@@ -57,7 +47,6 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
final Card host = sa.getHostCard();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
Card tgtCard = host;
|
||||
final Map<String, String> origSVars = host.getSVars();
|
||||
final Game game = activator.getGame();
|
||||
|
||||
// find cloning source i.e. thing to be copied
|
||||
@@ -91,9 +80,14 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// find target of cloning i.e. card becoming a clone
|
||||
final List<Card> cloneTargets = AbilityUtils.getDefinedCards(host, sa.getParam("CloneTarget"), sa);
|
||||
if (!cloneTargets.isEmpty()) {
|
||||
tgtCard = cloneTargets.get(0);
|
||||
if (sa.hasParam("CloneTarget")) {
|
||||
final List<Card> cloneTargets = AbilityUtils.getDefinedCards(host, sa.getParam("CloneTarget"), sa);
|
||||
if (!cloneTargets.isEmpty()) {
|
||||
tgtCard = cloneTargets.get(0);
|
||||
game.getTriggerHandler().clearInstrinsicActiveTriggers(tgtCard, null);
|
||||
}
|
||||
} else if (sa.hasParam("Choices") && sa.usesTargeting()) {
|
||||
tgtCard = sa.getTargets().getFirstTargetedCard();
|
||||
game.getTriggerHandler().clearInstrinsicActiveTriggers(tgtCard, null);
|
||||
}
|
||||
|
||||
@@ -103,96 +97,12 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
// determine the image to be used for the clone
|
||||
String imageFileName = cardToCopy.getGame().getRules().canCloneUseTargetsImage() ? tgtCard.getImageKey() : cardToCopy.getImageKey();
|
||||
if (sa.hasParam("ImageSource")) { // Allow the image to be stipulated by using a defined card source
|
||||
List<Card> cloneImgSources = AbilityUtils.getDefinedCards(host, sa.getParam("ImageSource"), sa);
|
||||
if (!cloneImgSources.isEmpty()) {
|
||||
imageFileName = cloneImgSources.get(0).getImageKey();
|
||||
}
|
||||
}
|
||||
final Long ts = game.getNextTimestamp();
|
||||
tgtCard.addCloneState(CardFactory.getCloneStates(cardToCopy, tgtCard, sa), ts);
|
||||
|
||||
final boolean keepName = sa.hasParam("KeepName");
|
||||
final String newName = sa.getParamOrDefault("NewName", null);
|
||||
final String originalName = tgtCard.getName();
|
||||
final boolean copyingSelf = (tgtCard == cardToCopy);
|
||||
final boolean isTransformed = cardToCopy.getCurrentStateName() == CardStateName.Transformed || cardToCopy.getCurrentStateName() == CardStateName.Meld || cardToCopy.getCurrentStateName() == CardStateName.Flipped;
|
||||
final CardStateName origState = isTransformed || cardToCopy.isFaceDown() ? CardStateName.Original : cardToCopy.getCurrentStateName();
|
||||
|
||||
if (!copyingSelf) {
|
||||
if (tgtCard.isCloned()) { // cloning again
|
||||
tgtCard.switchStates(CardStateName.Cloner, origState, false);
|
||||
tgtCard.setState(origState, false);
|
||||
tgtCard.clearStates(CardStateName.Cloner, false);
|
||||
}
|
||||
// add "Cloner" state to clone
|
||||
tgtCard.addAlternateState(CardStateName.Cloner, false);
|
||||
tgtCard.switchStates(origState, CardStateName.Cloner, false);
|
||||
tgtCard.setState(origState, false);
|
||||
} else {
|
||||
//copy Original state to Cloned
|
||||
tgtCard.addAlternateState(CardStateName.Cloned, false);
|
||||
tgtCard.switchStates(origState, CardStateName.Cloned, false);
|
||||
if (tgtCard.isFlipCard()) {
|
||||
tgtCard.setState(CardStateName.Original, false);
|
||||
}
|
||||
}
|
||||
|
||||
CardFactory.copyCopiableCharacteristics(cardToCopy, tgtCard);
|
||||
|
||||
// add extra abilities as granted by the copy effect
|
||||
addExtraCharacteristics(tgtCard, sa, origSVars);
|
||||
|
||||
// set the host card for copied replacement effects
|
||||
// needed for copied xPaid ETB effects (for the copy, xPaid = 0)
|
||||
for (final ReplacementEffect rep : tgtCard.getReplacementEffects()) {
|
||||
final SpellAbility newSa = rep.getOverridingAbility();
|
||||
if (newSa != null) {
|
||||
newSa.setOriginalHost(cardToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
// set the host card for copied spellabilities
|
||||
for (final SpellAbility newSa : tgtCard.getSpellAbilities()) {
|
||||
newSa.setOriginalHost(cardToCopy);
|
||||
}
|
||||
|
||||
// restore name if it should be unchanged
|
||||
// this should only be used for Sakashima the Impostor Avatar
|
||||
if (keepName) {
|
||||
tgtCard.setName(originalName);
|
||||
}
|
||||
if (newName != null) {
|
||||
tgtCard.setName(newName);
|
||||
}
|
||||
|
||||
// If target is a flip card, also set characteristics of the flipped
|
||||
// state.
|
||||
if (cardToCopy.isFlipCard()) {
|
||||
final CardState flippedState = tgtCard.getState(CardStateName.Flipped);
|
||||
if (keepName) {
|
||||
flippedState.setName(originalName);
|
||||
}
|
||||
if (newName != null) {
|
||||
tgtCard.setName(newName);
|
||||
}
|
||||
//keep the Clone card image for the cloned card
|
||||
flippedState.setImageKey(imageFileName);
|
||||
}
|
||||
|
||||
//Clean up copy of cloned state
|
||||
if (copyingSelf) {
|
||||
tgtCard.clearStates(CardStateName.Cloned, false);
|
||||
}
|
||||
|
||||
//game.getTriggerHandler().registerActiveTrigger(tgtCard, false);
|
||||
|
||||
//keep the Clone card image for the cloned card
|
||||
if (cardToCopy.isFlipCard() && tgtCard.getCurrentStateName() != CardStateName.Flipped) {
|
||||
//for a flip card that isn't flipped, load the original image
|
||||
tgtCard.setImageKey(cardToCopy.getImageKey(CardStateName.Original));
|
||||
} else {
|
||||
tgtCard.setImageKey(imageFileName);
|
||||
// set ETB tapped of clone
|
||||
if (sa.hasParam("IntoPlayTapped")) {
|
||||
tgtCard.setTapped(true);
|
||||
}
|
||||
|
||||
tgtCard.updateStateForView();
|
||||
@@ -213,10 +123,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (cloneCard.isCloned()) {
|
||||
cloneCard.setState(CardStateName.Cloner, false);
|
||||
cloneCard.switchStates(CardStateName.Cloner, origState, false);
|
||||
cloneCard.clearStates(CardStateName.Cloner, false);
|
||||
if (cloneCard.removeCloneState(ts)) {
|
||||
cloneCard.updateStateForView();
|
||||
game.fireEvent(new GameEventCardStatsChanged(cloneCard));
|
||||
}
|
||||
@@ -237,144 +144,4 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
game.fireEvent(new GameEventCardStatsChanged(tgtCard));
|
||||
} // cloneResolve
|
||||
|
||||
private static void addExtraCharacteristics(final Card tgtCard, final SpellAbility sa, final Map<String, String> origSVars) {
|
||||
// additional types to clone
|
||||
if (sa.hasParam("AddTypes")) {
|
||||
for (final String type : Arrays.asList(sa.getParam("AddTypes").split(","))) {
|
||||
tgtCard.addType(type);
|
||||
}
|
||||
}
|
||||
|
||||
// triggers to add to clone
|
||||
if (sa.hasParam("AddTriggers")) {
|
||||
for (final String s : Arrays.asList(sa.getParam("AddTriggers").split(","))) {
|
||||
if (origSVars.containsKey(s)) {
|
||||
final String actualTrigger = origSVars.get(s);
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, tgtCard, true);
|
||||
tgtCard.addTrigger(parsedTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SVars to add to clone
|
||||
if (sa.hasParam("AddSVars")) {
|
||||
for (final String s : Arrays.asList(sa.getParam("AddSVars").split(","))) {
|
||||
if (origSVars.containsKey(s)) {
|
||||
final String actualsVar = origSVars.get(s);
|
||||
tgtCard.setSVar(s, actualsVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// abilities to add to clone
|
||||
if (sa.hasParam("AddAbilities")) {
|
||||
for (final String s : Arrays.asList(sa.getParam("AddAbilities").split(","))) {
|
||||
if (origSVars.containsKey(s)) {
|
||||
final String actualAbility = origSVars.get(s);
|
||||
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, tgtCard);
|
||||
tgtCard.addSpellAbility(grantedAbility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keywords to add to clone
|
||||
|
||||
if (sa.hasParam("AddKeywords")) {
|
||||
final List<String> keywords = Arrays.asList(sa.getParam("AddKeywords").split(" & "));
|
||||
// allow SVar substitution for keywords
|
||||
for (int i = 0; i < keywords.size(); i++) {
|
||||
String k = keywords.get(i);
|
||||
if (origSVars.containsKey(k)) {
|
||||
keywords.add("\"" + k + "\"");
|
||||
keywords.remove(k);
|
||||
}
|
||||
k = keywords.get(i);
|
||||
|
||||
tgtCard.addIntrinsicKeyword(k);
|
||||
}
|
||||
}
|
||||
|
||||
// set ETB tapped of clone
|
||||
if (sa.hasParam("IntoPlayTapped")) {
|
||||
tgtCard.setTapped(true);
|
||||
}
|
||||
|
||||
// set power of clone
|
||||
if (sa.hasParam("SetPower")) {
|
||||
String rhs = sa.getParam("SetPower");
|
||||
int power = Integer.MAX_VALUE;
|
||||
try {
|
||||
power = Integer.parseInt(rhs);
|
||||
} catch (final NumberFormatException e) {
|
||||
power = CardFactoryUtil.xCount(tgtCard, tgtCard.getSVar(rhs));
|
||||
}
|
||||
for (StaticAbility sta : tgtCard.getStaticAbilities()) {
|
||||
Map<String, String> params = sta.getMapParams();
|
||||
if (params.containsKey("CharacteristicDefining") && params.containsKey("SetPower"))
|
||||
tgtCard.removeStaticAbility(sta);
|
||||
}
|
||||
tgtCard.setBasePower(power);
|
||||
}
|
||||
|
||||
// set toughness of clone
|
||||
if (sa.hasParam("SetToughness")) {
|
||||
String rhs = sa.getParam("SetToughness");
|
||||
int toughness = Integer.MAX_VALUE;
|
||||
try {
|
||||
toughness = Integer.parseInt(rhs);
|
||||
} catch (final NumberFormatException e) {
|
||||
toughness = CardFactoryUtil.xCount(tgtCard, tgtCard.getSVar(rhs));
|
||||
}
|
||||
for (StaticAbility sta : tgtCard.getStaticAbilities()) {
|
||||
Map<String, String> params = sta.getMapParams();
|
||||
if (params.containsKey("CharacteristicDefining") && params.containsKey("SetToughness"))
|
||||
tgtCard.removeStaticAbility(sta);
|
||||
}
|
||||
tgtCard.setBaseToughness(toughness);
|
||||
}
|
||||
|
||||
// colors to be added or changed to
|
||||
String shortColors = "";
|
||||
if (sa.hasParam("Colors")) {
|
||||
final String colors = sa.getParam("Colors");
|
||||
if (colors.equals("ChosenColor")) {
|
||||
shortColors = CardUtil.getShortColorsString(tgtCard.getChosenColors());
|
||||
} else {
|
||||
shortColors = CardUtil.getShortColorsString(Arrays.asList(colors.split(",")));
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("OverwriteColors")) {
|
||||
tgtCard.setColor(shortColors);
|
||||
} else {
|
||||
// TODO: this actually doesn't work for some reason (and fiddling with timestamps doesn't seem to fix it).
|
||||
// No cards currently use this, but if some ever do, this code will require tweaking.
|
||||
tgtCard.addColor(shortColors, true, tgtCard.getTimestamp());
|
||||
}
|
||||
|
||||
if (sa.hasParam("Embalm") && tgtCard.isEmbalmed()) {
|
||||
tgtCard.addType("Zombie");
|
||||
tgtCard.setColor(MagicColor.WHITE);
|
||||
tgtCard.setManaCost(ManaCost.NO_COST);
|
||||
}
|
||||
if (sa.hasParam("Eternalize") && tgtCard.isEternalized()) {
|
||||
tgtCard.addType("Zombie");
|
||||
tgtCard.setColor(MagicColor.BLACK);
|
||||
tgtCard.setManaCost(ManaCost.NO_COST);
|
||||
tgtCard.setBasePower(4);
|
||||
tgtCard.setBaseToughness(4);
|
||||
}
|
||||
|
||||
if (sa.hasParam("GainThisAbility")) {
|
||||
SpellAbility root = sa.getRootAbility();
|
||||
|
||||
if (root.isTrigger() && root.getTrigger() != null) {
|
||||
tgtCard.addTrigger(root.getTrigger().copy(tgtCard, false));
|
||||
} else if (root.isReplacementAbility()) {
|
||||
tgtCard.addReplacementEffect(root.getReplacementEffect().copy(tgtCard, false));
|
||||
} else {
|
||||
tgtCard.addSpellAbility(root.copy(tgtCard, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
@@ -262,7 +260,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
final List<String> keywords = Lists.newArrayList();
|
||||
final List<String> types = Lists.newArrayList();
|
||||
final List<String> svars = Lists.newArrayList();
|
||||
final List<String> triggers = Lists.newArrayList();
|
||||
//final List<String> triggers = Lists.newArrayList();
|
||||
boolean asNonLegendary = false;
|
||||
|
||||
if (sa.hasParam("Keywords")) {
|
||||
@@ -277,9 +275,11 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AddSVars")) {
|
||||
svars.addAll(Arrays.asList(sa.getParam("AddSVars").split(" & ")));
|
||||
}
|
||||
/*
|
||||
if (sa.hasParam("Triggers")) {
|
||||
triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(" & ")));
|
||||
}
|
||||
//*/
|
||||
|
||||
final Card copy = CardFactory.copyCopiableCharacteristics(original, sa.getActivatingPlayer());
|
||||
copy.setToken(true);
|
||||
@@ -312,11 +312,13 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||
}
|
||||
copy.setSVar(name, actualsVar);
|
||||
}
|
||||
/*
|
||||
for (final String s : triggers) {
|
||||
final String actualTrigger = host.getSVar(s);
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, copy, true);
|
||||
copy.addTrigger(parsedTrigger);
|
||||
}
|
||||
//*/
|
||||
|
||||
// set power of clone
|
||||
if (sa.hasParam("SetPower")) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -318,7 +317,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
c.setState(CardStateName.FaceDown, true);
|
||||
c.turnFaceDown(true);
|
||||
}
|
||||
if (sa.hasParam("Imprint")) {
|
||||
host.addImprintedCard(c);
|
||||
|
||||
@@ -70,8 +70,8 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
final String imageFileName = game.getRules().canCloneUseTargetsImage() ? source.getImageKey() : random.getImageKey();
|
||||
source.addAlternateState(CardStateName.Cloner, false);
|
||||
source.switchStates(CardStateName.Original, CardStateName.Cloner, false);
|
||||
//source.addAlternateState(CardStateName.Cloner, false);
|
||||
//source.switchStates(CardStateName.Original, CardStateName.Cloner, false);
|
||||
source.setState(CardStateName.Original, false);
|
||||
source.updateStateForView();
|
||||
final CardStateName stateToCopy = random.getCurrentStateName();
|
||||
|
||||
@@ -88,7 +88,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private final Map<CardStateName, CardState> states = Maps.newEnumMap(CardStateName.class);
|
||||
private CardState currentState;
|
||||
private CardStateName currentStateName = CardStateName.Original;
|
||||
private CardStateName preFaceDownState = CardStateName.Original;
|
||||
|
||||
private ZoneType castFrom = null;
|
||||
private SpellAbility castSA = null;
|
||||
@@ -119,6 +118,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private final Map<Long, CardChangedType> changedCardTypes = Maps.newTreeMap();
|
||||
private final Map<Long, KeywordsChange> changedCardKeywords = Maps.newTreeMap();
|
||||
private final Map<Long, CardColor> changedCardColors = Maps.newTreeMap();
|
||||
private final NavigableMap<Long, CardCloneStates> clonedStates = Maps.newTreeMap();
|
||||
|
||||
// changes that say "replace each instance of one [color,type] by another - timestamp is the key of maps
|
||||
private final CardChangedWords changedTextColors = new CardChangedWords();
|
||||
@@ -170,6 +170,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
private boolean madness = false;
|
||||
private boolean madnessWithoutCast = false;
|
||||
|
||||
private boolean flipped = false;
|
||||
private boolean facedown = false;
|
||||
// set for transform and meld, needed for clone effects
|
||||
private boolean backside = false;
|
||||
|
||||
private boolean phasedOut = false;
|
||||
private boolean directlyPhasedOut = true;
|
||||
|
||||
@@ -302,25 +307,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public boolean changeToState(final CardStateName state) {
|
||||
CardStateName cur = currentStateName;
|
||||
|
||||
if (!setState(state, true)) {
|
||||
return false;
|
||||
if (hasState(state)) {
|
||||
return setState(state, true);
|
||||
}
|
||||
|
||||
if ((cur == CardStateName.Original && state == CardStateName.Transformed)
|
||||
|| (cur == CardStateName.Transformed && state == CardStateName.Original)) {
|
||||
|
||||
// Clear old dfc trigger from the trigger handler
|
||||
getGame().getTriggerHandler().clearInstrinsicActiveTriggers(this, null);
|
||||
getGame().getTriggerHandler().registerActiveTrigger(this, false);
|
||||
Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Transformer", this);
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.Transformed, runParams, false);
|
||||
this.incrementTransformedTimestamp();
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public long getTransformedTimestamp() { return transformedTimestamp; }
|
||||
@@ -366,23 +356,55 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return null;
|
||||
}
|
||||
public CardState getState(final CardStateName state) {
|
||||
CardCloneStates clStates = getLastClonedState();
|
||||
if (clStates == null) {
|
||||
return getOriginalState(state);
|
||||
} else {
|
||||
return clStates.get(state);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasState(final CardStateName state) {
|
||||
if (state == CardStateName.FaceDown) {
|
||||
return true;
|
||||
}
|
||||
CardCloneStates clStates = getLastClonedState();
|
||||
if (clStates == null) {
|
||||
return states.containsKey(state);
|
||||
} else {
|
||||
return clStates.containsKey(state);
|
||||
}
|
||||
}
|
||||
|
||||
public CardState getOriginalState(final CardStateName state) {
|
||||
if (!states.containsKey(state) && state == CardStateName.FaceDown) {
|
||||
states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this));
|
||||
}
|
||||
return states.get(state);
|
||||
}
|
||||
|
||||
public boolean setState(final CardStateName state, boolean updateView) {
|
||||
if (!states.containsKey(state)) {
|
||||
if (state == CardStateName.FaceDown) {
|
||||
// The face-down state is created lazily only when needed.
|
||||
states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this));
|
||||
} else {
|
||||
System.out.println(getName() + " tried to switch to non-existant state \"" + state + "\"!");
|
||||
return false; // Nonexistant state.
|
||||
return setState(state, updateView, false);
|
||||
}
|
||||
public boolean setState(final CardStateName state, boolean updateView, boolean forceUpdate) {
|
||||
CardCloneStates cloneStates = getLastClonedState();
|
||||
|
||||
if (cloneStates == null) {
|
||||
if (!states.containsKey(state)) {
|
||||
if (state == CardStateName.FaceDown) {
|
||||
// The face-down state is created lazily only when needed.
|
||||
states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this));
|
||||
} else {
|
||||
System.out.println(getName() + " tried to switch to non-existant state \"" + state + "\"!");
|
||||
return false; // Nonexistant state.
|
||||
}
|
||||
}
|
||||
} else if (!cloneStates.containsKey(state)) {
|
||||
throw new RuntimeException(getName() + " tried to switch to non-existant cloned state \"" + state + "\"!");
|
||||
//return false; // Nonexistant state.
|
||||
}
|
||||
|
||||
if (state.equals(currentStateName)) {
|
||||
if (state.equals(currentStateName) && !forceUpdate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -392,7 +414,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
currentStateName = state;
|
||||
currentState = states.get(state);
|
||||
currentState = cloneStates == null ? states.get(state) : cloneStates.get(state);
|
||||
|
||||
// update the host for static abilities
|
||||
for (StaticAbility sa : currentState.getStaticAbilities()) {
|
||||
@@ -432,6 +454,14 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return currentStateName;
|
||||
}
|
||||
|
||||
// use by CopyPermament
|
||||
public void setStates(Map<CardStateName, CardState> map) {
|
||||
states.clear();
|
||||
states.putAll(map);
|
||||
}
|
||||
|
||||
// was only used for Clone Effects
|
||||
@Deprecated
|
||||
public void switchStates(final CardStateName from, final CardStateName to, boolean updateView) {
|
||||
final CardState tmp = states.get(from);
|
||||
states.put(from, states.get(to));
|
||||
@@ -445,7 +475,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public final void addAlternateState(final CardStateName state, final boolean updateView) {
|
||||
states.put(state, new CardState(view.createAlternateState(state), this));
|
||||
states.put(state, new CardState(this, state));
|
||||
if (updateView) {
|
||||
view.updateState(this);
|
||||
}
|
||||
@@ -483,10 +513,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
currentState.getView().updateType(currentState);
|
||||
}
|
||||
|
||||
public void setPreFaceDownState(CardStateName preCharacteristic) {
|
||||
preFaceDownState = preCharacteristic;
|
||||
}
|
||||
|
||||
public boolean changeCardState(final String mode, final String customState) {
|
||||
if (mode == null)
|
||||
return changeToState(CardStateName.smartValueOf(customState));
|
||||
@@ -501,26 +527,51 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardStateName destState = oldState == CardStateName.Transformed ? CardStateName.Original : CardStateName.Transformed;
|
||||
backside = !backside;
|
||||
|
||||
return changeToState(destState);
|
||||
boolean result = changeToState(backside ? CardStateName.Transformed : CardStateName.Original);
|
||||
|
||||
// do the Transform trigger there, it can also happen if the resulting state doesn't change
|
||||
|
||||
// Clear old dfc trigger from the trigger handler
|
||||
getGame().getTriggerHandler().clearInstrinsicActiveTriggers(this, null);
|
||||
getGame().getTriggerHandler().registerActiveTrigger(this, false);
|
||||
Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Transformer", this);
|
||||
getGame().getTriggerHandler().runTrigger(TriggerType.Transformed, runParams, false);
|
||||
incrementTransformedTimestamp();
|
||||
|
||||
return result;
|
||||
|
||||
} else if (mode.equals("Flip") && isFlipCard()) {
|
||||
CardStateName destState = oldState == CardStateName.Flipped ? CardStateName.Original : CardStateName.Flipped;
|
||||
return changeToState(destState);
|
||||
// 709.4. Flipping a permanent is a one-way process.
|
||||
if (isFlipped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
flipped = true;
|
||||
|
||||
// a facedown card does flip but the state doesn't change
|
||||
if (isFaceDown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return changeToState(CardStateName.Flipped);
|
||||
} else if (mode.equals("TurnFace")) {
|
||||
if (oldState == CardStateName.Original) {
|
||||
if (oldState == CardStateName.Original || oldState == CardStateName.Flipped) {
|
||||
// Reset cloned state if Vesuvan Shapeshifter
|
||||
/*
|
||||
if (isCloned() && getState(CardStateName.Cloner).getName().equals("Vesuvan Shapeshifter")) {
|
||||
switchStates(CardStateName.Cloner, CardStateName.Original, false);
|
||||
setState(CardStateName.Original, false);
|
||||
clearStates(CardStateName.Cloner, false);
|
||||
}
|
||||
//*/
|
||||
return turnFaceDown();
|
||||
} else if (oldState == CardStateName.FaceDown) {
|
||||
} else if (isFaceDown()) {
|
||||
return turnFaceUp();
|
||||
}
|
||||
} else if (mode.equals("Meld") && hasAlternateState()) {
|
||||
} else if (mode.equals("Meld") && isMeldable()) {
|
||||
return changeToState(CardStateName.Meld);
|
||||
}
|
||||
return false;
|
||||
@@ -543,7 +594,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
setController(p, game.getNextTimestamp());
|
||||
|
||||
// Mark this card as "manifested"
|
||||
setPreFaceDownState(CardStateName.Original);
|
||||
setManifested(true);
|
||||
|
||||
Card c = game.getAction().moveToPlay(this, p, sa);
|
||||
@@ -564,14 +614,14 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
public boolean turnFaceDown(boolean override) {
|
||||
if (override || (!isDoubleFaced() && !isMeldable())) {
|
||||
preFaceDownState = currentStateName;
|
||||
facedown = true;
|
||||
return setState(CardStateName.FaceDown, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean turnFaceDownNoUpdate() {
|
||||
preFaceDownState = currentStateName;
|
||||
facedown = true;
|
||||
return setState(CardStateName.FaceDown, false);
|
||||
}
|
||||
|
||||
@@ -580,16 +630,22 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public boolean turnFaceUp(boolean manifestPaid, boolean runTriggers) {
|
||||
if (currentStateName == CardStateName.FaceDown) {
|
||||
if (manifestPaid && this.isManifested() && !this.getRules().getType().isCreature()) {
|
||||
if (isFaceDown()) {
|
||||
if (manifestPaid && isManifested() && !getRules().getType().isCreature()) {
|
||||
// If we've manifested a non-creature and we're demanifesting disallow it
|
||||
|
||||
// Unless this creature also has a Morph ability
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = setState(preFaceDownState, true);
|
||||
boolean result;
|
||||
if (isFlipped() && isFlipCard()) {
|
||||
result = setState(CardStateName.Flipped, true);
|
||||
} else {
|
||||
result = setState(CardStateName.Original, true);
|
||||
}
|
||||
|
||||
facedown = false;
|
||||
// need to run faceup commands, currently
|
||||
// it does cleanup the modified facedown state
|
||||
if (result) {
|
||||
@@ -619,10 +675,15 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardStateName oldState = getCurrentStateName();
|
||||
CardStateName destState = oldState == CardStateName.Transformed ? CardStateName.Original : CardStateName.Transformed;
|
||||
CardStateName destState = backside ? CardStateName.Original : CardStateName.Transformed;
|
||||
|
||||
if (isInPlay() && !getState(destState).getType().isPermanent()) {
|
||||
// below only when in play
|
||||
if (!isInPlay()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// use Original State for the transform check
|
||||
if (!getOriginalState(destState).getType().isPermanent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -653,8 +714,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public final boolean isInAlternateState() {
|
||||
return currentStateName != CardStateName.Original
|
||||
&& currentStateName != CardStateName.Cloned;
|
||||
return currentStateName != CardStateName.Original;
|
||||
}
|
||||
|
||||
public final boolean hasAlternateState() {
|
||||
@@ -675,23 +735,27 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public final boolean isDoubleFaced() {
|
||||
return states.containsKey(CardStateName.Transformed);
|
||||
return getRules() != null && getRules().getSplitType() == CardSplitType.Transform;
|
||||
}
|
||||
|
||||
public final boolean isMeldable() {
|
||||
return states.containsKey(CardStateName.Meld);
|
||||
return getRules() != null && getRules().getSplitType() == CardSplitType.Meld;
|
||||
}
|
||||
|
||||
public final boolean isFlipCard() {
|
||||
return states.containsKey(CardStateName.Flipped);
|
||||
return hasState(CardStateName.Flipped);
|
||||
}
|
||||
|
||||
public final boolean isSplitCard() {
|
||||
return states.containsKey(CardStateName.LeftSplit);
|
||||
return getRules() != null && getRules().getSplitType() == CardSplitType.Split;
|
||||
}
|
||||
|
||||
public final boolean isBackSide() {
|
||||
return backside;
|
||||
}
|
||||
|
||||
public boolean isCloned() {
|
||||
return states.containsKey(CardStateName.Cloner);
|
||||
return !clonedStates.isEmpty();
|
||||
}
|
||||
|
||||
public static List<String> getStorableSVars() {
|
||||
@@ -2396,7 +2460,20 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
public final boolean isFaceDown() {
|
||||
return currentStateName == CardStateName.FaceDown;
|
||||
//return currentStateName == CardStateName.FaceDown;
|
||||
return facedown;
|
||||
}
|
||||
|
||||
public final void setFaceDown(boolean value) {
|
||||
facedown = value;
|
||||
}
|
||||
|
||||
public final boolean isFlipped() {
|
||||
return flipped;
|
||||
}
|
||||
|
||||
public final void setFlipped(boolean value) {
|
||||
flipped = value;
|
||||
}
|
||||
|
||||
public final void setCanCounter(final boolean b) {
|
||||
@@ -2900,11 +2977,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
public final void setColor(final String color) {
|
||||
currentState.setColor(color);
|
||||
currentState.getView().updateColors(this);
|
||||
}
|
||||
public final void setColor(final byte color) {
|
||||
currentState.setColor(color);
|
||||
currentState.getView().updateColors(this);
|
||||
}
|
||||
|
||||
public final ColorSet determineColor() {
|
||||
@@ -2977,6 +3052,51 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return getLatestPT().getRight();
|
||||
}
|
||||
|
||||
public final void addCloneState(CardCloneStates states, final long timestamp) {
|
||||
clonedStates.put(timestamp, states);
|
||||
updateCloneState(true);
|
||||
}
|
||||
|
||||
public final boolean removeCloneState(final long timestamp) {
|
||||
if (clonedStates.remove(timestamp) != null) {
|
||||
updateCloneState(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Card getCloner() {
|
||||
CardCloneStates clStates = getLastClonedState();
|
||||
if (clStates == null) {
|
||||
return null;
|
||||
}
|
||||
return clStates.getHost();
|
||||
}
|
||||
|
||||
public final void removeCloneStates() {
|
||||
clonedStates.clear();
|
||||
}
|
||||
|
||||
private final void updateCloneState(final boolean updateView) {
|
||||
if (isFaceDown()) {
|
||||
setState(CardStateName.FaceDown, updateView, true);
|
||||
} else if (isFlipped() && hasState(CardStateName.Flipped)) {
|
||||
setState(CardStateName.Flipped, updateView, true);
|
||||
} else if (backside && isDoubleFaced()) {
|
||||
setState(CardStateName.Transformed, updateView, true);
|
||||
} else if (backside && isMeldable()) {
|
||||
setState(CardStateName.Meld, updateView, true);
|
||||
} else {
|
||||
setState(CardStateName.Original, updateView, true);
|
||||
}
|
||||
}
|
||||
|
||||
private final CardCloneStates getLastClonedState() {
|
||||
if (clonedStates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return clonedStates.lastEntry().getValue();
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Get the latest set Power and Toughness of this Card.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package forge.game.card;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ForwardingMap;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class CardCloneStates extends ForwardingMap<CardStateName, CardState> {
|
||||
|
||||
private Map<CardStateName, CardState> dataMap = Maps.newEnumMap(CardStateName.class);
|
||||
|
||||
private Card origin = null;
|
||||
private SpellAbility sa = null;
|
||||
|
||||
public CardCloneStates(Card origin, SpellAbility sa) {
|
||||
super();
|
||||
this.origin = origin;
|
||||
this.sa = sa;
|
||||
}
|
||||
|
||||
public Card getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public SpellAbility getSource() {
|
||||
return sa;
|
||||
}
|
||||
|
||||
public Card getHost() {
|
||||
return sa.getHostCard();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<CardStateName, CardState> delegate() {
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
public CardState get(CardStateName key) {
|
||||
if (dataMap.containsKey(key)) {
|
||||
return super.get(key);
|
||||
}
|
||||
CardState original = super.get(CardStateName.Original);
|
||||
// need to copy it so the view has the right state name
|
||||
CardState result = new CardState(original.getCard(), key);
|
||||
result.copyFrom(original, false);
|
||||
dataMap.put(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,7 +18,10 @@
|
||||
package forge.game.card;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
import forge.card.*;
|
||||
import forge.card.mana.ManaCost;
|
||||
@@ -27,15 +30,19 @@ import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementHandler;
|
||||
import forge.game.spellability.*;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.WrappedAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -101,6 +108,7 @@ public class CardFactory {
|
||||
out.addImprintedCard(o);
|
||||
}
|
||||
out.setCommander(in.isCommander());
|
||||
//out.setFaceDown(in.isFaceDown());
|
||||
|
||||
return out;
|
||||
|
||||
@@ -687,5 +695,210 @@ public class CardFactory {
|
||||
return wrapperAbility;
|
||||
}
|
||||
|
||||
public static CardCloneStates getCloneStates(final Card in, final Card out, final SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Map<String,String> origSVars = host.getSVars();
|
||||
final List<String> types = Lists.newArrayList();
|
||||
final List<String> keywords = Lists.newArrayList();
|
||||
List<String> creatureTypes = null;
|
||||
final CardCloneStates result = new CardCloneStates(in, sa);
|
||||
|
||||
final String newName = sa.getParamOrDefault("NewName", null);
|
||||
String shortColors = "";
|
||||
|
||||
if (sa.hasParam("AddTypes")) {
|
||||
types.addAll(Arrays.asList(sa.getParam("AddTypes").split(",")));
|
||||
}
|
||||
|
||||
if (sa.hasParam("AddKeywords")) {
|
||||
keywords.addAll(Arrays.asList(sa.getParam("AddKeywords").split(" & ")));
|
||||
}
|
||||
|
||||
if (sa.hasParam("SetColor")) {
|
||||
shortColors = CardUtil.getShortColorsString(Arrays.asList(sa.getParam("SetColor").split(",")));
|
||||
}
|
||||
|
||||
if (sa.hasParam("SetCreatureTypes")) {
|
||||
creatureTypes = ImmutableList.copyOf(sa.getParam("SetCreatureTypes").split(" "));
|
||||
}
|
||||
|
||||
// TODO handle Volrath's Shapeshifter
|
||||
|
||||
if (in.isFaceDown()) {
|
||||
// if something is cloning a facedown card, it only clones the
|
||||
// facedown state into original
|
||||
final CardState ret = new CardState(out, CardStateName.Original);
|
||||
ret.copyFrom(in.getState(CardStateName.FaceDown), false);
|
||||
result.put(CardStateName.Original, ret);
|
||||
} else if (in.isFlipCard()) {
|
||||
// if something is cloning a flip card, copy both original and
|
||||
// flipped state
|
||||
final CardState ret1 = new CardState(out, CardStateName.Original);
|
||||
ret1.copyFrom(in.getState(CardStateName.Original), false);
|
||||
result.put(CardStateName.Original, ret1);
|
||||
|
||||
final CardState ret2 = new CardState(out, CardStateName.Flipped);
|
||||
ret2.copyFrom(in.getState(CardStateName.Flipped), false);
|
||||
result.put(CardStateName.Flipped, ret2);
|
||||
} else {
|
||||
// in all other cases just copy the current state to original
|
||||
final CardState ret = new CardState(out, CardStateName.Original);
|
||||
ret.copyFrom(in.getCurrentState(), false);
|
||||
result.put(CardStateName.Original, ret);
|
||||
}
|
||||
|
||||
// update all states, both for flip cards
|
||||
for (Map.Entry<CardStateName, CardState> e : result.entrySet()) {
|
||||
final CardState originalState = out.getState(e.getKey());
|
||||
final CardState state = e.getValue();
|
||||
// update the names for the states
|
||||
if (sa.hasParam("KeepName")) {
|
||||
state.setName(originalState.getName());
|
||||
} else if (newName != null) {
|
||||
state.setName(newName);
|
||||
}
|
||||
|
||||
state.setColor(shortColors);
|
||||
|
||||
if (sa.hasParam("NonLegendary")) {
|
||||
state.removeType(CardType.Supertype.Legendary);
|
||||
}
|
||||
|
||||
for (final String type : types) {
|
||||
state.addType(type);
|
||||
}
|
||||
|
||||
if (creatureTypes != null) {
|
||||
state.setCreatureTypes(creatureTypes);
|
||||
}
|
||||
|
||||
state.addIntrinsicKeywords(keywords);
|
||||
|
||||
if (sa.hasParam("SetPower")) {
|
||||
state.setBasePower(Integer.parseInt(sa.getParam("SetPower")));
|
||||
}
|
||||
if (sa.hasParam("SetToughness")) {
|
||||
state.setBaseToughness(Integer.parseInt(sa.getParam("SetToughness")));
|
||||
}
|
||||
|
||||
|
||||
// triggers to add to clone
|
||||
if (sa.hasParam("AddTriggers")) {
|
||||
for (final String s : Arrays.asList(sa.getParam("AddTriggers").split(","))) {
|
||||
if (origSVars.containsKey(s)) {
|
||||
final String actualTrigger = origSVars.get(s);
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, out, true);
|
||||
state.addTrigger(parsedTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SVars to add to clone
|
||||
if (sa.hasParam("AddSVars")) {
|
||||
for (final String s : Arrays.asList(sa.getParam("AddSVars").split(","))) {
|
||||
if (origSVars.containsKey(s)) {
|
||||
final String actualsVar = origSVars.get(s);
|
||||
state.setSVar(s, actualsVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (sa.hasParam("GainThisAbility")) {
|
||||
SpellAbility root = sa.getRootAbility();
|
||||
|
||||
if (root.isTrigger() && root.getTrigger() != null) {
|
||||
state.addTrigger(root.getTrigger().copy(out, false));
|
||||
} else if (root.isReplacementAbility()) {
|
||||
state.addReplacementEffect(root.getReplacementEffect().copy(out, false));
|
||||
} else {
|
||||
state.addSpellAbility(root.copy(out, false));
|
||||
}
|
||||
}
|
||||
|
||||
// Special Rules for Embalm and Eternalize
|
||||
if (sa.hasParam("Embalm") && out.isEmbalmed()) {
|
||||
state.addType("Zombie");
|
||||
state.setColor(MagicColor.WHITE);
|
||||
state.setManaCost(ManaCost.NO_COST);
|
||||
|
||||
String name = TextUtil.fastReplace(
|
||||
TextUtil.fastReplace(state.getName(), ",", ""),
|
||||
" ", "_").toLowerCase();
|
||||
state.setImageKey(ImageKeys.getTokenKey("embalm_" + name));
|
||||
}
|
||||
|
||||
if (sa.hasParam("Eternalize") && out.isEternalized()) {
|
||||
state.addType("Zombie");
|
||||
state.setColor(MagicColor.BLACK);
|
||||
state.setManaCost(ManaCost.NO_COST);
|
||||
state.setBasePower(4);
|
||||
state.setBaseToughness(4);
|
||||
|
||||
String name = TextUtil.fastReplace(
|
||||
TextUtil.fastReplace(state.getName(), ",", ""),
|
||||
" ", "_").toLowerCase();
|
||||
state.setImageKey(ImageKeys.getTokenKey("eternalize_" + name));
|
||||
}
|
||||
|
||||
// set the host card for copied replacement effects
|
||||
// needed for copied xPaid ETB effects (for the copy, xPaid = 0)
|
||||
for (final ReplacementEffect rep : state.getReplacementEffects()) {
|
||||
final SpellAbility newSa = rep.getOverridingAbility();
|
||||
if (newSa != null) {
|
||||
newSa.setOriginalHost(in);
|
||||
}
|
||||
}
|
||||
|
||||
// set the host card for copied spellabilities
|
||||
for (final SpellAbility newSa : state.getSpellAbilities()) {
|
||||
newSa.setOriginalHost(in);
|
||||
}
|
||||
|
||||
// remove some characteristic static abilties
|
||||
for (StaticAbility sta : state.getStaticAbilities()) {
|
||||
if (!sta.hasParam("CharacteristicDefining")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sa.hasParam("SetPower") || sa.hasParam("Eternalize")) {
|
||||
if (sta.hasParam("SetPower"))
|
||||
state.removeStaticAbility(sta);
|
||||
}
|
||||
if (sa.hasParam("SetToughness") || sa.hasParam("Eternalize")) {
|
||||
if (sta.hasParam("SetToughness"))
|
||||
state.removeStaticAbility(sta);
|
||||
}
|
||||
if (sa.hasParam("SetCreatureTypes")) {
|
||||
// currently only Changeling and similar should be affected by that
|
||||
// other cards using AddType$ ChosenType should not
|
||||
if (sta.hasParam("AddType") && "AllCreatureTypes".equals(sta.getParam("AddType"))) {
|
||||
state.removeStaticAbility(sta);
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("SetColor") || sa.hasParam("Embalm") || sa.hasParam("Eternalize")) {
|
||||
if (sta.hasParam("SetColor")) {
|
||||
state.removeStaticAbility(sta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove some keywords
|
||||
if (sa.hasParam("SetCreatureTypes")) {
|
||||
state.removeIntrinsicKeyword("Changeling");
|
||||
}
|
||||
if (sa.hasParam("SetColor") || sa.hasParam("Embalm") || sa.hasParam("Eternalize")) {
|
||||
state.removeIntrinsicKeyword("Devoid");
|
||||
}
|
||||
|
||||
for (SpellAbility ab : state.getSpellAbilities()) {
|
||||
ab.getRestrictions().resetTurnActivations();
|
||||
}
|
||||
}
|
||||
|
||||
// Dont copy the facedown state, make new one
|
||||
result.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(out));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // end class AbstractCardFactory
|
||||
|
||||
@@ -101,16 +101,18 @@ public class CardFactoryUtil {
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
Card c = hostCard.getGame().getAction().moveToPlay(hostCard, this);
|
||||
c.setPreFaceDownState(CardStateName.Original);
|
||||
hostCard.getGame().getAction().moveToPlay(hostCard, this);
|
||||
//c.setPreFaceDownState(CardStateName.Original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
CardStateName stateBackup = hostCard.getCurrentStateName();
|
||||
hostCard.setState(CardStateName.FaceDown, false);
|
||||
boolean face = hostCard.isFaceDown();
|
||||
hostCard.turnFaceDownNoUpdate();
|
||||
boolean success = super.canPlay();
|
||||
hostCard.setState(stateBackup, false);
|
||||
hostCard.setFaceDown(face);
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -229,10 +229,12 @@ public final class CardUtil {
|
||||
|
||||
newCopy.getCurrentState().copyFrom(in.getState(in.getCurrentStateName()), true);
|
||||
|
||||
/*
|
||||
if (in.isCloned()) {
|
||||
newCopy.addAlternateState(CardStateName.Cloner, false);
|
||||
newCopy.getState(CardStateName.Cloner).copyFrom(in.getState(CardStateName.Cloner), true);
|
||||
}
|
||||
//*/
|
||||
|
||||
newCopy.setType(new CardType(in.getType()));
|
||||
newCopy.setToken(in.isToken());
|
||||
@@ -327,7 +329,7 @@ public final class CardUtil {
|
||||
final CardType type = new CardType();
|
||||
type.add("Creature");
|
||||
|
||||
final CardState ret = new CardState(c.getView().createAlternateState(CardStateName.FaceDown), c);
|
||||
final CardState ret = new CardState(c, CardStateName.FaceDown);
|
||||
ret.setBasePower(2);
|
||||
ret.setBaseToughness(2);
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
public boolean isFaceDown() {
|
||||
return getCurrentState().getState() == CardStateName.FaceDown;
|
||||
return get(TrackableProperty.Facedown);// getCurrentState().getState() == CardStateName.FaceDown;
|
||||
}
|
||||
|
||||
public boolean isFlipCard() {
|
||||
@@ -121,16 +121,18 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
public boolean isFlipped() {
|
||||
return getCurrentState().getState() == CardStateName.Flipped;
|
||||
return get(TrackableProperty.Flipped); // getCurrentState().getState() == CardStateName.Flipped;
|
||||
}
|
||||
|
||||
public boolean isSplitCard() {
|
||||
return get(TrackableProperty.SplitCard);
|
||||
}
|
||||
|
||||
/*
|
||||
public boolean isTransformed() {
|
||||
return getCurrentState().getState() == CardStateName.Transformed;
|
||||
}
|
||||
//*/
|
||||
|
||||
public boolean isAttacking() {
|
||||
return get(TrackableProperty.Attacking);
|
||||
@@ -627,7 +629,9 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.SplitCard, isSplitCard);
|
||||
set(TrackableProperty.FlipCard, c.isFlipCard());
|
||||
|
||||
CardStateView cloner = CardView.getState(c, CardStateName.Cloner);
|
||||
final Card cloner = c.getCloner();
|
||||
|
||||
//CardStateView cloner = CardView.getState(c, CardStateName.Cloner);
|
||||
set(TrackableProperty.Cloner, cloner == null ? null : cloner.getName() + " (" + cloner.getId() + ")");
|
||||
|
||||
CardState currentState = c.getCurrentState();
|
||||
@@ -699,7 +703,7 @@ public class CardView extends GameEntityView {
|
||||
if (name.isEmpty()) {
|
||||
CardStateView alternate = getAlternateState();
|
||||
if (alternate != null) {
|
||||
if (this.getCurrentState().getState() == CardStateName.FaceDown) {
|
||||
if (isFaceDown()) {
|
||||
return "Face-down card (H" + getHiddenId() + ")";
|
||||
} else {
|
||||
return getAlternateState().getName() + " (" + getId() + ")";
|
||||
|
||||
@@ -42,7 +42,7 @@ public class CostAdjustment {
|
||||
boolean isStateChangeToFaceDown = false;
|
||||
if (sa.isSpell() && ((Spell) sa).isCastFaceDown()) {
|
||||
// Turn face down to apply cost modifiers correctly
|
||||
host.setState(CardStateName.FaceDown, false);
|
||||
host.turnFaceDownNoUpdate();
|
||||
isStateChangeToFaceDown = true;
|
||||
} // isSpell
|
||||
|
||||
@@ -83,6 +83,7 @@ public class CostAdjustment {
|
||||
// Reset card state (if changed)
|
||||
if (isStateChangeToFaceDown) {
|
||||
host.setState(CardStateName.Original, false);
|
||||
host.setFaceDown(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,16 @@ public enum TrackableProperty {
|
||||
Owner(TrackableTypes.PlayerViewType),
|
||||
Controller(TrackableTypes.PlayerViewType),
|
||||
Zone(TrackableTypes.EnumType(ZoneType.class)),
|
||||
|
||||
Flipped(TrackableTypes.BooleanType),
|
||||
Facedown(TrackableTypes.BooleanType),
|
||||
|
||||
//TODO?
|
||||
Cloner(TrackableTypes.StringType),
|
||||
Cloned(TrackableTypes.BooleanType),
|
||||
FlipCard(TrackableTypes.BooleanType),
|
||||
SplitCard(TrackableTypes.BooleanType),
|
||||
|
||||
Attacking(TrackableTypes.BooleanType),
|
||||
Blocking(TrackableTypes.BooleanType),
|
||||
PhasedOut(TrackableTypes.BooleanType),
|
||||
@@ -47,7 +54,7 @@ public enum TrackableProperty {
|
||||
EncodedCards(TrackableTypes.CardViewCollectionType),
|
||||
GainControlTargets(TrackableTypes.CardViewCollectionType),
|
||||
CloneOrigin(TrackableTypes.CardViewType),
|
||||
Cloner(TrackableTypes.StringType),
|
||||
|
||||
ImprintedCards(TrackableTypes.CardViewCollectionType),
|
||||
HauntedBy(TrackableTypes.CardViewCollectionType),
|
||||
Haunting(TrackableTypes.CardViewType),
|
||||
|
||||
@@ -34,7 +34,6 @@ import forge.card.CardDetailUtil;
|
||||
import forge.card.CardDetailUtil.DetailColors;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
@@ -244,7 +243,7 @@ public class CardDetailPanel extends SkinnedPanel {
|
||||
setInfoLabel.setBorder(BorderFactory.createLineBorder(foreColor));
|
||||
}
|
||||
|
||||
if (state.getState() == CardStateName.FaceDown) {
|
||||
if (card.isFaceDown()) {
|
||||
updateBorder(state, false); // TODO: HACK! A temporary measure until the morphs still leaking color can be fixed properly.
|
||||
} else {
|
||||
updateBorder(state, mayView);
|
||||
|
||||
@@ -38,7 +38,6 @@ import net.miginfocom.swing.MigLayout;
|
||||
import forge.CachedCardImage;
|
||||
import forge.card.CardDetailUtil;
|
||||
import forge.card.CardDetailUtil.DetailColors;
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.spellability.StackItemView;
|
||||
@@ -257,7 +256,7 @@ public class VStack implements IVDoc<CStack> {
|
||||
|
||||
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
||||
final CardStateView curState = item.getSourceCard().getCurrentState();
|
||||
final boolean isFaceDown = curState.getState() == CardStateName.FaceDown;
|
||||
final boolean isFaceDown = item.getSourceCard().isFaceDown();
|
||||
final DetailColors color = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(curState, true); // otherwise doesn't work correctly for face down Morphs
|
||||
setBackground(new Color(color.r, color.g, color.b));
|
||||
setForeground(FSkin.getHighContrastColor(getBackground()));
|
||||
|
||||
@@ -1696,4 +1696,99 @@ public class GameSimulatorTest extends SimulationTestCase {
|
||||
// One cards drawn
|
||||
assertTrue(simGame.getPlayers().get(0).getZone(ZoneType.Hand).size() == 1);
|
||||
}
|
||||
|
||||
|
||||
public void testCloneTransform() {
|
||||
Game game = initAndCreateGame();
|
||||
Player p = game.getPlayers().get(0);
|
||||
Player p2 = game.getPlayers().get(1);
|
||||
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
|
||||
|
||||
final String outLawName = "Kruin Outlaw";
|
||||
final String hillGiantName = "Elite Vanguard";
|
||||
final String terrorName = "Terror of Kruin Pass";
|
||||
|
||||
Card outlaw = addCard(outLawName, p2);
|
||||
Card giant = addCard(hillGiantName, p);
|
||||
|
||||
assertFalse(outlaw.isCloned());
|
||||
assertTrue(outlaw.isDoubleFaced());
|
||||
assertTrue(outlaw.hasState(CardStateName.Transformed));
|
||||
assertTrue(outlaw.canTransform());
|
||||
assertFalse(outlaw.isBackSide());
|
||||
|
||||
assertFalse(giant.isDoubleFaced());
|
||||
assertFalse(giant.canTransform());
|
||||
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Forest", p);
|
||||
addCard("Island", p);
|
||||
|
||||
Card cytoCard = addCardToZone("Cytoshape", p, ZoneType.Hand);
|
||||
SpellAbility cytoSA = cytoCard.getFirstSpellAbility();
|
||||
|
||||
Card moonmist = addCardToZone("Moonmist", p, ZoneType.Hand);
|
||||
SpellAbility moonmistSA = moonmist.getFirstSpellAbility();
|
||||
|
||||
cytoSA.getTargets().add(outlaw);
|
||||
|
||||
GameSimulator sim = createSimulator(game, p);
|
||||
int score = sim.simulateSpellAbility(cytoSA).value;
|
||||
|
||||
assertTrue(score > 0);
|
||||
|
||||
Game simGame = sim.getSimulatedGameState();
|
||||
|
||||
assertTrue(countCardsWithName(simGame, outLawName) == 0);
|
||||
assertTrue(countCardsWithName(simGame, hillGiantName) == 2);
|
||||
assertTrue(countCardsWithName(simGame, terrorName) == 0);
|
||||
|
||||
Card clonedOutLaw = (Card)sim.getGameCopier().find(outlaw);
|
||||
|
||||
assertTrue(clonedOutLaw.isCloned());
|
||||
assertTrue(clonedOutLaw.isDoubleFaced());
|
||||
assertFalse(clonedOutLaw.hasState(CardStateName.Transformed));
|
||||
assertTrue(clonedOutLaw.canTransform());
|
||||
assertFalse(clonedOutLaw.isBackSide());
|
||||
|
||||
assertTrue(clonedOutLaw.getName().equals(hillGiantName));
|
||||
|
||||
assertTrue(clonedOutLaw.isDoubleFaced());
|
||||
|
||||
score = sim.simulateSpellAbility(moonmistSA).value;
|
||||
assertTrue(score > 0);
|
||||
|
||||
simGame = sim.getSimulatedGameState();
|
||||
|
||||
assertTrue(countCardsWithName(simGame, outLawName) == 0);
|
||||
assertTrue(countCardsWithName(simGame, hillGiantName) == 2);
|
||||
assertTrue(countCardsWithName(simGame, terrorName) == 0);
|
||||
|
||||
Card transformOutLaw = (Card)sim.getGameCopier().find(outlaw);
|
||||
|
||||
assertTrue(transformOutLaw.isCloned());
|
||||
assertTrue(transformOutLaw.isDoubleFaced());
|
||||
assertFalse(transformOutLaw.hasState(CardStateName.Transformed));
|
||||
assertTrue(transformOutLaw.canTransform());
|
||||
assertTrue(transformOutLaw.isBackSide());
|
||||
|
||||
assertTrue(transformOutLaw.getName().equals(hillGiantName));
|
||||
|
||||
// need to clean up the clone state
|
||||
simGame.getPhaseHandler().devAdvanceToPhase(PhaseType.CLEANUP);
|
||||
|
||||
assertTrue(countCardsWithName(simGame, outLawName) == 0);
|
||||
assertTrue(countCardsWithName(simGame, hillGiantName) == 1);
|
||||
assertTrue(countCardsWithName(simGame, terrorName) == 1);
|
||||
|
||||
assertFalse(transformOutLaw.isCloned());
|
||||
assertTrue(transformOutLaw.isDoubleFaced());
|
||||
assertTrue(transformOutLaw.hasState(CardStateName.Transformed));
|
||||
assertTrue(transformOutLaw.canTransform());
|
||||
assertTrue(transformOutLaw.isBackSide());
|
||||
|
||||
assertTrue(transformOutLaw.getName().equals(terrorName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class CardImageRenderer {
|
||||
|
||||
//determine colors for borders
|
||||
final List<DetailColors> borderColors;
|
||||
final boolean isFaceDown = card.getCurrentState().getState() == CardStateName.FaceDown;
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
if (isFaceDown) {
|
||||
borderColors = ImmutableList.of(DetailColors.FACE_DOWN);
|
||||
}
|
||||
@@ -371,7 +371,7 @@ public class CardImageRenderer {
|
||||
|
||||
//determine colors for borders
|
||||
final List<DetailColors> borderColors;
|
||||
final boolean isFaceDown = card.getCurrentState().getState() == CardStateName.FaceDown;
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
if (isFaceDown) {
|
||||
borderColors = ImmutableList.of(DetailColors.FACE_DOWN);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class CardRenderer {
|
||||
return cardArt;
|
||||
}
|
||||
|
||||
public static FImageComplex getAftermathSecondCardArt(String imageKey) {
|
||||
public static FImageComplex getAftermathSecondCardArt(final String imageKey) {
|
||||
FImageComplex cardArt = cardArtCache.get("Aftermath_second_"+imageKey);
|
||||
if (cardArt == null) {
|
||||
Texture image = new CachedCardImage(imageKey) {
|
||||
@@ -439,7 +439,7 @@ public class CardRenderer {
|
||||
|
||||
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
||||
final CardStateView details = card.getCurrentState();
|
||||
final boolean isFaceDown = details.getState() == CardStateName.FaceDown;
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
final DetailColors borderColor = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(details, canShow); // canShow doesn't work here for face down Morphs
|
||||
Color color = FSkinColor.fromRGB(borderColor.r, borderColor.g, borderColor.b);
|
||||
color = FSkinColor.tintColor(Color.WHITE, color, CardRenderer.PT_BOX_TINT);
|
||||
|
||||
@@ -242,8 +242,7 @@ public class VStack extends FDropDown {
|
||||
}
|
||||
|
||||
// TODO: A hacky workaround is currently used to make the game not leak the color information for Morph cards.
|
||||
final CardStateView curState = card.getCurrentState();
|
||||
final boolean isFaceDown = curState.getState() == CardStateName.FaceDown;
|
||||
final boolean isFaceDown = card.isFaceDown();
|
||||
final DetailColors color = isFaceDown ? CardDetailUtil.DetailColors.FACE_DOWN : CardDetailUtil.getBorderColor(card.getCurrentState(), true); // otherwise doesn't work correctly for face down Morphs
|
||||
backColor = FSkinColor.fromRGB(color.r, color.g, color.b);
|
||||
foreColor = FSkinColor.getHighContrastColor(backColor);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
Name:Cytoshape
|
||||
ManaCost:1 G U
|
||||
Types:Instant
|
||||
A:SP$ ChooseCard | Cost$ 1 G U | Defined$ You | Amount$ 1 | Choices$ Creature.nonLegendary | Mandatory$ True | SubAbility$ Pump4Tgt | RememberChosen$ True | AILogic$ Clone | SpellDescription$ Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn.
|
||||
SVar:Pump4Tgt:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Choose target creature | AILogic$ Pump | SubAbility$ ShapeTgt | StackDescription$ None
|
||||
SVar:ShapeTgt:DB$ Clone | Defined$ Remembered | CloneTarget$ ParentTarget | Duration$ UntilEndOfTurn
|
||||
A:SP$ Clone | Cost$ 1 G U | Choices$ Creature.nonLegendary | ChoiceTitle$ Choose a nonlegendary creature to copy | ValidTgts$ Creature | TgtPrompt$ Choose target creature to become a copy | Duration$ UntilEndOfTurn | StackDescription$ SpellDescription | SpellDescription$ Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn.
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/cytoshape.jpg
|
||||
Oracle:Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn.
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.google.common.collect.Sets;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
@@ -178,8 +177,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
|
||||
switch (altState.getState()) {
|
||||
case Original:
|
||||
final CardStateView currentState = cv.getCurrentState();
|
||||
if (currentState.getState() == CardStateName.FaceDown) {
|
||||
if (cv.isFaceDown()) {
|
||||
return getCurrentPlayer() == null || cv.canFaceDownBeShownToAny(getLocalPlayers());
|
||||
}
|
||||
return true; //original can always be shown if not a face down that can't be shown
|
||||
|
||||
Reference in New Issue
Block a user