mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Card: changed some logic with facedown and copy states
This commit is contained in:
@@ -15,6 +15,7 @@ import com.google.common.collect.Maps;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.CardType;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
@@ -27,6 +28,7 @@ import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardZoneTable;
|
||||
@@ -651,6 +653,12 @@ 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")) {
|
||||
gameCard.turnFaceDown(true);
|
||||
setFaceDownState(gameCard, sa);
|
||||
}
|
||||
|
||||
movedCard = game.getAction().moveTo(gameCard.getController().getZone(destination), gameCard, cause, moveParams);
|
||||
if (sa.hasParam("Unearth")) {
|
||||
movedCard.setUnearthed(true);
|
||||
@@ -662,9 +670,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("LeaveBattlefield")) {
|
||||
addLeaveBattlefieldReplacement(movedCard, sa, sa.getParam("LeaveBattlefield"));
|
||||
}
|
||||
if (sa.hasParam("FaceDown")) {
|
||||
movedCard.turnFaceDown(true);
|
||||
}
|
||||
if (addToCombat(movedCard, movedCard.getController(), sa, "Attacking", "Blocking")) {
|
||||
combatChanged = true;
|
||||
}
|
||||
@@ -1295,34 +1300,7 @@ 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")) {
|
||||
c.turnFaceDown(true);
|
||||
|
||||
// set New Pt doesn't work because this values need to be copyable for clone effects
|
||||
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")) {
|
||||
c.addType(Arrays.asList(sa.getParam("FaceDownAddType").split(" & ")));
|
||||
}
|
||||
|
||||
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|
||||
|| sa.hasParam("FaceDownAddType")) {
|
||||
final GameCommand unanimate = new GameCommand() {
|
||||
private static final long serialVersionUID = 8853789549297846163L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
c.clearStates(CardStateName.FaceDown, true);
|
||||
}
|
||||
};
|
||||
|
||||
c.addFaceupCommand(unanimate);
|
||||
}
|
||||
setFaceDownState(c, sa);
|
||||
}
|
||||
movedCard = game.getAction().moveToPlay(c, c.getController(), cause, moveParams);
|
||||
if (sa.hasParam("Tapped")) {
|
||||
@@ -1460,6 +1438,39 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
&& sa.getParam("WithTotalCMC") == null;
|
||||
}
|
||||
|
||||
private static void setFaceDownState(Card c, SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
CardState faceDown = c.getFaceDownState();
|
||||
|
||||
// set New Pt doesn't work because this values need to be copyable for clone effects
|
||||
if (sa.hasParam("FaceDownPower")) {
|
||||
faceDown.setBasePower(AbilityUtils.calculateAmount(
|
||||
source, sa.getParam("FaceDownPower"), sa));
|
||||
}
|
||||
if (sa.hasParam("FaceDownToughness")) {
|
||||
faceDown.setBaseToughness(AbilityUtils.calculateAmount(
|
||||
source, sa.getParam("FaceDownToughness"), sa));
|
||||
}
|
||||
|
||||
if (sa.hasParam("FaceDownSetType")) {
|
||||
faceDown.setType(new CardType(Arrays.asList(sa.getParam("FaceDownSetType").split(" & ")), false));
|
||||
}
|
||||
|
||||
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|
||||
|| sa.hasParam("FaceDownSetType")) {
|
||||
final GameCommand unanimate = new GameCommand() {
|
||||
private static final long serialVersionUID = 8853789549297846163L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
c.clearStates(CardStateName.FaceDown, true);
|
||||
}
|
||||
};
|
||||
|
||||
c.addFaceupCommand(unanimate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* removeFromStack.
|
||||
|
||||
@@ -5,13 +5,11 @@ import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCloneStates;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardFactory;
|
||||
@@ -54,6 +52,8 @@ public class MutateEffect extends SpellAbilityEffect {
|
||||
host.setController(p, 0);
|
||||
}
|
||||
|
||||
final boolean wasFaceDown = target.isFaceDown();
|
||||
|
||||
host.setMergedToCard(target);
|
||||
// If first time mutate, add target first.
|
||||
if (!target.hasMergedCard()) {
|
||||
@@ -72,10 +72,15 @@ public class MutateEffect extends SpellAbilityEffect {
|
||||
// Now add all abilities from bottom cards
|
||||
final Long ts = game.getNextTimestamp();
|
||||
target.setMutatedTimestamp(ts);
|
||||
if (topCard.getCurrentStateName() != CardStateName.FaceDown) {
|
||||
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(target, sa);
|
||||
target.addCloneState(mutatedStates, ts);
|
||||
|
||||
target.addCloneState(CardFactory.getMutatedCloneStates(target, sa), ts);
|
||||
|
||||
// currently used by Tezzeret, Cruel Machinist and Yedora, Grave Gardener
|
||||
// when mutating onto the FaceDown, their effect should end, then 721.2e would stop trigger
|
||||
if (wasFaceDown && !target.isFaceDown()) {
|
||||
target.runFaceupCommands();
|
||||
}
|
||||
|
||||
// Re-register triggers for target card
|
||||
game.getTriggerHandler().clearActiveTriggers(target, null);
|
||||
game.getTriggerHandler().registerActiveTrigger(target, false);
|
||||
|
||||
@@ -49,7 +49,6 @@ import com.google.common.collect.Table;
|
||||
import com.google.common.collect.TreeBasedTable;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.ImageKeys;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardChangedType;
|
||||
import forge.card.CardDb.SetPreference;
|
||||
@@ -433,6 +432,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return getState(state, false);
|
||||
}
|
||||
public CardState getState(final CardStateName state, boolean skipTextChange) {
|
||||
if (state == CardStateName.FaceDown) {
|
||||
return getFaceDownState();
|
||||
}
|
||||
if (!skipTextChange) {
|
||||
CardCloneStates txtStates = getLastTextChangeState();
|
||||
if (txtStates != null) {
|
||||
@@ -460,12 +462,19 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
public CardState getOriginalState(final CardStateName state) {
|
||||
if (!states.containsKey(state) && state == CardStateName.FaceDown) {
|
||||
states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this));
|
||||
if (state == CardStateName.FaceDown) {
|
||||
return getFaceDownState();
|
||||
}
|
||||
return states.get(state);
|
||||
}
|
||||
|
||||
public CardState getFaceDownState() {
|
||||
if (!states.containsKey(CardStateName.FaceDown)) {
|
||||
states.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(this));
|
||||
}
|
||||
return states.get(CardStateName.FaceDown);
|
||||
}
|
||||
|
||||
public void setOriginalStateAsFaceDown() {
|
||||
// For Ertai's Meddling a morph spell
|
||||
currentState = CardUtil.getFaceDownCharacteristic(this, CardStateName.Original);
|
||||
@@ -476,26 +485,25 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return setState(state, updateView, false);
|
||||
}
|
||||
public boolean setState(final CardStateName state, boolean updateView, boolean forceUpdate) {
|
||||
CardCloneStates textChangeStates = getLastTextChangeState();
|
||||
// faceDown has higher priority over clone states
|
||||
// while text change states doesn't apply while the card is faceDown
|
||||
if (state != CardStateName.FaceDown) {
|
||||
CardCloneStates textChangeStates = getLastTextChangeState();
|
||||
|
||||
if (textChangeStates != null) {
|
||||
if (!textChangeStates.containsKey(state)) {
|
||||
throw new RuntimeException(getName() + " tried to switch to non-existant text change state \"" + state + "\"!");
|
||||
//return false; // Nonexistant state.
|
||||
}
|
||||
} else {
|
||||
CardCloneStates cloneStates = getLastClonedState();
|
||||
if (cloneStates != null) {
|
||||
if (!cloneStates.containsKey(state)) {
|
||||
throw new RuntimeException(getName() + " tried to switch to non-existant cloned state \"" + state + "\"!");
|
||||
if (textChangeStates != null) {
|
||||
if (!textChangeStates.containsKey(state)) {
|
||||
throw new RuntimeException(getName() + " tried to switch to non-existant text change state \"" + state + "\"!");
|
||||
//return false; // Nonexistant state.
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
CardCloneStates cloneStates = getLastClonedState();
|
||||
if (cloneStates != null) {
|
||||
if (!cloneStates.containsKey(state)) {
|
||||
throw new RuntimeException(getName() + " tried to switch to non-existant cloned state \"" + state + "\"!");
|
||||
//return false; // Nonexistant state.
|
||||
}
|
||||
} else {
|
||||
if (!states.containsKey(state)) {
|
||||
System.out.println(getName() + " tried to switch to non-existant state \"" + state + "\"!");
|
||||
return false; // Nonexistant state.
|
||||
}
|
||||
@@ -554,20 +562,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
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));
|
||||
states.put(to, tmp);
|
||||
if (currentStateName == from) {
|
||||
setState(to, false);
|
||||
}
|
||||
if (updateView) {
|
||||
view.updateState(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final void addAlternateState(final CardStateName state, final boolean updateView) {
|
||||
states.put(state, new CardState(this, state));
|
||||
if (updateView) {
|
||||
@@ -619,9 +613,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
}
|
||||
CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this);
|
||||
boolean retResult = false;
|
||||
for (final Card c : cards) {
|
||||
@@ -633,7 +624,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
boolean result = c.changeToState(c.backside ? CardStateName.Transformed : CardStateName.Original);
|
||||
retResult = retResult || result;
|
||||
}
|
||||
if (hasMergedCard()) {
|
||||
if (retResult && hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
rebuildMutatedStates(cause);
|
||||
}
|
||||
|
||||
@@ -654,9 +646,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
}
|
||||
CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this);
|
||||
boolean retResult = false;
|
||||
for (final Card c : cards) {
|
||||
@@ -668,6 +657,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
retResult = retResult || result;
|
||||
}
|
||||
if (retResult && hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
rebuildMutatedStates(cause);
|
||||
game.getTriggerHandler().clearActiveTriggers(this, null);
|
||||
game.getTriggerHandler().registerActiveTrigger(this, false);
|
||||
@@ -716,9 +706,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
public boolean turnFaceDown(boolean override) {
|
||||
if (hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
}
|
||||
CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this);
|
||||
boolean retResult = false;
|
||||
for (final Card c : cards) {
|
||||
@@ -730,7 +717,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasMergedCard()) {
|
||||
if (retResult && hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
rebuildMutatedStates(null);
|
||||
game.getTriggerHandler().clearActiveTriggers(this, null);
|
||||
game.getTriggerHandler().registerActiveTrigger(this, false);
|
||||
@@ -756,9 +744,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
}
|
||||
CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this);
|
||||
boolean retResult = false;
|
||||
for (final Card c : cards) {
|
||||
@@ -778,7 +763,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
retResult = retResult || result;
|
||||
}
|
||||
if (hasMergedCard()) {
|
||||
if (retResult && hasMergedCard()) {
|
||||
removeMutatedStates();
|
||||
rebuildMutatedStates(cause);
|
||||
game.getTriggerHandler().clearActiveTriggers(this, null);
|
||||
game.getTriggerHandler().registerActiveTrigger(this, false);
|
||||
@@ -1170,7 +1156,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
public final void rebuildMutatedStates(final CardTraitBase sa) {
|
||||
if (getCurrentStateName() != CardStateName.FaceDown) {
|
||||
if (!isFaceDown()) {
|
||||
final CardCloneStates mutatedStates = CardFactory.getMutatedCloneStates(this, sa);
|
||||
addCloneState(mutatedStates, getMutatedTimestamp());
|
||||
}
|
||||
@@ -5596,10 +5582,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
public final void setManifested(final boolean manifested) {
|
||||
this.manifested = manifested;
|
||||
final String image = manifested ? ImageKeys.MANIFEST_IMAGE : ImageKeys.MORPH_IMAGE;
|
||||
// Note: This should only be called after state has been set to CardStateName.FaceDown,
|
||||
// so the below call should be valid since the state should have been created already.
|
||||
getState(CardStateName.FaceDown).setImageKey(ImageKeys.getTokenKey(image));
|
||||
}
|
||||
|
||||
public final boolean isForetold() {
|
||||
@@ -5616,7 +5598,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
public final void setForetold(final boolean foretold) {
|
||||
this.foretold = foretold;
|
||||
getState(CardStateName.FaceDown).setImageKey(ImageKeys.getTokenKey(ImageKeys.FORETELL_IMAGE));
|
||||
}
|
||||
|
||||
public boolean isForetoldByEffect() {
|
||||
|
||||
@@ -623,7 +623,7 @@ public class CardFactory {
|
||||
// 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, true), false);
|
||||
ret.copyFrom(in.getFaceDownState(), false);
|
||||
result.put(CardStateName.Original, ret);
|
||||
} else if (in.isFlipCard()) {
|
||||
// if something is cloning a flip card, copy both original and
|
||||
@@ -828,9 +828,6 @@ public class CardFactory {
|
||||
state.removeIntrinsicKeyword("Devoid");
|
||||
}
|
||||
}
|
||||
|
||||
// Dont copy the facedown state, make new one
|
||||
result.put(CardStateName.FaceDown, CardUtil.getFaceDownCharacteristic(out));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -380,10 +380,7 @@ public final class CardUtil {
|
||||
|
||||
//show hidden if exiled facedown
|
||||
if (state == CardStateName.FaceDown) {
|
||||
if (c.isInZone(ZoneType.Exile))
|
||||
ret.setImageKey(ImageKeys.getTokenKey(c.isForetold() ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD));
|
||||
else
|
||||
ret.setImageKey(ImageKeys.getTokenKey(c.isManifested() ? ImageKeys.MANIFEST_IMAGE : ImageKeys.MORPH_IMAGE));
|
||||
ret.setImageKey(ImageKeys.getTokenKey(ImageKeys.HIDDEN_CARD));
|
||||
} else {
|
||||
ret.setImageKey(c.getImageKey());
|
||||
}
|
||||
|
||||
@@ -142,6 +142,10 @@ public class CardView extends GameEntityView {
|
||||
return get(TrackableProperty.Foretold);
|
||||
}
|
||||
|
||||
public boolean isManifested() {
|
||||
return get(TrackableProperty.Manifested);
|
||||
}
|
||||
|
||||
public boolean isFlipCard() {
|
||||
return get(TrackableProperty.FlipCard);
|
||||
}
|
||||
@@ -792,6 +796,7 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.FlipCard, c.isFlipCard());
|
||||
set(TrackableProperty.Facedown, c.isFaceDown());
|
||||
set(TrackableProperty.Foretold, c.isForetold());
|
||||
set(TrackableProperty.Manifested, c.isManifested());
|
||||
set(TrackableProperty.Adventure, c.isAdventureCard());
|
||||
set(TrackableProperty.DoubleFaced, c.isDoubleFaced());
|
||||
set(TrackableProperty.Modal, c.isModal());
|
||||
@@ -1059,6 +1064,12 @@ public class CardView extends GameEntityView {
|
||||
return getImageKey(null);
|
||||
}
|
||||
public String getImageKey(Iterable<PlayerView> viewers) {
|
||||
if (getState() == CardStateName.FaceDown) {
|
||||
if (getCard().getZone() == ZoneType.Exile) {
|
||||
return ImageKeys.getTokenKey(getCard().isForeTold() ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD);
|
||||
}
|
||||
return ImageKeys.getTokenKey(getCard().isManifested() ? ImageKeys.MANIFEST_IMAGE : ImageKeys.MORPH_IMAGE);
|
||||
}
|
||||
if (canBeShownToAny(viewers)) {
|
||||
return get(TrackableProperty.ImageKey);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public enum TrackableProperty {
|
||||
Flipped(TrackableTypes.BooleanType),
|
||||
Facedown(TrackableTypes.BooleanType),
|
||||
Foretold(TrackableTypes.BooleanType),
|
||||
Manifested(TrackableTypes.BooleanType),
|
||||
Modal(TrackableTypes.BooleanType),
|
||||
Adventure(TrackableTypes.BooleanType),
|
||||
DoubleFaced(TrackableTypes.BooleanType),
|
||||
|
||||
Reference in New Issue
Block a user