mage icon indicating copy to clipboard operation
mage copied to clipboard

Non-stack delayed triggers must be reworked?

Open JayDi85 opened this issue 1 year ago • 1 comments

It's not a critical problem, can be related to some rare bugs with commander moves, counters conditional and other state base things. See example: #4025

Affected cards

[[Banisher Priest]], [[Grasp of Fate]] and other with rules like "exile ... until {this} leaves the battlefield". Search problem cards by usesStack = false or new OnLeaveReturnExiledAbility. shot_240411_222040

Problem

Current code do not support full rules like "nothing happens between the two events, including state-based actions" (see related test in StateBaseTriggeredAbilityTest::test_GraspOfFate_DelayedTriggerMustResolveImmediately).

Non-stack delayed triggers are xmage's workaround to support specific cards instead replacement effects usage. I don't find any MTG rules about non-stack triggers. That effects must be resolved immediately like mana abilities does -- but it uses triggers lifecycle instead (all triggered collects in shared list and executes one by one on new game cycle).

Related rules:

Banisher Priest's ability causes a zone change with a duration, a new style of ability that's somewhat reminiscent of older cards like Oblivion Ring. However, unlike Oblivion Ring, cards like Banisher Priest have a single ability that creates two one-shot effects: one that exiles the creature when the ability resolves, and another that returns the exiled card to the battlefield immediately after Banisher Priest leaves the battlefield. (2013-07-01)

The exiled card returns to the battlefield immediately after Banisher Priest leaves the battlefield. Nothing happens between the two events, including state-based actions. The two creatures aren't on the battlefield at the same time. For example, if the returning creature is a Clone, it can't enter the battlefield as a copy of Banisher Priest. (2013-07-01)

In a multiplayer game, if Grasp of Fate's owner leaves the game, the exiled cards will return to the battlefield. Because the one-shot effect that returns the cards isn't an ability that goes on the stack, it won't cease to exist along with the leaving player's spells and abilities on the stack. (2015-11-04)

Possible solutions

I'm tried to fix it by immediately resolve like mana abilities does but it not work due new game cycle usage (ApplyEffects) on active/resolve. See details docs by code search state.addTriggeredAbility or related commit 367defd9958ac88b28206c6841771a71ee9b3ef5.

So only cards rework can help here. All related cards must migrate to existing replacement effects due card rules (example replacement effect on zone move). Make sure it support resolve on controller leave (delayed triggers does). The last one very important feature of delayed triggers: #4025.

JayDi85 avatar Apr 11 '24 19:04 JayDi85

Banisher Priest - (Gatherer) (Scryfall) (EDHREC)

{1}{W}{W} Creature — Human Cleric 2/2 When Banisher Priest enters the battlefield, exile target creature an opponent controls until Banisher Priest leaves the battlefield. (That creature returns under its owner's control.)

Grasp of Fate - (Gatherer) (Scryfall) (EDHREC)

{1}{W}{W} Enchantment When Grasp of Fate enters the battlefield, for each opponent, exile up to one target nonland permanent that player controls until Grasp of Fate leaves the battlefield. (Those permanents return under their owners' control.)

github-actions[bot] avatar Apr 11 '24 19:04 github-actions[bot]

Another problem: different duration in delayed triggers (see related PR in #12253):

Delayed triggers can be used with different duration:

  • Duration.EndOfTurn like [[Aggravated Assault]];
  • Duration.EndOfGame like [[Magus of the Unseen]];
  • Duration.Custom like [[Waylay]] -- before changes in #12253 it keeps in game forever, but after changes it will be discarded as fast as possible on first new game cycle (ApplyEffects or ProcessActions).
  • Etc

Old contract:

  • keep all custom delayed triggers forever (it's important cause some triggers used for take/lose permanent control);
  • keep all other durations until that duration ends like other effects does;

New contract after #12253:

  • keep custom delayed triggers with non-stack forever (example: control change - must be executed anyway);
  • keep custom delayed triggers with stack until player leave the game (example: create token - must be discarded as fast as possible);
  • keep all other durations until that duration ends like other effects does;

What can be checked:

  • [ ] So it's important to check stack delayed triggers and make sure it really need a stack (e.g. create something new) -- if not then replace with non-stack version or by replacement effect (see started topic).
  • [ ] Also make sure there are no override isInactive methods in delayed triggers (it must be compatible with new stack and non-stack logic)

JayDi85 avatar May 18 '24 18:05 JayDi85

Aggravated Assault - (Gatherer) (Scryfall) (EDHREC)

{2}{R} Enchantment {3}{R}{R}: Untap all creatures you control. After this main phase, there is an additional combat phase followed by an additional main phase. Activate only as a sorcery.

Magus of the Unseen - (Gatherer) (Scryfall) (EDHREC)

{1}{U} Creature — Human Wizard 1/1 {1}{U}, {T}: Untap target artifact an opponent controls and gain control of it until end of turn. It gains haste until end of turn. When you lose control of the artifact, tap it.

Waylay - (Gatherer) (Scryfall) (EDHREC)

{2}{W} Instant Create three 2/2 white Knight creature tokens. Exile them at the beginning of the next cleanup step.

github-actions[bot] avatar May 18 '24 18:05 github-actions[bot]