Skip to content

Move a cast spell to the graveyard when it's cast, not after it resolves - #416

Open
DragosIonita23 wants to merge 1 commit into
sindreslungaard:mainfrom
DragosIonita23:wonkerdrg/spell-cast-to-graveyard
Open

Move a cast spell to the graveyard when it's cast, not after it resolves#416
DragosIonita23 wants to merge 1 commit into
sindreslungaard:mainfrom
DragosIonita23:wonkerdrg/spell-cast-to-graveyard

Conversation

@DragosIonita23

Copy link
Copy Markdown
Contributor

Move a cast spell to the graveyard when it's cast, not after it resolves

fx.Spell kept a cast spell in its caster's hand for the spell's entire resolution: the HAND -> GRAVEYARD move was scheduled via two layers of ScheduleAfter, both deferred until after the spell's own effect (and anything that effect triggered) had already run. Any nested effect that inspected hand/graveyard state during that window saw stale data:

  • Soulswap putting Jagila into play let Jagila's "opponent discards 3 at random" discard Soulswap itself out of the caster's hand.
  • Soulswap/Miraculous Rebirth putting Phal Eega into play couldn't have Phal Eega recur the spell that just cast it, because that spell wasn't in the graveyard yet.
  • Pincer Scarab's power (+2000 per card in the opponent's hand) still counted a spell the opponent had just cast, keeping it out of reach of effects like Apocalypse Vise that destroy by total power.

Fixed by moving the card to the graveyard synchronously, as the first thing that happens on SpellCast, before the card's own effect body (registered later in the same c.Use chain) runs. Cards that redirect the final resting zone still do so from SpellResolved, same as before:

Audited the rest of the card set for code that hard-coded the old "still in hand while resolving" assumption (self-referential MoveCard calls, != card.ID exclusion filters paired with hand selections, and hand-size arithmetic computed during a spell's own resolution) and fixed two real regressions:

  • Boomerang Comet (dm03/spells.go) moved itself from hand to the mana zone with a hand-rolled MoveCard instead of fx.Charger; that fails silently since the card already left hand, so it now moves from the graveyard.
  • Mega Detonator (dm04/spells.go) computed its max discard count as (hand size - 1) to exclude itself; that now undercounts the remaining hand by one, so the subtraction and the now-redundant exclusion filter are removed.

A few other cards had the same now-redundant != card.ID guar (Enigmatic Cascade, Propeller Mutant) which were harmless but whose comments were actively wrong about current behavior; updated t accuracy. Left fx.SwapHandAndMana's defensive exclusion in place since it has its own unit test asserting that contract directly.

Also updated Morbid Medicine's "empty graveyard" test: it now sees itself in its own graveyard scan (as a spell, not a creature, so still not selectable), which means the return-prompt opens wit to pick instead of being skipped - a minor, correct side effect.

Added regression coverage: Soulswap (self-discard via Jagila, self- recursion via Phal Eega, both from hand and from a broken shie Miraculous Rebirth (same two interactions with Emeral and Phal Eega), Pincer Scarab vs. Apocalypse Vise, Super Terradragon Bailas Ga prior coverage), Boomerang Comet, and Mega Detonator. Each new test was verified to fail against the pre-fix code and pass against the

Full suite green under go test ./... -race, and `tests/cards to rule out flakiness from the random-discard interactions exercised here (Jagila, Ghost Touch, Propeller Mutant).

📝 Summary

Provide a brief summary of your changes and the motivation behind them.

🎴 New Cards Added

🐞 Bugs Fixed

  • Soulswap interactions with Phal Ega, Jagila etc
  • Apocalypse Vise interactions with Pincer Scarab etc
  • Miraculous Rebirth interactions with different cards (e.g. Emeral etc)

🔧 Other Changes

  • fx.Charger now relocates from the graveyard instead of hand.
  • Super Terradragon Bailas Gale (dm08/earth_dragon.go) used to prevent the graveyard move by cancelling the whole SpellResolved context; it now explicitly moves the spell back from the graveyard instead.

✅ Checklist

Please confirm the following before submitting your PR:

  • I have read CONTRIBUTING.md
  • The changes has been tested locally
  • Tests are written that covers the changes made and any bugs fixed (./sim/tests)

📸 Screenshots (if applicable)

If there are any visual changes to the frontend, please include some screenshots or screen recordings of it

fx.Spell kept a cast spell in its caster's hand for the spell's entire
resolution: the HAND -> GRAVEYARD move was scheduled via two layers of
ScheduleAfter, both deferred until after the spell's own effect (and
anything that effect triggered) had already run. Any nested effect that
inspected hand/graveyard state during that window saw stale data:

- Soulswap putting Jagila into play let Jagila's "opponent discards 3 at
  random" discard Soulswap itself out of the caster's hand.
- Soulswap/Miraculous Rebirth putting Phal Eega into play couldn't have
  Phal Eega recur the spell that just cast it, because that spell wasn't
  in the graveyard yet.
- Pincer Scarab's power (+2000 per card in the opponent's hand) still
  counted a spell the opponent had just cast, keeping it out of reach of
  effects like Apocalypse Vise that destroy by total power.

Fixed by moving the card to the graveyard synchronously, as the first
thing that happens on SpellCast, before the card's own effect body
(registered later in the same c.Use chain) runs. Cards that redirect the
final resting zone still do so from SpellResolved, same as before:

- fx.Charger now relocates from the graveyard instead of hand.
- Super Terradragon Bailas Gale (dm08/earth_dragon.go) used to prevent
  the graveyard move by cancelling the whole SpellResolved context;
  it now explicitly moves the spell back from the graveyard instead.

Audited the rest of the card set for code that hard-coded the old
"still in hand while resolving" assumption (self-referential MoveCard
calls, `!= card.ID` exclusion filters paired with hand selections, and
hand-size arithmetic computed during a spell's own resolution)
and fixed two real regressions:

- Boomerang Comet (dm03/spells.go) moved itself from hand to the mana
  zone with a hand-rolled MoveCard instead of fx.Charger; that
  fails silently since the card already left hand, so it now moves from
  the graveyard.
- Mega Detonator (dm04/spells.go) computed its max discard count as
  (hand size - 1) to exclude itself; that now undercounts the
  remaining hand by one, so the subtraction and the now-redundant
  exclusion filter are removed.

A few other cards had the same now-redundant `!= card.ID` guar
(Enigmatic Cascade, Propeller Mutant) which were harmless but whose
comments were actively wrong about current behavior; updated t
accuracy. Left fx.SwapHandAndMana's defensive exclusion in place since
it has its own unit test asserting that contract directly.

Also updated Morbid Medicine's "empty graveyard" test: it now
sees itself in its own graveyard scan (as a spell, not a creature, so
still not selectable), which means the return-prompt opens wit
to pick instead of being skipped - a minor, correct side effect.

Added regression coverage: Soulswap (self-discard via Jagila, self-
recursion via Phal Eega, both from hand and from a broken shie
Miraculous Rebirth (same two interactions with Emeral and Phal Eega),
Pincer Scarab vs. Apocalypse Vise, Super Terradragon Bailas Ga
prior coverage), Boomerang Comet, and Mega Detonator. Each new test was
verified to fail against the pre-fix code and pass against the

Full suite green under `go test ./... -race`, and `tests/cards
to rule out flakiness from the random-discard interactions exercised
here (Jagila, Ghost Touch, Propeller Mutant).
@DragosIonita23

Copy link
Copy Markdown
Contributor Author

@sindreslungaard This one is a bigger one, but it solves older problems with spell casting and spell resolution various interactions with other cards with special triggered effects, e.g. a spell should not be considered still in hand after casting. I feel it's an important one, and please comment here if you feel I missed some spells / specific interactions in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant