Skip to content

Counters scheduled onto a liminal token entrant lose the CR 122.6a placer #8607

Description

@matthewevans

Summary

Counters redirected onto a liminal token entrant lose the player who put them there. LiminalEntry::enter_with_counters is a Vec<(CounterType, u32)> with no actor slot, so the CR 122.6a placer is discarded at the redirect and the commit pass places the counters as somebody else.

The first case this is observable in is CR 702.104a Tribute on a token copy.

Where the actor is lost

add_counter_with_replacement receives an actor and, for a live object, threads it into ProposedEvent::AddCounter { placement: CounterPlacement::Object { actor, .. } }. For a liminal token entrant it takes the redirect instead:

  • crates/engine/src/game/effects/counters.rs:227if state.schedule_entry_counters(object_id, counter_type.clone(), count) { return true; }. The early return happens before actor is used.
  • crates/engine/src/types/game_state.rsGameState::schedule_entry_counters(&mut self, id, counter_type, count) takes no actor and pushes (counter_type, count) onto entry.enter_with_counters.
  • crates/engine/src/game/effects/token.rs — the commit pass reads enter_with_counters back as Vec<(CounterType, u32)> (see token.rs:665, :1505, :1522), so there is nothing to restore the actor from.

The caller does supply a meaningful actor:

  • crates/engine/src/game/effects/tribute.rs:104apply_paid passes the paying opponent as actor.

Why the actor is not bookkeeping

CR 702.104a (docs/MagicCompRules.txt:4774):

Tribute is a static ability that functions as the creature with tribute is entering the battlefield. "Tribute N" means "As this creature enters, choose an opponent. That player may put an additional N +1/+1 counters on it as it enters."

The rule names the opponent as the placer, not the entering permanent's controller.

Replacement effects key on that. Vorinclex, Monstrous Raider:

If you would put one or more counters on a permanent or player, put twice that many of each of those kinds of counters on that permanent or player instead.
If an opponent would put one or more counters on a permanent or player, they put half that many of each of those kinds of counters on that permanent or player instead, rounded down.

So with a Vorinclex on the battlefield, a paid Tribute of N on a token copy should resolve to a halved or doubled count depending on who the placer is, and today the placer recorded is not the opponent CR 702.104a chose. GameEvent::CounterAdded trigger evaluation reads the same field.

Scope of the fix

enter_with_counters predates this seam — it has been actor-less since it was introduced, and the redirect in counters.rs is what first routes an actor-bearing call into it. Carrying the actor reaches:

  • the LiminalEntry / token-spec types that hold enter_with_counters (and their serialization),
  • GameState::schedule_entry_counters,
  • every commit path that replays the list, including the resumed-entry path.

History

Found by CodeRabbit on #8606 and confirmed there. It is not a regression from that PR — before it, these counters were dropped entirely (apply_counter_addition found no object and returned true), so a paid tribute placed none at all. #8606 makes them land; this issue is the remaining attribution gap and was left out of that PR deliberately because the fix is a type change rather than a local correction.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug fix

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions