diff --git a/lib/magic/card.rb b/lib/magic/card.rb index 0246d09..661113a 100644 --- a/lib/magic/card.rb +++ b/lib/magic/card.rb @@ -7,7 +7,7 @@ class Card include Cards::Keywords include Cards::Shared::Events include Cards::Shared::Types - attr_reader :game, :controller, :owner, :name, :cost, :kicker_cost, :types, :countered, :keyword_grants, :keywords, :protections, :delayed_responses, :modes + attr_reader :game, :controller, :owner, :name, :cost, :kicker_cost, :types, :countered, :keyword_grants, :keywords, :protections, :modes attr_accessor :tapped attr_accessor :zone @@ -93,7 +93,6 @@ def initialize(game: Game.new, owner:) @cost = Costs::Mana.new(self.class::COST.dup) @kicker_cost = Costs::Kicker.new(self.class::KICKER_COST.dup) @tapped = tapped - @delayed_responses = [] @keywords = self.class::KEYWORDS @keyword_grants = [] @protections = self.class::PROTECTIONS diff --git a/lib/magic/cards/basri_ket.rb b/lib/magic/cards/basri_ket.rb index 2d48179..b8ba478 100644 --- a/lib/magic/cards/basri_ket.rb +++ b/lib/magic/cards/basri_ket.rb @@ -41,23 +41,21 @@ def resolve!(target:) end end + class PreliminaryAttackersHandler < TriggeredAbility + def call + attackers = game.current_turn.attacks.count + attackers.times do + token = trigger_effect(:create_token, token_class: SoldierToken, enters_tapped: true).first + game.current_turn.declare_attacker(token) + end + end + end + class LoyaltyAbility2 < Magic::LoyaltyAbility def loyalty_change = -2 def resolve! - source.delayed_response( - turn: game.current_turn, - event_type: Events::PreliminaryAttackersDeclared, - response: -> { - attackers = game.current_turn.attacks.count - - attackers.times do - token = trigger_effect(:create_token, token_class: SoldierToken, enters_tapped: true).first - - game.current_turn.declare_attacker(token) - end - } - ) + source.register_until_eot_handler(Events::PreliminaryAttackersDeclared, PreliminaryAttackersHandler) end end @@ -76,6 +74,7 @@ def loyalty_abilities LoyaltyAbility3, ] end + end end end diff --git a/lib/magic/cards/massacre_girl.rb b/lib/magic/cards/massacre_girl.rb index 0f3f6be..2663f5f 100644 --- a/lib/magic/cards/massacre_girl.rb +++ b/lib/magic/cards/massacre_girl.rb @@ -8,20 +8,19 @@ class MassacreGirl < Creature toughness 4 keywords :menace - enters_the_battlefield do - debuff = -> do + class CreatureDiedDebuff < TriggeredAbility + def call game.creatures.except(actor).each do |creature| - actor.trigger_effect(:modify_power_toughness, target: creature, power: -1, toughness: -1) + trigger_effect(:modify_power_toughness, target: creature, power: -1, toughness: -1) end end + end - debuff.call - - actor.delayed_response( - turn: game.current_turn, - event_type: Events::CreatureDied, - response: debuff, - ) + enters_the_battlefield do + game.creatures.except(actor).each do |creature| + actor.trigger_effect(:modify_power_toughness, target: creature, power: -1, toughness: -1) + end + actor.register_until_eot_handler(Events::CreatureDied, CreatureDiedDebuff) end end end diff --git a/lib/magic/cards/sanctum_of_fruitful_harvest.rb b/lib/magic/cards/sanctum_of_fruitful_harvest.rb new file mode 100644 index 0000000..156b991 --- /dev/null +++ b/lib/magic/cards/sanctum_of_fruitful_harvest.rb @@ -0,0 +1,33 @@ +module Magic + module Cards + SanctumOfFruitfulHarvest = Enchantment("Sanctum of Fruitful Harvest") do + type T::Super::Legendary, T::Enchantment, "Shrine" + cost generic: 2, green: 1 + + class FirstMainPhaseTrigger < TriggeredAbility + def should_perform? + event.active_player == controller + end + + def call + game.choices.add(SanctumOfFruitfulHarvest::ColorChoice.new(actor: actor)) + end + end + + def event_handlers + { + Events::FirstMainPhase => FirstMainPhaseTrigger + } + end + end + + class SanctumOfFruitfulHarvest < Enchantment + class ColorChoice < Magic::Choice::Color + def resolve!(color:) + shrines = controller.permanents.by_type("Shrine").count + controller.add_mana(color => shrines) + end + end + end + end +end diff --git a/lib/magic/permanent.rb b/lib/magic/permanent.rb index ef73179..4f19e59 100644 --- a/lib/magic/permanent.rb +++ b/lib/magic/permanent.rb @@ -17,7 +17,6 @@ class Permanent :power, :toughness, :keywords, - :delayed_responses, :attachments, :protections, :modifiers, @@ -65,7 +64,7 @@ def initialize(game:, owner:, card:, token: false, cast: true, kicked: false, co @kicked = kicked @copy = copy @base_types = card.types - @delayed_responses = [] + @eot_handlers = [] @attachments = [] @modifiers = [] @tapped = false @@ -181,7 +180,6 @@ def replacement_matcher_applies?(matcher, effect) end def receive_event(event) - trigger_delayed_response(event) dispatch_lifecycle_triggers(event) dispatch_event_handlers(event) end @@ -283,18 +281,12 @@ def can_block?(permanent) end - def delayed_response(turn:, event_type:, response:) - @delayed_responses << { turn: turn.number, event_type: event_type, response: response } - end - - def trigger_delayed_response(event) - responses = delayed_responses.select { |response| event.is_a?(response[:event_type]) && response[:turn] == game.current_turn.number } - responses.each do |response| - response[:response].call - end + def register_until_eot_handler(event_type, handler_class) + @eot_handlers << { event_type: event_type, handler_class: handler_class } end def cleanup! + @eot_handlers = [] remove_until_eot_keyword_grants! remove_until_eot_protections! remove_until_eot_modifiers! @@ -391,6 +383,10 @@ def dispatch_event_handlers(event) logger.debug "EVENT HANDLER: #{self} handling #{event}" handler_class.new(actor: self, event: event).perform! end + + @eot_handlers.select { |h| event.is_a?(h[:event_type]) }.each do |h| + h[:handler_class].new(actor: self, event: event).perform! + end end def remove_until_eot_keyword_grants! diff --git a/spec/cards/basri_ket_spec.rb b/spec/cards/basri_ket_spec.rb index 41e0a81..0d1e549 100644 --- a/spec/cards/basri_ket_spec.rb +++ b/spec/cards/basri_ket_spec.rb @@ -32,14 +32,12 @@ allow(game).to receive(:current_turn) { turn } end - it "adds an after attackers declared step trigger" do + it "registers an until-end-of-turn trigger for attackers declared" do p1.activate_loyalty_ability(ability: ability) game.stack.resolve! game.tick! expect(subject.loyalty).to eq(1) - expect(subject.delayed_responses.count).to eq(1) - expect(subject.delayed_responses.first[:event_type]).to eq(Magic::Events::PreliminaryAttackersDeclared) end end diff --git a/spec/cards/sanctum_of_fruitful_harvest_spec.rb b/spec/cards/sanctum_of_fruitful_harvest_spec.rb new file mode 100644 index 0000000..9b0613e --- /dev/null +++ b/spec/cards/sanctum_of_fruitful_harvest_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Magic::Cards::SanctumOfFruitfulHarvest do + include_context "two player game" + + let!(:sanctum_of_tranquil_light) { ResolvePermanent("Sanctum Of Tranquil Light") } + let!(:sanctum_of_fruitful_harvest) { ResolvePermanent("Sanctum Of Fruitful Harvest") } + + context "at beginning of precombat main phase" do + it "adds mana equal to the number of shrines in the chosen color" do + go_to_main_phase! + + choice = game.choices.first + expect(choice).to be_a(described_class::ColorChoice) + game.resolve_choice!(color: :green) + + expect(p1.mana_pool[:green]).to eq(2) + end + end +end