Skip to content
Open
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
3 changes: 1 addition & 2 deletions lib/magic/card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
25 changes: 12 additions & 13 deletions lib/magic/cards/basri_ket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -76,6 +74,7 @@ def loyalty_abilities
LoyaltyAbility3,
]
end

end
end
end
19 changes: 9 additions & 10 deletions lib/magic/cards/massacre_girl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions lib/magic/cards/sanctum_of_fruitful_harvest.rb
Original file line number Diff line number Diff line change
@@ -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
20 changes: 8 additions & 12 deletions lib/magic/permanent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class Permanent
:power,
:toughness,
:keywords,
:delayed_responses,
:attachments,
:protections,
:modifiers,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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!
Expand Down Expand Up @@ -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!
Expand Down
4 changes: 1 addition & 3 deletions spec/cards/basri_ket_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
22 changes: 22 additions & 0 deletions spec/cards/sanctum_of_fruitful_harvest_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Loading