Trigger: reuse Execute for multiple Trigger (#9094)

* Trigger: reuse Execute for multiple Trigger

* Update CardState.java

copy abilityForTrigger

* Update CardState.java

* Update Trigger.java

* Update CardState.java

* Fix Multi Trigger with Idris

* Fix Oasis of Renewal ActivationLimit

* fix storedAbilityForTrigger

* remove hasAbilityForTrigger

* Update script

---------

Co-authored-by: tool4EvEr <tool4EvEr@>
This commit is contained in:
Hans Mackowiak
2025-11-11 08:46:53 +01:00
committed by GitHub
parent c90ddd1bac
commit bce5466c62
6 changed files with 46 additions and 33 deletions

View File

@@ -140,6 +140,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
// stores the card traits created by static abilities // stores the card traits created by static abilities
private final Table<StaticAbility, String, SpellAbility> storedSpellAbility = TreeBasedTable.create(); private final Table<StaticAbility, String, SpellAbility> storedSpellAbility = TreeBasedTable.create();
private final Table<StaticAbility, String, Trigger> storedTrigger = TreeBasedTable.create(); private final Table<StaticAbility, String, Trigger> storedTrigger = TreeBasedTable.create();
private final Table<StaticAbility, SpellAbility, SpellAbility> storedAbilityForTrigger = HashBasedTable.create();
private final Table<StaticAbility, String, ReplacementEffect> storedReplacementEffect = TreeBasedTable.create(); private final Table<StaticAbility, String, ReplacementEffect> storedReplacementEffect = TreeBasedTable.create();
private final Table<StaticAbility, String, StaticAbility> storedStaticAbility = TreeBasedTable.create(); private final Table<StaticAbility, String, StaticAbility> storedStaticAbility = TreeBasedTable.create();
@@ -4974,7 +4975,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
String str = trig.toString() + trig.getId(); String str = trig.toString() + trig.getId();
Trigger result = storedTrigger.get(stAb, str); Trigger result = storedTrigger.get(stAb, str);
if (result == null) { if (result == null) {
result = trig.copy(this, false); SpellAbility ab = null;
if (trig.hasParam("Execute") && trig.getOverridingAbility() != null) {
ab = storedAbilityForTrigger.get(stAb, trig.getOverridingAbility());
if (ab == null) {
ab = trig.getOverridingAbility().copy(this, false);
storedAbilityForTrigger.put(stAb, trig.getOverridingAbility(), ab);
}
}
result = trig.copy(this, false, false, ab);
storedTrigger.put(stAb, str, result); storedTrigger.put(stAb, str, result);
} }
return result; return result;

View File

@@ -79,6 +79,7 @@ public class CardState implements GameObject, IHasSVars, ITranslatable {
private FCollection<StaticAbility> staticAbilities = new FCollection<>(); private FCollection<StaticAbility> staticAbilities = new FCollection<>();
private String imageKey = ""; private String imageKey = "";
private Map<String, String> sVars = Maps.newTreeMap(); private Map<String, String> sVars = Maps.newTreeMap();
private Map<String, SpellAbility> abilityForTrigger = Maps.newHashMap();
private KeywordCollection cachedKeywords = new KeywordCollection(); private KeywordCollection cachedKeywords = new KeywordCollection();
@@ -732,6 +733,11 @@ public class CardState implements GameObject, IHasSVars, ITranslatable {
setFlavorName(source.getFlavorName()); setFlavorName(source.getFlavorName());
setSVars(source.getSVars()); setSVars(source.getSVars());
abilityForTrigger.clear();
for (Map.Entry<String, SpellAbility> e : source.abilityForTrigger.entrySet()) {
abilityForTrigger.put(e.getKey(), e.getValue().copy(card, lki));
}
abilities.clear(); abilities.clear();
for (SpellAbility sa : source.abilities) { for (SpellAbility sa : source.abilities) {
if (sa.isIntrinsic()) { if (sa.isIntrinsic()) {
@@ -758,7 +764,7 @@ public class CardState implements GameObject, IHasSVars, ITranslatable {
continue; continue;
} }
if (tr.isIntrinsic()) { if (tr.isIntrinsic()) {
triggers.add(tr.copy(card, lki)); triggers.add(tr.copy(card, lki, false, tr.hasParam("Execute") ? abilityForTrigger.get(tr.getParam("Execute")) : null));
} }
} }
ReplacementEffect runRE = null; ReplacementEffect runRE = null;
@@ -951,6 +957,10 @@ public class CardState implements GameObject, IHasSVars, ITranslatable {
return cloakUp; return cloakUp;
} }
public SpellAbility getAbilityForTrigger(String svar) {
return abilityForTrigger.computeIfAbsent(svar, s -> AbilityFactory.getAbility(getCard(), s, this));
}
@Override @Override
public String getTranslationKey() { public String getTranslationKey() {
String displayName = flavorName == null ? name : flavorName; String displayName = flavorName == null ? name : flavorName;

View File

@@ -544,14 +544,19 @@ public abstract class Trigger extends TriggerReplacementBase {
} }
public final Trigger copy(Card newHost, boolean lki) { public final Trigger copy(Card newHost, boolean lki) {
return copy(newHost, lki, false); return copy(newHost, lki, false, null);
} }
public final Trigger copy(Card newHost, boolean lki, boolean keepTextChanges) { public final Trigger copy(Card newHost, boolean lki, boolean keepTextChanges) {
return copy(newHost, lki, keepTextChanges, null);
}
public final Trigger copy(Card newHost, boolean lki, boolean keepTextChanges, SpellAbility spellAbility) {
final Trigger copy = (Trigger) clone(); final Trigger copy = (Trigger) clone();
copyHelper(copy, newHost, lki || keepTextChanges); copyHelper(copy, newHost, lki || keepTextChanges);
if (getOverridingAbility() != null) { if (spellAbility != null) {
copy.setOverridingAbility(spellAbility);
} else if (getOverridingAbility() != null) {
copy.setOverridingAbility(getOverridingAbility().copy(newHost, lki)); copy.setOverridingAbility(getOverridingAbility().copy(newHost, lki));
} }
@@ -611,7 +616,11 @@ public abstract class Trigger extends TriggerReplacementBase {
public SpellAbility ensureAbility(final IHasSVars sVarHolder) { public SpellAbility ensureAbility(final IHasSVars sVarHolder) {
SpellAbility sa = getOverridingAbility(); SpellAbility sa = getOverridingAbility();
if (sa == null && hasParam("Execute")) { if (sa == null && hasParam("Execute")) {
sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute"), sVarHolder); if (this.isIntrinsic() && sVarHolder instanceof CardState state) {
sa = state.getAbilityForTrigger(getParam("Execute"));
} else {
sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute"), sVarHolder);
}
setOverridingAbility(sa); setOverridingAbility(sa);
} }
return sa; return sa;

View File

@@ -3,12 +3,8 @@ ManaCost:4 U
Types:Enchantment Types:Enchantment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigIncubate | TriggerDescription$ When CARDNAME enters, incubate 4. (Create an Incubator token with four +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.) T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigIncubate | TriggerDescription$ When CARDNAME enters, incubate 4. (Create an Incubator token with four +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.)
SVar:TrigIncubate:DB$ Incubate | Amount$ 4 SVar:TrigIncubate:DB$ Incubate | Amount$ 4
T:Mode$ Transformed | ValidCard$ Permanent.YouCtrl+inZoneBattlefield | Execute$ TrigDraw | TriggerZones$ Battlefield | OptionalDecider$ You | CheckSVar$ X | SVarCompare$ LT1 | TriggerDescription$ Whenever a permanent you control transforms or a permanent you control enters transformed, you may draw a card. Do this only once each turn. T:Mode$ Transformed | ValidCard$ Permanent.YouCtrl+inZoneBattlefield | Execute$ TrigDraw | TriggerZones$ Battlefield | OptionalDecider$ You | ResolvedLimit$ 1 | TriggerDescription$ Whenever a permanent you control transforms or a permanent you control enters transformed, you may draw a card. Do this only once each turn.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Permanent.Transformed+YouCtrl | OptionalDecider$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | Secondary$ True | CheckSVar$ X | SVarCompare$ LT1 | TriggerDescription$ Whenever a permanent you control transforms or a permanent you control enters transformed, you may draw a card. Do this only once each turn. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Permanent.Transformed+YouCtrl | OptionalDecider$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | Secondary$ True | ResolvedLimit$ 1 | TriggerDescription$ Whenever a permanent you control transforms or a permanent you control enters transformed, you may draw a card. Do this only once each turn.
SVar:TrigDraw:DB$ Draw | SubAbility$ DBLog SVar:TrigDraw:DB$ Draw
SVar:DBLog:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 1
SVar:X:Number$0
T:Mode$ Phase | Phase$ Cleanup | TriggerZones$ Battlefield | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 0
DeckHas:Ability$Counters|Token & Type$Incubator|Artifact|Phyrexian DeckHas:Ability$Counters|Token & Type$Incubator|Artifact|Phyrexian
Oracle:When Corruption of Towashi enters, incubate 4. (Create an Incubator token with four +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.)\nWhenever a permanent you control transforms or a permanent you control enters transformed, you may draw a card. Do this only once each turn. Oracle:When Corruption of Towashi enters, incubate 4. (Create an Incubator token with four +1/+1 counters on it and "{2}: Transform this artifact." It transforms into a 0/0 Phyrexian artifact creature.)\nWhenever a permanent you control transforms or a permanent you control enters transformed, you may draw a card. Do this only once each turn.

View File

@@ -1,18 +1,10 @@
Name:Oasis of Renewal Name:Oasis of Renewal
ManaCost:B G U ManaCost:B G U
Types:Legendary Enchantment Types:Legendary Enchantment
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSeekLand | TriggerDescription$ When CARDNAME enters and whenever a land card leaves your graveyard, seek a land card. This ability triggers only once each turn. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | ActivationLimit$ 1 | Execute$ TrigSeekLand | TriggerDescription$ When CARDNAME enters and whenever a land card leaves your graveyard, seek a land card. This ability triggers only once each turn.
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.Land+YouOwn | TriggerZones$ Battlefield | Execute$ TrigSeekLand | Secondary$ True | CheckSVar$ X | SVarCompare$ LT1 | TriggerDescription$ When CARDNAME enters and whenever a land card leaves your graveyard, seek a land card. This ability triggers only once each turn. T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.Land+YouOwn | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigSeekLand | Secondary$ True | TriggerDescription$ When CARDNAME enters and whenever a land card leaves your graveyard, seek a land card. This ability triggers only once each turn.
SVar:TrigSeekLand:DB$ Seek | Type$ Card.Land | SubAbility$ DBLogLand SVar:TrigSeekLand:DB$ Seek | Type$ Card.Land
SVar:DBLogLand:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 1 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | ActivationLimit$ 1 | Execute$ TrigSeekNonLand | TriggerDescription$ When CARDNAME enters and whenever a nonland card leaves your graveyard, seek a nonland card. This ability triggers only once each turn.
SVar:X:Number$0 T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.nonLand+YouOwn | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigSeekNonLand | Secondary$ True | TriggerDescription$ When CARDNAME enters and whenever a nonland card leaves your graveyard, seek a nonland card. This ability triggers only once each turn.
T:Mode$ Phase | Phase$ Cleanup | TriggerZones$ Battlefield | Execute$ DBLandClean | Static$ True SVar:TrigSeekNonLand:DB$ Seek | Type$ Card.nonLand
SVar:DBLandClean:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 0
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSeekNonLand | TriggerDescription$ When CARDNAME enters and whenever a nonland card leaves your graveyard, seek a nonland card. This ability triggers only once each turn.
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.nonLand+YouOwn | TriggerZones$ Battlefield | Execute$ TrigSeekNonLand | Secondary$ True | CheckSVar$ Y | SVarCompare$ LT1 | TriggerDescription$ When CARDNAME enters and whenever a nonland card leaves your graveyard, seek a nonland card. This ability triggers only once each turn.
SVar:TrigSeekNonLand:DB$ Seek | Type$ Card.nonLand | SubAbility$ DBLogNonLand
SVar:DBLogNonLand:DB$ StoreSVar | SVar$ Y | Type$ Number | Expression$ 1
SVar:Y:Number$0
T:Mode$ Phase | Phase$ Cleanup | TriggerZones$ Battlefield | Execute$ DBNonLandClean | Static$ True
SVar:DBNonLandClean:DB$ StoreSVar | SVar$ Y | Type$ Number | Expression$ 0
Oracle:When Oasis of Renewal enters and whenever a land card leaves your graveyard, seek a land card. This ability triggers only once each turn.\nWhen Oasis of Renewal enters and whenever a nonland card leaves your graveyard, seek a nonland card. This ability triggers only once each turn. Oracle:When Oasis of Renewal enters and whenever a land card leaves your graveyard, seek a land card. This ability triggers only once each turn.\nWhen Oasis of Renewal enters and whenever a nonland card leaves your graveyard, seek a nonland card. This ability triggers only once each turn.

View File

@@ -4,12 +4,9 @@ Types:Legendary Creature Human Warlock
PT:3/3 PT:3/3
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigSurveil | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigSurveil | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control.
T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | Execute$ TrigSurveil | TriggerZones$ Battlefield | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control. T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | Execute$ TrigSurveil | TriggerZones$ Battlefield | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control.
SVar:TrigSurveil:DB$ Surveil | Amount$ 2 | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ1 | SubAbility$ DBDiscard SVar:TrigSurveil:DB$ Surveil | Amount$ 2 | ConditionCheckSVar$ Resolved | ConditionSVarCompare$ EQ1 | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Defined$ Player.Opponent | Mode$ TgtChoose | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ2 | SubAbility$ DBChangeZone SVar:DBDiscard:DB$ Discard | Defined$ Player.Opponent | Mode$ TgtChoose | ConditionCheckSVar$ Resolved | ConditionSVarCompare$ EQ2 | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature | ChangeNum$ 1 | Mandatory$ True | GainControl$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ3 | SelectPrompt$ Select a creature card in a graveyard | Hidden$ True | SubAbility$ DBLog SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature | ChangeNum$ 1 | Mandatory$ True | GainControl$ True | ConditionCheckSVar$ Resolved | ConditionSVarCompare$ EQ3 | SelectPrompt$ Select a creature card in a graveyard | Hidden$ True
SVar:DBLog:DB$ StoreSVar | SVar$ X | Type$ CountSVar | Expression$ X/Plus.1 SVar:Resolved:Count$ResolvedThisTurn
SVar:X:Number$1
T:Mode$ Phase | Phase$ Cleanup | TriggerZones$ Battlefield | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 1
DeckNeeds:Type$Enchantment DeckNeeds:Type$Enchantment
Oracle:Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control. Oracle:Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control.