mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 01:38:13 +00:00
Merge pull request #2449 from Card-Forge/flashbackCastKeyword
Flashback: use castKeyword
This commit is contained in:
@@ -265,31 +265,21 @@ public class GameAction {
|
||||
copied.setTimestamp(c.getTimestamp());
|
||||
|
||||
if (zoneTo.is(ZoneType.Stack)) {
|
||||
// when moving to stack, copy changed card information
|
||||
copied.setChangedCardColors(c.getChangedCardColorsTable());
|
||||
copied.setChangedCardColorsCharacterDefining(c.getChangedCardColorsCharacterDefiningTable());
|
||||
copied.setChangedCardKeywords(c.getChangedCardKeywords());
|
||||
copied.setChangedCardTypes(c.getChangedCardTypesTable());
|
||||
copied.setChangedCardTypesCharacterDefining(c.getChangedCardTypesCharacterDefiningTable());
|
||||
copied.setChangedCardNames(c.getChangedCardNames());
|
||||
copied.setChangedCardTraits(c.getChangedCardTraits());
|
||||
copied.setDrawnThisTurn(c.getDrawnThisTurn());
|
||||
|
||||
copied.copyChangedTextFrom(c);
|
||||
|
||||
// clean up changes that come from its own static abilities
|
||||
copied.cleanupCopiedChangesFrom(c);
|
||||
// try not to copy changed stats when moving to stack
|
||||
|
||||
// copy exiled properties when adding to stack
|
||||
// will be cleanup later in MagicStack
|
||||
copied.setExiledWith(c.getExiledWith());
|
||||
copied.setExiledBy(c.getExiledBy());
|
||||
copied.setDrawnThisTurn(c.getDrawnThisTurn());
|
||||
|
||||
// copy bestow timestamp
|
||||
copied.setBestowTimestamp(c.getBestowTimestamp());
|
||||
|
||||
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard())) {
|
||||
copied.setCastSA(cause);
|
||||
copied.setSplitStateToPlayAbility(cause);
|
||||
|
||||
// CR 112.2 A spell’s controller is, by default, the player who put it on the stack.
|
||||
copied.setController(cause.getActivatingPlayer(), 0);
|
||||
KeywordInterface kw = cause.getKeyword();
|
||||
if (kw != null) {
|
||||
copied.addKeywordForStaticAbility(kw);
|
||||
@@ -619,10 +609,12 @@ public class GameAction {
|
||||
checkStaticAbilities();
|
||||
|
||||
// 400.7g try adding keyword back into card if it doesn't already have it
|
||||
if (zoneTo.is(ZoneType.Stack) && cause != null && cause.isSpell() && c.equals(cause.getHostCard())) {
|
||||
if (zoneTo.is(ZoneType.Stack) && cause != null && cause.isSpell() && !cause.isIntrinsic() && c.equals(cause.getHostCard())) {
|
||||
if (cause.getKeyword() != null) {
|
||||
if (!copied.getKeywords().contains(cause.getKeyword())) {
|
||||
copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getTimestamp(), 0, false);
|
||||
copied.addChangedCardKeywordsInternal(ImmutableList.of(cause.getKeyword()), null, false, game.getNextTimestamp(), 0, false);
|
||||
// update Keyword Cache
|
||||
copied.updateKeywords();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -874,17 +866,7 @@ public class GameAction {
|
||||
return moveToStack(c, cause, params);
|
||||
}
|
||||
public final Card moveToStack(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||
Card result = moveTo(game.getStackZone(), c, cause, params);
|
||||
if (cause != null && cause.isSpell() && result.equals(cause.getHostCard())) {
|
||||
result.setSplitStateToPlayAbility(cause);
|
||||
|
||||
// CR 112.2 A spell’s controller is, by default, the player who put it on the stack.
|
||||
result.setController(cause.getActivatingPlayer(), 0);
|
||||
// for triggers like from Wild-Magic Sorcerer
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
game.getTriggerHandler().resetActiveTriggers();
|
||||
}
|
||||
return result;
|
||||
return moveTo(game.getStackZone(), c, cause, params);
|
||||
}
|
||||
|
||||
public final Card moveToGraveyard(final Card c, SpellAbility cause) {
|
||||
|
||||
@@ -224,6 +224,7 @@ public final class GameActionUtil {
|
||||
|
||||
newSA.setAlternativeCost(AlternativeCost.Escape);
|
||||
newSA.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
newSA.setIntrinsic(inst.isIntrinsic());
|
||||
|
||||
alternatives.add(newSA);
|
||||
} else if (keyword.startsWith("Flashback")) {
|
||||
@@ -255,6 +256,7 @@ public final class GameActionUtil {
|
||||
flashback.setAlternativeCost(AlternativeCost.Flashback);
|
||||
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
||||
flashback.setKeyword(inst);
|
||||
flashback.setIntrinsic(inst.isIntrinsic());
|
||||
alternatives.add(flashback);
|
||||
} else if (keyword.startsWith("Foretell")) {
|
||||
// Foretell cast only from Exile
|
||||
|
||||
@@ -20,6 +20,7 @@ package forge.game.card;
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.*;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.StaticData;
|
||||
import forge.card.*;
|
||||
@@ -4587,6 +4588,17 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
|
||||
public Table<Long, String, KeywordInterface> getStoredKeywords() {
|
||||
return storedKeywords;
|
||||
}
|
||||
|
||||
public void setStoredKeywords(Table<Long, String, KeywordInterface> table, boolean lki) {
|
||||
storedKeywords.clear();
|
||||
for (Table.Cell<Long, String, KeywordInterface> c : table.cellSet()) {
|
||||
storedKeywords.put(c.getRowKey(), c.getColumnKey(), c.getValue().copy(this, lki));
|
||||
}
|
||||
}
|
||||
|
||||
public final void addChangedCardKeywordsByText(final List<KeywordInterface> keywords, final long timestamp, final long staticId, final boolean updateView) {
|
||||
// keywords should already created for Card, so no addKeywordsToCard
|
||||
// this one is done for Volrath's Shapeshifter which replaces all the card text
|
||||
|
||||
@@ -2285,7 +2285,7 @@ public class CardFactoryUtil {
|
||||
} else if (keyword.startsWith("Flashback")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile ");
|
||||
sb.append("| ValidStackSa$ Spell.Flashback | Description$ Flashback");
|
||||
sb.append("| ValidStackSa$ Spell.Flashback+castKeyword | Description$ Flashback");
|
||||
|
||||
if (keyword.contains(":")) { // K:Flashback:Cost:ExtraParams:ExtraDescription
|
||||
final String[] k = keyword.split(":");
|
||||
|
||||
@@ -293,6 +293,8 @@ public final class CardUtil {
|
||||
newCopy.setChangedCardNames(in.getChangedCardNames());
|
||||
newCopy.setChangedCardTraits(in.getChangedCardTraits());
|
||||
|
||||
newCopy.setStoredKeywords(in.getStoredKeywords(), true);
|
||||
|
||||
newCopy.copyChangedTextFrom(in);
|
||||
|
||||
newCopy.setTimestamp(in.getTimestamp());
|
||||
|
||||
@@ -20,6 +20,9 @@ import io.sentry.Breadcrumb;
|
||||
import io.sentry.Sentry;
|
||||
|
||||
public abstract class KeywordInstance<T extends KeywordInstance<?>> implements KeywordInterface {
|
||||
private Card hostCard = null;
|
||||
private boolean intrinsic = false;
|
||||
|
||||
private Keyword keyword;
|
||||
private String original;
|
||||
private long staticId = 0;
|
||||
@@ -87,6 +90,8 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
|
||||
* @see forge.game.keyword.KeywordInterface#createTraits(forge.game.card.Card, boolean, boolean)
|
||||
*/
|
||||
public final void createTraits(final Card host, final boolean intrinsic, final boolean clear) {
|
||||
this.hostCard = host;
|
||||
this.intrinsic = intrinsic;
|
||||
if (clear) {
|
||||
triggers.clear();
|
||||
replacements.clear();
|
||||
@@ -249,7 +254,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
|
||||
public KeywordInterface copy(final Card host, final boolean lki) {
|
||||
try {
|
||||
KeywordInstance<?> result = (KeywordInstance<?>) super.clone();
|
||||
|
||||
result.hostCard = host;
|
||||
result.abilities = Lists.newArrayList();
|
||||
for (SpellAbility sa : this.abilities) {
|
||||
SpellAbility copy = sa.copy(host, lki);
|
||||
@@ -300,11 +305,17 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
|
||||
return !list.isEmpty() && keyword.isMultipleRedundant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card getHostCard() {
|
||||
return hostCard;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.keyword.KeywordInterface#setHostCard(forge.game.card.Card)
|
||||
*/
|
||||
@Override
|
||||
public void setHostCard(Card host) {
|
||||
this.hostCard = host;
|
||||
for (SpellAbility sa : this.abilities) {
|
||||
sa.setHostCard(host);
|
||||
}
|
||||
@@ -322,8 +333,14 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntrinsic() {
|
||||
return intrinsic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntrinsic(final boolean value) {
|
||||
this.intrinsic = value;
|
||||
for (SpellAbility sa : this.abilities) {
|
||||
sa.setIntrinsic(value);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ import forge.game.trigger.Trigger;
|
||||
|
||||
public interface KeywordInterface extends Cloneable {
|
||||
|
||||
Card getHostCard();
|
||||
void setHostCard(final Card host);
|
||||
boolean isIntrinsic();
|
||||
void setIntrinsic(final boolean value);
|
||||
|
||||
String getOriginal();
|
||||
|
||||
Keyword getKeyword();
|
||||
@@ -34,8 +39,6 @@ public interface KeywordInterface extends Cloneable {
|
||||
void addSpellAbility(final SpellAbility s);
|
||||
void addStaticAbility(final StaticAbility st);
|
||||
|
||||
void setHostCard(final Card host);
|
||||
void setIntrinsic(final boolean value);
|
||||
|
||||
/**
|
||||
* @return the triggers
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
@@ -48,10 +47,7 @@ import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.card.CardTraitChanges;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.keyword.KeywordsChange;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
@@ -110,53 +106,6 @@ public class ReplacementHandler {
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(affectedLKI), preList);
|
||||
checkAgain = true;
|
||||
|
||||
// need to check if Intrinsic has run
|
||||
for (ReplacementEffect re : affectedLKI.getReplacementEffects()) {
|
||||
if (re.isIntrinsic() && this.hasRun.contains(re)) {
|
||||
re.setHasRun(true);
|
||||
}
|
||||
}
|
||||
|
||||
// need to check non Intrinsic
|
||||
for (Table.Cell<Long, Long, CardTraitChanges> e : affectedLKI.getChangedCardTraits().cellSet()) {
|
||||
boolean hasRunRE = false;
|
||||
String skey = String.valueOf(e.getRowKey()) + ":" + String.valueOf(e.getColumnKey());
|
||||
|
||||
for (ReplacementEffect re : this.hasRun) {
|
||||
if (!re.isIntrinsic() && skey.equals(re.getSVar("_ReplacedTimestamp"))) {
|
||||
hasRunRE = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (ReplacementEffect re : e.getValue().getReplacements()) {
|
||||
re.setSVar("_ReplacedTimestamp", skey);
|
||||
if (hasRunRE) {
|
||||
re.setHasRun(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Table.Cell<Long, Long, KeywordsChange> e : affectedLKI.getChangedCardKeywords().cellSet()) {
|
||||
boolean hasRunRE = false;
|
||||
String skey = String.valueOf(e.getRowKey()) + ":" + String.valueOf(e.getColumnKey());
|
||||
|
||||
for (ReplacementEffect re : this.hasRun) {
|
||||
if (!re.isIntrinsic() && skey.equals(re.getSVar("_ReplacedTimestamp"))) {
|
||||
hasRunRE = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (KeywordInterface k : e.getValue().getKeywords()) {
|
||||
for (ReplacementEffect re : k.getReplacements()) {
|
||||
re.setSVar("_ReplacedTimestamp", skey);
|
||||
if (hasRunRE) {
|
||||
re.setHasRun(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runParams.put(AbilityKey.Affected, affectedLKI);
|
||||
}
|
||||
|
||||
@@ -222,6 +171,8 @@ public class ReplacementHandler {
|
||||
for (final ReplacementEffect re : affectedLKI.getReplacementEffects()) {
|
||||
re.setHostCard(affectedCard);
|
||||
}
|
||||
// need to copy stored keywords from lki into real object to prevent the replacement effect from making new ones
|
||||
affectedCard.setStoredKeywords(affectedLKI.getStoredKeywords(), true);
|
||||
runParams.put(AbilityKey.Affected, affectedCard);
|
||||
runParams.put(AbilityKey.NewCard, CardUtil.getLKICopy(affectedLKI));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user