Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/prs/pr-010-enrich-event-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# PR 10: Enrich events with context available at creation time

## Summary

Several events were created with only the minimum data needed at the time they were written, forcing ability handlers to query live game state after the fact. This PR adds fields that were already available at each event's creation site.

`Events::DamageDealt` is left unchanged — enriching it with "damage that actually resolved after prevention" requires a damage-prevention tracking system that does not exist yet.

## What Changed

**`lib/magic/events/card_draw.rb`**
- Added `card` field (the `Card` object removed from the library)
- Field is optional (`card: nil`) so any future call site that omits it continues to work

**`lib/magic/player.rb`**
- `draw!` now passes `card:` to `Events::CardDraw`; the card is available immediately after `library.draw` and before `move_to_hand!`

**`lib/magic/events/spell_cast.rb`**
- Added `x_value` — the raw X value passed at cast time (nil if not an X spell)
- Added `flashback?` — true when the spell was cast from the graveyard via flashback
- Added `targets` — the array of chosen targets at the time the spell was placed on the stack
- `x_value` and `targets` exposed via `attr_reader`; `flashback?` is a predicate method

**`lib/magic/actions/cast.rb`**
- `perform` now passes `x_value:`, `flashback:`, and `targets:` to `Events::SpellCast`; all three are already in scope at that call site

**`lib/magic/events/entered_the_battlefield.rb`**
- Added `kicked?` predicate (stored as `@kicked`, defaulting to `false`)

**`lib/magic/events/permanent_entered_zone_transition.rb`**
- Passes `kicked: permanent.kicked?` when constructing `Events::EnteredTheBattlefield`

## Why these fields

| Field | Previous workaround | Now |
|---|---|---|
| `CardDraw#card` | Inspect player's hand after the fact | Read `event.card` directly |
| `SpellCast#x_value` | Read `mana_cost.x` off the stack item | `event.x_value` |
| `SpellCast#flashback?` | Check card's current zone (may have changed) | `event.flashback?` |
| `SpellCast#targets` | Re-resolve targets from stack item | `event.targets` |
| `EnteredTheBattlefield#kicked?` | Call `permanent.kicked?` on the actor | `event.kicked?` |

## Invariants Preserved

- All new fields have safe defaults (nil / false / []) so existing creation sites that don't pass them continue to compile and work
- No existing handler reads the new fields yet; this is purely additive
- All 534 tests pass
8 changes: 7 additions & 1 deletion lib/magic/actions/cast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@ def perform
mana_cost.finalize!(player)
game.stack.add(self)

game.notify!(Events::SpellCast.new(spell: card, player: player))
game.notify!(Events::SpellCast.new(
spell: card,
player: player,
x_value: value_for_x,
flashback: @flashback,
targets: targets,
))
end

def choose_mode(mode_class, &)
Expand Down
5 changes: 3 additions & 2 deletions lib/magic/events/card_draw.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module Magic
module Events
class CardDraw
attr_reader :player
attr_reader :player, :card

def initialize(player:)
def initialize(player:, card: nil)
@player = player
@card = card
end

def inspect
Expand Down
7 changes: 6 additions & 1 deletion lib/magic/events/entered_the_battlefield.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ module Events
class EnteredTheBattlefield < Base
attr_reader :permanent, :from

def initialize(permanent, from:)
def initialize(permanent, from:, kicked: false)
@from = from
@permanent = permanent
@kicked = kicked
end

def kicked?
@kicked
end

def inspect
Expand Down
2 changes: 1 addition & 1 deletion lib/magic/events/permanent_entered_zone_transition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def self.new(permanent, from:, to:)
events = []

if to.battlefield?
events << Events::EnteredTheBattlefield.new(permanent, from: from)
events << Events::EnteredTheBattlefield.new(permanent, from: from, kicked: permanent.kicked?)
events << Events::Landfall.new(permanent) if permanent.land?
else
events << Events::PermanentEnteredZone.new(permanent, from: from, to: to)
Expand Down
11 changes: 9 additions & 2 deletions lib/magic/events/spell_cast.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
module Magic
module Events
class SpellCast
attr_reader :spell, :player
attr_reader :spell, :player, :x_value, :targets

def initialize(spell:, player:)
def initialize(spell:, player:, x_value: nil, flashback: false, targets: [])
@spell = spell
@player = player
@x_value = x_value
@flashback = flashback
@targets = targets
end

def flashback?
@flashback
end

def type?(type)
Expand Down
5 changes: 1 addition & 4 deletions lib/magic/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ def unsubscribe(listener)
def start!
@current_turn = add_turn(number: 1, active_player: players.first)
players.each do |player|
7.times do
card = player.library.draw
card&.move_to_hand!(player)
end
7.times { player.draw! }
end
end

Expand Down
1 change: 1 addition & 0 deletions lib/magic/player.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def draw!
game.notify!(
Events::CardDraw.new(
player: self,
card: card,
)
)
card.move_to_hand!(self)
Expand Down
Loading