Skip to content

2 Bombazars incorrect ordering effect overridden bugfix - #417

Open
DragosIonita23 wants to merge 1 commit into
sindreslungaard:mainfrom
DragosIonita23:wonkerdrg/fix-2-bombazars-ordering-bug
Open

2 Bombazars incorrect ordering effect overridden bugfix#417
DragosIonita23 wants to merge 1 commit into
sindreslungaard:mainfrom
DragosIonita23:wonkerdrg/fix-2-bombazars-ordering-bug

Conversation

@DragosIonita23

Copy link
Copy Markdown
Contributor

Bombazar's extra-turn/lose obligation was tracked in local closure variables private to each physical card instance. When a second Bombazar enters and destroys the first (both are exactly 6000 power), the destroyed copy's own closure state — loseAfterExtraTurn = true — still lives on and still fires at the next EndOfTurnStep (card handlers run regardless of zone, by design, per the existing code comment).

The actual bug is in ordering: Match.HandleFx processes battlezone cards before graveyard cards, so the new Bombazar's "grant an extra turn" callback got appended to the shared ctx.postFxs queue before the destroyed Bombazar's "you lose" callback. The new copy's callback calls ctx.InterruptFlow() (needed to stop the engine's default turn hand-off), which sets ctx.cancel = true. The postFx loop checks ctx.cancel before running each entry, so the older Bombazar's pending loss got silently skipped — giving an unlimited chain of extra turns instead of the ruled-correct loss.

Fix

  • Added generic Card.LocalData/Card.SetLocalData accessors (sim/game/match/card.go) — a per-card store that, unlike conditions, isn't wiped every EndOfTurnStep.
  • Reworked BombazarDragonOfDestiny (sim/game/cards/dm10/armored_dragon.go) to track pending obligations as an ordered queue (oldest first) in that storage instead of closures. When a Bombazar destroys an earlier copy owned by the same player, it now inherits that copy's unresolved queue before creating its own new entry. Each EndOfTurnStep resolves only the oldest entry in the queue, so an earlier "lose" is never starved by a newer "extra turn" grant — regardless of postFx ordering.
  • Guarded the transfer to same-controller copies only, so destroying an opponent's Bombazar can never steal their pending loss.

Tests added (sim/tests/cards/bombazar_dragon_of_destiny_test.go)

  • Reproduces the reported bug exactly (two Bombazars back-to-back across the extra turn) — confirmed this test fails on the old code (match never closes) and passes with the fix.
  • Confirms destroying an opponent's Bombazar doesn't transfer their promise onto yours.

Full suite (go test ./...) and the race suite (go test -race ./...) both pass.

📝 Summary

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

🎴 New Cards Added

🐞 Bugs Fixed

🔧 Other Changes

✅ 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

Bombazar's extra-turn/lose obligation was tracked in local closure variables private to each physical card instance. When a second Bombazar enters and destroys the first (both are exactly 6000 power), the destroyed copy's own closure state — loseAfterExtraTurn = true — still lives on and still fires at the next EndOfTurnStep (card handlers run regardless of zone, by design, per the existing code comment).

The actual bug is in ordering: Match.HandleFx processes battlezone cards before graveyard cards, so the new Bombazar's "grant an extra turn" callback got appended to the shared ctx.postFxs queue before the destroyed Bombazar's "you lose" callback. The new copy's callback calls ctx.InterruptFlow() (needed to stop the engine's default turn hand-off), which sets ctx.cancel = true. The postFx loop checks ctx.cancel before running each entry, so the older Bombazar's pending loss got silently skipped — giving an unlimited chain of extra turns instead of the ruled-correct loss.

Fix

- Added generic Card.LocalData/Card.SetLocalData accessors (sim/game/match/card.go) — a per-card store that, unlike conditions, isn't wiped every EndOfTurnStep.
- Reworked BombazarDragonOfDestiny (sim/game/cards/dm10/armored_dragon.go) to track pending obligations as an ordered queue (oldest first) in that storage instead of closures. When a Bombazar destroys an earlier copy owned by the same player, it now inherits that copy's unresolved queue before creating its own new entry. Each EndOfTurnStep resolves only the oldest entry in the queue, so an earlier "lose" is never starved by a newer "extra turn" grant — regardless of postFx ordering.
- Guarded the transfer to same-controller copies only, so destroying an opponent's Bombazar can never steal their pending loss.

Tests added (sim/tests/cards/bombazar_dragon_of_destiny_test.go)

- Reproduces the reported bug exactly (two Bombazars back-to-back across the extra turn) — confirmed this test fails on the old code (match never closes) and passes with the fix.
- Confirms destroying an opponent's Bombazar doesn't transfer their promise onto yours.

Full suite (go test ./...) and the race suite (go test -race ./...) both pass.
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