mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-11 16:26:22 +00:00
Add TextBoxExchangeEffect Ability and 'Deadpool, Trading Card' (#7637)
* capure Textboxes to avoid LKI copy * Fix copying keyworded traits twice * Support keepTextChanges across all traits Co-authored-by: kvn <kevni@secure.mailbox.org> Co-authored-by: tool4EvEr <tool4EvEr@>
This commit is contained in:
@@ -92,6 +92,7 @@ public enum ApiType {
|
||||
ExchangeControlVariant (ControlExchangeVariantEffect.class),
|
||||
ExchangePower (PowerExchangeEffect.class),
|
||||
ExchangeZone (ZoneExchangeEffect.class),
|
||||
ExchangeTextBox (TextBoxExchangeEffect.class),
|
||||
Explore (ExploreEffect.class),
|
||||
Fight (FightEffect.class),
|
||||
FlipACoin (FlipCoinEffect.class),
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardState;
|
||||
import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exchanges text boxes between two creatures.
|
||||
*/
|
||||
public class TextBoxExchangeEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
protected String getStackDescription(final SpellAbility sa) {
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
Card c1;
|
||||
Card c2;
|
||||
if (tgtCards.size() == 1) {
|
||||
c1 = sa.getHostCard();
|
||||
c2 = tgtCards.get(0);
|
||||
} else {
|
||||
c1 = tgtCards.get(0);
|
||||
c2 = tgtCards.get(1);
|
||||
}
|
||||
return c1 + " exchanges text box with " + c2 + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(final SpellAbility sa) {
|
||||
final List<Card> tgtCards = getTargetCards(sa);
|
||||
if (tgtCards.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Card c1 = tgtCards.get(0);
|
||||
final Card c2 = tgtCards.get(1);
|
||||
|
||||
// snapshot the original text boxes before modifying
|
||||
final TextBoxData data1 = captureTextBoxData(c1);
|
||||
final TextBoxData data2 = captureTextBoxData(c2);
|
||||
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final long ts = game.getNextTimestamp();
|
||||
|
||||
swapTextBox(c1, data2, ts);
|
||||
swapTextBox(c2, data1, ts);
|
||||
|
||||
game.fireEvent(new GameEventCardStatsChanged(c1));
|
||||
game.fireEvent(new GameEventCardStatsChanged(c2));
|
||||
}
|
||||
|
||||
private static void swapTextBox(final Card to, final TextBoxData from, final long ts) {
|
||||
List<SpellAbility> spellabilities = Lists.newArrayList();
|
||||
for (SpellAbility sa : from.spellabilities) {
|
||||
SpellAbility copy = sa.copy(to, false, true);
|
||||
// need to persist any previous word changes
|
||||
copy.changeTextIntrinsic(copy.getChangedTextColors(), copy.getChangedTextTypes());
|
||||
spellabilities.add(copy);
|
||||
}
|
||||
List<Trigger> triggers = Lists.newArrayList();
|
||||
for (Trigger tr : from.triggers) {
|
||||
Trigger copy = tr.copy(to, false, true);
|
||||
copy.changeTextIntrinsic(copy.getChangedTextColors(), copy.getChangedTextTypes());
|
||||
triggers.add(copy);
|
||||
}
|
||||
List<ReplacementEffect> reps = Lists.newArrayList();
|
||||
for (ReplacementEffect re : from.replacements) {
|
||||
ReplacementEffect copy = re.copy(to, false, true);
|
||||
copy.changeTextIntrinsic(copy.getChangedTextColors(), copy.getChangedTextTypes());
|
||||
reps.add(copy);
|
||||
}
|
||||
List<StaticAbility> statics = Lists.newArrayList();
|
||||
for (StaticAbility st : from.statics) {
|
||||
StaticAbility copy = st.copy(to, false, true);
|
||||
copy.changeTextIntrinsic(copy.getChangedTextColors(), copy.getChangedTextTypes());
|
||||
statics.add(copy);
|
||||
}
|
||||
to.addChangedCardTraitsByText(spellabilities, triggers, reps, statics, ts, 0);
|
||||
|
||||
List<KeywordInterface> kws = Lists.newArrayList();
|
||||
for (KeywordInterface kw : from.keywords) {
|
||||
kws.add(kw.copy(to, false));
|
||||
}
|
||||
to.addChangedCardKeywordsByText(kws, ts, 0, false);
|
||||
|
||||
to.updateChangedText();
|
||||
to.updateStateForView();
|
||||
}
|
||||
|
||||
private static TextBoxData captureTextBoxData(final Card card) {
|
||||
TextBoxData data = new TextBoxData();
|
||||
CardState state = card.getCurrentState();
|
||||
|
||||
data.spellabilities = Lists.newArrayList();
|
||||
for (SpellAbility sa : state.getSpellAbilities()) {
|
||||
if (sa.isIntrinsic() && sa.getKeyword() == null) {
|
||||
data.spellabilities.add(sa);
|
||||
}
|
||||
}
|
||||
data.triggers = Lists.newArrayList();
|
||||
for (Trigger tr : state.getTriggers()) {
|
||||
if (tr.isIntrinsic() && tr.getKeyword() == null) {
|
||||
data.triggers.add(tr);
|
||||
}
|
||||
}
|
||||
data.replacements = Lists.newArrayList();
|
||||
for (ReplacementEffect re : state.getReplacementEffects()) {
|
||||
if (re.isIntrinsic() && re.getKeyword() == null) {
|
||||
data.replacements.add(re);
|
||||
}
|
||||
}
|
||||
data.statics = Lists.newArrayList();
|
||||
for (StaticAbility st : state.getStaticAbilities()) {
|
||||
if (st.isIntrinsic() && st.getKeyword() == null) {
|
||||
data.statics.add(st);
|
||||
}
|
||||
}
|
||||
|
||||
data.keywords = Lists.newArrayList();
|
||||
for (KeywordInterface ki : card.getKeywords()) {
|
||||
if (ki.isIntrinsic()) {
|
||||
data.keywords.add(ki);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static class TextBoxData {
|
||||
List<SpellAbility> spellabilities;
|
||||
List<Trigger> triggers;
|
||||
List<ReplacementEffect> replacements;
|
||||
List<StaticAbility> statics;
|
||||
List<KeywordInterface> keywords;
|
||||
}
|
||||
}
|
||||
@@ -5324,6 +5324,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
}
|
||||
|
||||
public void setChangedCardKeywordsByText(Table<Long, Long, KeywordsChange> changedCardKeywords) {
|
||||
this.changedCardKeywordsByText.clear();
|
||||
for (Table.Cell<Long, Long, KeywordsChange> entry : changedCardKeywords.cellSet()) {
|
||||
this.changedCardKeywordsByText.put(entry.getRowKey(), entry.getColumnKey(), entry.getValue().copy(this, true));
|
||||
}
|
||||
}
|
||||
|
||||
public final void addChangedCardKeywordsInternal(
|
||||
final Collection<KeywordInterface> keywords, final Collection<KeywordInterface> removeKeywords,
|
||||
final boolean removeAllKeywords,
|
||||
@@ -8351,5 +8358,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
|
||||
this.changedCardNames.putAll(in.changedCardNames);
|
||||
setChangedCardTraits(in.getChangedCardTraits());
|
||||
|
||||
setChangedCardTraitsByText(in.getChangedCardTraitsByText());
|
||||
setChangedCardKeywordsByText(in.getChangedCardKeywordsByText());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +360,8 @@ public class CardCopyService {
|
||||
newCopy.setStoredReplacements(copyFrom.getStoredReplacements());
|
||||
|
||||
newCopy.copyChangedTextFrom(copyFrom);
|
||||
newCopy.updateChangedText();
|
||||
newCopy.changedTypeByText = copyFrom.changedTypeByText;
|
||||
newCopy.changedCardKeywordsByWord = copyFrom.changedCardKeywordsByWord.copy(newCopy, true);
|
||||
|
||||
newCopy.setGameTimestamp(copyFrom.getGameTimestamp());
|
||||
newCopy.setLayerTimestamp(copyFrom.getLayerTimestamp());
|
||||
|
||||
@@ -181,15 +181,18 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
return meetsCommonRequirements(getMapParams());
|
||||
}
|
||||
|
||||
public final ReplacementEffect copy(Card newHost, boolean lki) {
|
||||
return copy(newHost, lki, false);
|
||||
}
|
||||
/**
|
||||
* Gets the copy.
|
||||
*
|
||||
* @return the copy
|
||||
*/
|
||||
public final ReplacementEffect copy(final Card host, final boolean lki) {
|
||||
public final ReplacementEffect copy(final Card host, final boolean lki, boolean keepTextChanges) {
|
||||
final ReplacementEffect res = (ReplacementEffect) clone();
|
||||
|
||||
copyHelper(res, host);
|
||||
copyHelper(res, host, lki || keepTextChanges);
|
||||
|
||||
final SpellAbility sa = this.getOverridingAbility();
|
||||
if (sa != null) {
|
||||
|
||||
@@ -1208,6 +1208,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
public SpellAbility copy(Card host, final boolean lki) {
|
||||
return copy(host, this.getActivatingPlayer(), lki);
|
||||
}
|
||||
public SpellAbility copy(Card host, final boolean lki, boolean keepTextChanges) {
|
||||
return copy(host, this.getActivatingPlayer(), lki, keepTextChanges);
|
||||
}
|
||||
public SpellAbility copy(Card host, Player activ, final boolean lki) {
|
||||
return copy(host, activ, lki, false);
|
||||
}
|
||||
|
||||
@@ -591,13 +591,16 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
}
|
||||
}
|
||||
|
||||
public StaticAbility copy(Card host, final boolean lki) {
|
||||
public final StaticAbility copy(Card newHost, boolean lki) {
|
||||
return copy(newHost, lki, false);
|
||||
}
|
||||
public StaticAbility copy(Card host, final boolean lki, boolean keepTextChanges) {
|
||||
StaticAbility clone = null;
|
||||
try {
|
||||
clone = (StaticAbility) clone();
|
||||
clone.id = lki ? id : nextId();
|
||||
|
||||
copyHelper(clone, host);
|
||||
copyHelper(clone, host, lki || keepTextChanges);
|
||||
|
||||
// reset to force refresh if needed
|
||||
clone.payingTrigSA = null;
|
||||
|
||||
@@ -532,9 +532,12 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
}
|
||||
|
||||
public final Trigger copy(Card newHost, boolean lki) {
|
||||
return copy(newHost, lki, false);
|
||||
}
|
||||
public final Trigger copy(Card newHost, boolean lki, boolean keepTextChanges) {
|
||||
final Trigger copy = (Trigger) clone();
|
||||
|
||||
copyHelper(copy, newHost);
|
||||
copyHelper(copy, newHost, lki || keepTextChanges);
|
||||
|
||||
if (getOverridingAbility() != null) {
|
||||
copy.setOverridingAbility(getOverridingAbility().copy(newHost, lki));
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
- Bug fixes -
|
||||
As always, this release of Forge features an assortment of bug fixes and improvements based on user feedback during the previous release run.
|
||||
- Bug fixes -
|
||||
As always, this release of Forge features an assortment of bug fixes and improvements based on user feedback during the previous release run.
|
||||
|
||||
12
forge-gui/res/cardsfolder/d/deadpool_trading_card.txt
Normal file
12
forge-gui/res/cardsfolder/d/deadpool_trading_card.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Deadpool, Trading Card
|
||||
ManaCost:2 B R
|
||||
Types:Legendary Creature Mutant Mercenary Hero
|
||||
PT:5/3
|
||||
K:ETBReplacement:Other:DBChooseExchange:Optional
|
||||
SVar:DBChooseExchange:DB$ ChooseCard | Defined$ You | Choices$ Creature.Other | ChoiceTitle$ Choose a creature to exchange text boxes with | SubAbility$ DBExchangeText | SpellDescription$ As NICKNAME enters, you may exchange his text box and another creature's.
|
||||
SVar:DBExchangeText:DB$ ExchangeTextBox | Defined$ Self & ChosenCard
|
||||
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ Lose3 | TriggerDescription$ At the beginning of your upkeep, you lose 3 life.
|
||||
SVar:Lose3:DB$ LoseLife | Defined$ TriggeredPlayer | LifeAmount$ 3
|
||||
A:AB$ Draw | Cost$ 3 Sac<1/CARDNAME> | Defined$ Player.Other | NumCards$ 1 | SpellDescription$ Each other player draws a card.
|
||||
AI:RemoveDeck:All
|
||||
Oracle:As Deadpool enters, you may exchange his text box and another creature's.\nAt the beginning of your upkeep, you lose 3 life.\n{3}, Sacrifice this creature: Each other player draws a card.
|
||||
Reference in New Issue
Block a user