diff --git a/lib/magic/action.rb b/lib/magic/action.rb index 4d0769b..36b6475 100644 --- a/lib/magic/action.rb +++ b/lib/magic/action.rb @@ -1,10 +1,10 @@ module Magic class Action - attr_reader :player, :game + attr_reader :game, :player - def initialize(player:) + def initialize(game:, player:) + @game = game @player = player - @game = player.game end end end diff --git a/lib/magic/actions/activate_ability.rb b/lib/magic/actions/activate_ability.rb index 22c89f1..3371703 100644 --- a/lib/magic/actions/activate_ability.rb +++ b/lib/magic/actions/activate_ability.rb @@ -1,11 +1,10 @@ module Magic module Actions class ActivateAbility < Action - attr_reader :permanent, :ability, :costs, :requirements, :targets + attr_reader :ability, :costs, :requirements, :targets - def initialize(permanent:, ability:, **args) - @permanent = permanent - @ability = ability.new(source: permanent) + def initialize(ability:, **args) + @ability = ability @costs = @ability.costs @requirements = @ability.requirements @targets = [] @@ -13,7 +12,7 @@ def initialize(permanent:, ability:, **args) end def inspect - "#" + "#" end def can_be_activated?(player) @@ -39,16 +38,20 @@ def targeting(*targets) end def pay_mana(payment) - pay(player, :mana, payment) + pay(:mana, payment) self end def pay_tap - pay(player, :tap) + pay(:tap) end def pay_multi_tap(targets) - pay(player, :multi_tap, targets) + pay(:multi_tap, targets) + end + + def pay_sacrifice(targets) + pay(:sacrifice, targets) end def perform @@ -63,7 +66,7 @@ def perform end end - def pay(player, cost_type, payment = nil) + def pay(cost_type, payment = nil) cost_type = case cost_type when :mana Costs::Mana @@ -85,6 +88,10 @@ def pay(player, cost_type, payment = nil) self end + def has_cost?(cost_type) + costs.any? { |cost| cost.is_a?(cost_type) } + end + def finalize_costs!(player) costs.each { |cost| cost.finalize!(player) } end diff --git a/lib/magic/actions/activate_loyalty_ability.rb b/lib/magic/actions/activate_loyalty_ability.rb index 47cc88f..f25d55f 100644 --- a/lib/magic/actions/activate_loyalty_ability.rb +++ b/lib/magic/actions/activate_loyalty_ability.rb @@ -3,9 +3,9 @@ module Actions class ActivateLoyaltyAbility < Action attr_reader :ability, :planeswalker, :targets, :x_value - def initialize(planeswalker:, ability:, **args) - @planeswalker = planeswalker - @ability = ability.new(planeswalker: planeswalker) + def initialize(ability:, **args) + @planeswalker = ability.planeswalker + @ability = ability @targets = [] super(**args) end diff --git a/lib/magic/actions/cast.rb b/lib/magic/actions/cast.rb index 1b65989..5644fc1 100644 --- a/lib/magic/actions/cast.rb +++ b/lib/magic/actions/cast.rb @@ -3,6 +3,8 @@ module Actions class Cast < Action extend Forwardable + class InvalidTarget < StandardError; end + def_delegators :@card, :enchantment?, :artifact? attr_reader :card, :targets def initialize(card:, **args) @@ -47,6 +49,10 @@ def mana_cost end end + def auto_pay + mana_cost.auto_pay(player) + end + def kicker_cost card.kicker_cost end @@ -69,7 +75,7 @@ def can_target?(target) def targeting(*targets) targets.each do |target| - raise "Invalid target for #{card.name}: #{target}" unless can_target?(target) + raise InvalidTarget, "Invalid target for #{card.name}: #{target}" unless can_target?(target) end @targets = targets self diff --git a/lib/magic/actions/play_land.rb b/lib/magic/actions/play_land.rb index def1bc1..1e6cde6 100644 --- a/lib/magic/actions/play_land.rb +++ b/lib/magic/actions/play_land.rb @@ -13,8 +13,7 @@ def inspect end def can_perform? - return false if player.can_play_lands? - true + player.can_play_lands? end def perform diff --git a/lib/magic/cards/animal_sanctuary.rb b/lib/magic/cards/animal_sanctuary.rb index e8bdd15..1cd255b 100644 --- a/lib/magic/cards/animal_sanctuary.rb +++ b/lib/magic/cards/animal_sanctuary.rb @@ -12,14 +12,14 @@ def costs end def resolve! - controller.add_mana(colorless: 1) + controller.add_mana(generic: 1) end end class ActivatedAbility < Magic::ActivatedAbility def costs [ - Costs::Mana.new(colorless: 2), + Costs::Mana.new(generic: 2), Costs::Tap.new(source), ] end diff --git a/lib/magic/cards/celestial_enforcer.rb b/lib/magic/cards/celestial_enforcer.rb index 4de5265..9235c28 100644 --- a/lib/magic/cards/celestial_enforcer.rb +++ b/lib/magic/cards/celestial_enforcer.rb @@ -36,6 +36,8 @@ def resolve!(target:) target.tap! end end + + def activated_abilities = [ActivatedAbility] end end end diff --git a/lib/magic/cards/fabled_passage.rb b/lib/magic/cards/fabled_passage.rb index ce670be..5dfa145 100644 --- a/lib/magic/cards/fabled_passage.rb +++ b/lib/magic/cards/fabled_passage.rb @@ -2,7 +2,6 @@ module Magic module Cards class FabledPassage < Land NAME = "Fabled Passage" - TYPE_LINE = "Land" def target_choices(receiver) receiver.controller.library.basic_lands @@ -30,9 +29,7 @@ def resolve! end end - def activated_abilities - [ActivatedAbility] - end + def activated_abilities = [ActivatedAbility] end end end diff --git a/lib/magic/cards/faiths_fetters.rb b/lib/magic/cards/faiths_fetters.rb index 6fc1de2..38334d2 100644 --- a/lib/magic/cards/faiths_fetters.rb +++ b/lib/magic/cards/faiths_fetters.rb @@ -23,7 +23,7 @@ def can_block? end def can_activate_ability?(ability) - ability < ManaAbility + ability.is_a?(ManaAbility) end end end diff --git a/lib/magic/cards/idol_of_endurance.rb b/lib/magic/cards/idol_of_endurance.rb index 789c87b..34305b3 100644 --- a/lib/magic/cards/idol_of_endurance.rb +++ b/lib/magic/cards/idol_of_endurance.rb @@ -40,9 +40,9 @@ def target_choices def resolve!(target:) source.remove_from_exile(target) - cast_action = Magic::Actions::Cast.new(card: target, player: source.controller) - cast_action.mana_cost = 0 - game.take_action(cast_action) + source.controller.cast(card: target) do + _1.mana_cost = 0 + end end end diff --git a/lib/magic/cards/land.rb b/lib/magic/cards/land.rb index a4fc788..6b35b33 100644 --- a/lib/magic/cards/land.rb +++ b/lib/magic/cards/land.rb @@ -1,6 +1,8 @@ module Magic module Cards class Land < Card + type "Land" + def play! move_zone!(game.battlefield) end diff --git a/lib/magic/cards/shock.rb b/lib/magic/cards/shock.rb index 33fee21..d59e758 100644 --- a/lib/magic/cards/shock.rb +++ b/lib/magic/cards/shock.rb @@ -12,10 +12,8 @@ def single_target? end def resolve!(_controller, target:) - game.add_effect(Effects::DealDamage.new(source: self, targets: [target], damage: 2)) + game.add_effect(Effects::DealDamage.new(source: self, targets: [target], damage: 2)) end end - - end end diff --git a/lib/magic/costs/mana.rb b/lib/magic/costs/mana.rb index 58d2f77..9b76e71 100644 --- a/lib/magic/costs/mana.rb +++ b/lib/magic/costs/mana.rb @@ -54,13 +54,19 @@ def can_pay?(player) end def pay(player, payment) - raise CannotPay unless can_pay?(player) pay_generic(payment[:generic]) if payment[:generic] pay_colors(payment.slice(*Magic::Mana::COLORS)) end + def auto_pay(player) + raise CannotPay unless can_pay?(player) + + pay_colors(color_costs) + pay_generic(generic: cost[:generic]) if cost[:generic] + end + def finalize!(player) raise OutstandingBalance if outstanding_balance? raise Overpayment if overpaid? diff --git a/lib/magic/effects/single_target.rb b/lib/magic/effects/single_target.rb new file mode 100644 index 0000000..e49de2d --- /dev/null +++ b/lib/magic/effects/single_target.rb @@ -0,0 +1,27 @@ +module Magic + module Effects + class SingleTarget < Effect + class InvalidTarget < StandardError; end; + + attr_reader :source, :target, :choices + + def initialize(source:, target:, choices: source.target_choices) + @target = target + @source = source + @choices = [*choices] + end + + def requires_choices? + true + end + + def single_choice? + choices.count == 1 + end + + def no_choice? + choices.count.zero? + end + end + end +end diff --git a/lib/magic/effects/targeted_effect.rb b/lib/magic/effects/targeted_effect.rb index f20a89f..1b5d392 100644 --- a/lib/magic/effects/targeted_effect.rb +++ b/lib/magic/effects/targeted_effect.rb @@ -21,10 +21,6 @@ def choices end end - def requires_targets? - true - end - def requires_choices? true end diff --git a/lib/magic/game.rb b/lib/magic/game.rb index d5afd1b..50f6085 100644 --- a/lib/magic/game.rb +++ b/lib/magic/game.rb @@ -40,7 +40,7 @@ def initialize( def add_players(*players) players.each(&method(:add_player)) end - +\ def add_player(player) @player_count += 1 @players << player diff --git a/lib/magic/permanent.rb b/lib/magic/permanent.rb index 275f4e4..6777d21 100644 --- a/lib/magic/permanent.rb +++ b/lib/magic/permanent.rb @@ -4,7 +4,7 @@ class Permanent include Types extend Forwardable - attr_reader :game, :owner, :controller, :card,:types, :delayed_responses, :attachments, :protections, :modifiers, :counters, :keywords, :keyword_grants, :activated_abilities, :exiled_cards, :cannot_untap_next_turn + attr_reader :game, :owner, :controller, :card, :types, :delayed_responses, :attachments, :protections, :modifiers, :counters, :keywords, :keyword_grants, :activated_abilities, :exiled_cards, :cannot_untap_next_turn def_delegators :@card, :name, :cmc, :mana_value, :colors, :colorless? def_delegators :@game, :logger @@ -49,7 +49,6 @@ def initialize(game:, owner:, card:, token: false, cast: true, kicked: false) @keywords = card.keywords @keyword_grants = [] @counters = Counters::Collection.new([]) - @activated_abilities = card.activated_abilities @damage = 0 @protections = Protections.new(card.protections.dup) @exiled_cards = Magic::CardList.new([]) @@ -67,6 +66,10 @@ def inspect "#" end + def activated_abilities + @activated_abilities ||= card.activated_abilities.map { |ability| ability.new(source: self) } + end + alias_method :to_s, :inspect def controller?(other_controller) diff --git a/lib/magic/permanents/planeswalker.rb b/lib/magic/permanents/planeswalker.rb index 5722f5e..0584332 100644 --- a/lib/magic/permanents/planeswalker.rb +++ b/lib/magic/permanents/planeswalker.rb @@ -12,7 +12,7 @@ def change_loyalty!(change) @loyalty += change destroy! if loyalty <= 0 end - + def take_damage(source:, damage:) game.notify!( Events::DamageDealt.new( @@ -24,9 +24,8 @@ def take_damage(source:, damage:) change_loyalty!(- damage) end - def loyalty_abilities - card.loyalty_abilities + card.loyalty_abilities.map { |ability| ability.new(planeswalker: self) } end end end diff --git a/lib/magic/player.rb b/lib/magic/player.rb index d978d91..ec7078f 100644 --- a/lib/magic/player.rb +++ b/lib/magic/player.rb @@ -35,6 +35,64 @@ def inspect end alias_method :to_s, :inspect + def prepare_action(action, **args, &block) + action = action.new(player: self, game: game, **args) + yield action if block_given? + action + end + + def take_action(action, **args) + if action.is_a?(Class) + action = prepare_action(action, **args) + end + + game.take_action(action) + end + + def play_land(land:, **args) + action = prepare_action(Magic::Actions::PlayLand, card: land, **args) + game.take_action(action) + end + + def prepare_activate_ability(ability:, **args, &block) + action = prepare_action(Magic::Actions::ActivateAbility, ability: ability, **args) + yield action if block_given? + action + end + + def activate_ability(ability:, auto_tap: true, **args, &block) + action = prepare_activate_ability(ability: ability, **args, &block) + action.pay_tap if action.has_cost?(Magic::Costs::Tap) && auto_tap + action.finalize_costs!(self) + game.take_action(action) + end + + def activate_loyalty_ability(ability:, auto_tap: true, **args) + action = prepare_action(Magic::Actions::ActivateLoyaltyAbility, ability: ability, **args) + yield action if block_given? + game.take_action(action) + end + + def prepare_cast(card:, **args) + action = prepare_action(Magic::Actions::Cast, card: card, **args) + yield action if block_given? + action + end + + def cast(card:, **args, &block) + action = prepare_cast(card: card, **args, &block) + game.take_action(action) + action + end + + def declare_attacker(attacker:, target:, **args) + action = prepare_action(Magic::Actions::DeclareAttacker, attacker: attacker, target: target, **args) + game.take_action(action) + action + end + + + def lost? @lost end @@ -74,7 +132,7 @@ def max_lands_per_turn end def can_play_lands? - lands_played >= max_lands_per_turn + lands_played < max_lands_per_turn end def can_be_targeted_by?(source) diff --git a/lib/magic/stack.rb b/lib/magic/stack.rb index 942d714..0c522e3 100644 --- a/lib/magic/stack.rb +++ b/lib/magic/stack.rb @@ -131,6 +131,7 @@ def unresolved_effects def resolve_effects! resolvable_effects_with_targets = effects.unresolved.targeted resolvable_effects_with_targets.each do |effect| + effects.delete(effect) effect.targets.each do |target| effect.resolve(target) end diff --git a/spec/cards/academy_elite_spec.rb b/spec/cards/academy_elite_spec.rb index 5946397..09e2584 100644 --- a/spec/cards/academy_elite_spec.rb +++ b/spec/cards/academy_elite_spec.rb @@ -17,9 +17,9 @@ context "when it enters the battlefield" do it "gets 1 +1/+1 counter" do p1.add_mana(blue: 4) - action = Magic::Actions::Cast.new(player: p1, card: academy_elite) - action.pay_mana(generic: { blue: 3 }, blue: 1) - game.take_action(action) + p1.cast(card: academy_elite) do + _1.pay_mana(generic: { blue: 3 }, blue: 1) + end game.tick! permanent = p1.permanents.last @@ -39,9 +39,9 @@ context "when it enters the battlefield" do it "gets 2 +1/+1 counters" do p1.add_mana(blue: 4) - action = Magic::Actions::Cast.new(player: p1, card: academy_elite) - action.pay_mana(generic: { blue: 3 }, blue: 1) - game.take_action(action) + p1.cast(card: academy_elite) do + _1.pay_mana(generic: { blue: 3 }, blue: 1) + end game.tick! permanent = p1.permanents.last diff --git a/spec/cards/altars_light_spec.rb b/spec/cards/altars_light_spec.rb index 1e1421d..57de45c 100644 --- a/spec/cards/altars_light_spec.rb +++ b/spec/cards/altars_light_spec.rb @@ -12,10 +12,10 @@ end it "exiles the sol ring" do - action = cast_action(card: card, player: p1) - action.targeting(sol_ring) - add_to_stack_and_resolve(action) - + p1.add_mana(white: 4) + cast_and_resolve(card: card, player: p1) do + _1.targeting(sol_ring) + end expect(sol_ring.card.zone).to be_exile end end diff --git a/spec/cards/angelic_ascension_spec.rb b/spec/cards/angelic_ascension_spec.rb index 52a85f4..954682a 100644 --- a/spec/cards/angelic_ascension_spec.rb +++ b/spec/cards/angelic_ascension_spec.rb @@ -7,8 +7,9 @@ subject { add_to_library("Angelic Ascension", player: p1) } it "exiles the wood elves and creates a 4/4 white angel creature token with flying" do - action = cast_action(card: subject, player: p1, targeting: wood_elves) - add_to_stack_and_resolve(action) + action = cast_and_resolve(card: subject, player: p1) do |action| + action.targeting(wood_elves) + end expect(wood_elves.card.zone).to be_exile expect(wood_elves.zone).to be_nil diff --git a/spec/cards/animal_sanctuary_spec.rb b/spec/cards/animal_sanctuary_spec.rb index d602ad1..e7ce9f2 100644 --- a/spec/cards/animal_sanctuary_spec.rb +++ b/spec/cards/animal_sanctuary_spec.rb @@ -8,13 +8,12 @@ context "mana ability" do def activate_ability - action = Magic::Actions::ActivateAbility.new(permanent: subject, ability: subject.activated_abilities.first, player: p1) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) end it "adds one colorless mana" do activate_ability - expect(p1.mana_pool[:colorless]).to eq(1) + expect(p1.mana_pool[:generic]).to eq(1) end end @@ -26,9 +25,9 @@ def activate_ability def activate_ability p1.add_mana(green: 2) - action = Magic::Actions::ActivateAbility.new(permanent: subject, ability: subject.activated_abilities.last, player: p1) - action.pay_mana(colorless: { green: 2 }) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.last) do + _1.pay_mana(generic: { green: 2 }) + end game.stack.resolve! end diff --git a/spec/cards/annul_spec.rb b/spec/cards/annul_spec.rb index d809112..53295e7 100644 --- a/spec/cards/annul_spec.rb +++ b/spec/cards/annul_spec.rb @@ -14,15 +14,15 @@ context "counters a Sol Ring" do it "sol ring never enters the battlefield" do p2.add_mana(red: 1) - action = Magic::Actions::Cast.new(player: p2, card: sol_ring) - action.pay_mana(generic: { red: 1 }) - game.take_action(action) + action = p2.cast(card: sol_ring) do + _1.pay_mana(generic: { red: 1 }) + end p1.add_mana(blue: 1) - action_2 = Magic::Actions::Cast.new(player: p1, card: annul) - action_2.targeting(action) - action_2.pay_mana(blue: 1) - game.take_action(action_2) + p1.cast(card: annul) do + _1.pay_mana(blue: 1) + _1.targeting(action) + end game.stack.resolve! expect(annul.zone).to be_graveyard @@ -31,12 +31,14 @@ it "cannot target a thing that is not an artifact or enchantment" do p2.add_mana(green: 3) - action = Magic::Actions::Cast.new(player: p2, card: Card("Wood Elves")) - action.pay_mana(generic: { green: 2 }, green: 1) + wood_elves_cast = p2.cast(card: Card("Wood Elves")) do + _1.pay_mana(generic: { green: 2 }, green: 1) + end p1.add_mana(blue: 1) - action_2 = Magic::Actions::Cast.new(player: p2, card: annul) - expect(action_2.can_target?(action)).to eq(false) + p1.prepare_action(Magic::Actions::Cast, card: annul) do |action| + expect { action.targeting(wood_elves_cast) }.to raise_error(Magic::Actions::Cast::InvalidTarget) + end end end end diff --git a/spec/cards/anointed_chorister_spec.rb b/spec/cards/anointed_chorister_spec.rb index c00579a..a1d8c90 100644 --- a/spec/cards/anointed_chorister_spec.rb +++ b/spec/cards/anointed_chorister_spec.rb @@ -14,10 +14,10 @@ it "applies a buff of +3/+3" do p1.add_mana(white: 5) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: permanent, ability: permanent.activated_abilities.first) - action.pay_mana({ generic: { white: 4 }, white: 1 }) - action.finalize_costs!(p1) - game.take_action(action) + p1.activate_ability(ability: permanent.activated_abilities.first) do + _1.pay_mana({ generic: { white: 4 }, white: 1 }) + end + game.tick! expect(p1.mana_pool[:white]).to eq(0) diff --git a/spec/cards/aron_benalias_ruin_spec.rb b/spec/cards/aron_benalias_ruin_spec.rb index fd0e7ac..ba085e5 100644 --- a/spec/cards/aron_benalias_ruin_spec.rb +++ b/spec/cards/aron_benalias_ruin_spec.rb @@ -15,10 +15,11 @@ it "puts a +1/+1 counter on all creatures under p1's control" do expect(subject.activated_abilities.count).to eq(1) p1.add_mana(white: 1, black: 1) - action = Magic::Actions::ActivateAbility.new(permanent: subject, ability: subject.activated_abilities.first, player: p1) - action.pay_mana(white: 1, black: 1) - action.pay(p1, :sacrifice, wood_elves) - game.take_action(action) + + p1.activate_ability(ability: subject.activated_abilities.first) do + _1.pay_mana(white: 1, black: 1) + _1.pay_sacrifice(wood_elves) + end game.stack.resolve! aggregate_failures do diff --git a/spec/cards/basri_ket_spec.rb b/spec/cards/basri_ket_spec.rb index d60eb3c..f18cc77 100644 --- a/spec/cards/basri_ket_spec.rb +++ b/spec/cards/basri_ket_spec.rb @@ -16,9 +16,9 @@ end it "targets the wood elves" do - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: planeswalker, ability: ability) - action.targeting(wood_elves) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) do + _1.targeting(wood_elves) + end game.tick! expect(planeswalker.loyalty).to eq(4) @@ -37,8 +37,7 @@ end it "adds an after attackers declared step trigger" do - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: planeswalker, ability: ability) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) game.tick! expect(subject.loyalty).to eq(1) @@ -51,8 +50,7 @@ let(:ability) { planeswalker.loyalty_abilities[2] } it "emblem for creating white soldier creature tokens and putting counters on all creatures" do - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: planeswalker, ability: ability) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) game.tick! expect(game.emblems.count).to eq(1) diff --git a/spec/cards/burn_bright_spec.rb b/spec/cards/burn_bright_spec.rb index 6e99da9..9005224 100644 --- a/spec/cards/burn_bright_spec.rb +++ b/spec/cards/burn_bright_spec.rb @@ -12,9 +12,10 @@ it "grants 2 power to both creatures " do p1.add_mana(red: 3) - action = Magic::Actions::Cast.new(player: p1, card: burn_bright) - .pay_mana(generic: { red: 2 }, red: 1) - game.take_action(action) + p1.cast(card: burn_bright) do + _1.pay_mana(red: 1, generic: { red: 2 }) + end + game.tick! expect(onakke_ogre.power).to eq(6) @@ -22,12 +23,21 @@ expect(blood_glutton.power).to eq(6) expect(blood_glutton.toughness).to eq(3) - game.current_turn.end! - game.current_turn.cleanup! - game.next_turn - - expect(onakke_ogre.power).to eq(4) - expect(blood_glutton.power).to eq(4) + expect(onakke_ogre.modifiers).to include( + an_object_having_attributes( + power: 2, + toughness: 0, + until_eot: true, + ) + ) + + expect(blood_glutton.modifiers).to include( + an_object_having_attributes( + power: 2, + toughness: 0, + until_eot: true, + ) + ) end end @@ -36,11 +46,11 @@ let!(:onakke_ogre) { ResolvePermanent("Onakke Ogre", owner: p1) } let!(:cloudkin_seer) { ResolvePermanent("Cloudkin Seer", owner: p2) } - it "does not buff opponent's creature " do + it "does not buff opponent's creature" do p1.add_mana(red: 3) - action = Magic::Actions::Cast.new(player: p1, card: burn_bright) - .pay_mana(generic: { red: 2 }, red: 1) - game.take_action(action) + p1.cast(card: burn_bright) do + _1.pay_mana(red: 1, generic: { red: 2 }) + end game.tick! expect(cloudkin_seer.power).to eq(2) diff --git a/spec/cards/cancel_spec.rb b/spec/cards/cancel_spec.rb index 037f7be..607f69e 100644 --- a/spec/cards/cancel_spec.rb +++ b/spec/cards/cancel_spec.rb @@ -14,15 +14,15 @@ context "counters a Sol Ring" do it "sol ring never enters the battlefield" do p2.add_mana(red: 1) - action = Magic::Actions::Cast.new(player: p2, card: sol_ring) - action.pay_mana(generic: { red: 1 }) - game.take_action(action) + action = p2.cast(card: sol_ring) do + _1.pay_mana(generic: { red: 1 }) + end p1.add_mana(blue: 3) - action_2 = Magic::Actions::Cast.new(player: p1, card: cancel) - action_2.targeting(action) - action_2.pay_mana(blue: 2, generic: { blue: 1 }) - game.take_action(action_2) + action_2 = p1.cast(card: cancel) do + _1.pay_mana(blue: 2, generic: { blue: 1 }) + _1.targeting(action) + end game.stack.resolve! expect(cancel.zone).to be_graveyard diff --git a/spec/cards/celestial_enforcer_spec.rb b/spec/cards/celestial_enforcer_spec.rb index 1ed5143..3958005 100644 --- a/spec/cards/celestial_enforcer_spec.rb +++ b/spec/cards/celestial_enforcer_spec.rb @@ -8,7 +8,7 @@ context "activated ability" do def ability - permanent.card.class::ActivatedAbility + permanent.activated_abilities.first end context "when p1 has two white mana" do @@ -24,11 +24,12 @@ def ability it "taps a target creature" do p1.add_mana(white: 2) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: permanent, ability: ability) - action.pay_mana(generic: { white: 1 }, white: 1) - action.pay_tap - action.targeting(wood_elves) - game.take_action(action) + p1.activate_ability(ability: ability) do + _1 + .targeting(wood_elves) + .pay_mana(generic: { white: 1 }, white: 1) + .pay_tap + end game.stack.resolve! expect(permanent).to be_tapped expect(wood_elves).to be_tapped @@ -37,7 +38,7 @@ def ability context "when p1 does not control any creatures with flying" do it "cannot be activated" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: permanent, ability: ability) + action = p1.prepare_activate_ability(ability: ability) expect(action.can_be_activated?(p1)).to eq(false) end end @@ -47,7 +48,7 @@ def ability before { permanent.tap! } it "cannot be activated" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: permanent, ability: ability) + action = p1.prepare_activate_ability(ability: ability) expect(action.can_be_activated?(p1)).to eq(false) end end diff --git a/spec/cards/containment_priest_spec.rb b/spec/cards/containment_priest_spec.rb index 9efa1b4..f3a92a0 100644 --- a/spec/cards/containment_priest_spec.rb +++ b/spec/cards/containment_priest_spec.rb @@ -42,9 +42,9 @@ story_seeker.zone = p2.hand p2.add_mana(white: 2) - action = Magic::Actions::Cast.new(player: p2, card: story_seeker) - action.pay_mana(generic: { white: 1 }, white: 1) - game.take_action(action) + p2.cast(card: story_seeker) do + _1.pay_mana(generic: { white: 1 }, white: 1) + end game.tick! expect(game.battlefield.permanents.count).to eq(3) @@ -56,16 +56,19 @@ it "exiles the creature" do p2.graveyard << Card('Story Seeker') p2.add_mana(black: 5) - action = Magic::Actions::Cast.new(player: p2, card: Card("Rise Again")) - action.pay_mana(generic: { black: 4 }, black: 1) - .targeting(p2.graveyard.cards.first) - game.take_action(action) + p2.cast(card: Card("Rise Again")) do + _1 + .pay_mana(generic: { black: 4 }, black: 1) + .targeting(p2.graveyard.cards.first) + end p2.add_mana(red: 1) - action = Magic::Actions::Cast.new(player: p2, card: Card("Shock")) - .pay_mana(red: 1) - .targeting(wood_elves) - game.take_action(action) + p2.cast(card: Card("Shock")) do + _1 + .pay_mana(red: 1) + .targeting(wood_elves) + end + expect{ game.tick! }.to change { game.exile.cards.count }.by(1) diff --git a/spec/cards/fabled_passage_spec.rb b/spec/cards/fabled_passage_spec.rb index 08468fb..bc2b995 100644 --- a/spec/cards/fabled_passage_spec.rb +++ b/spec/cards/fabled_passage_spec.rb @@ -10,9 +10,9 @@ def p1_library end def activate_ability - action = Magic::Actions::ActivateAbility.new(permanent: subject, ability: subject.activated_abilities.first, player: p1) - action.pay(p1, :sacrifice) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) do + _1.pay(:sacrifice) + end game.stack.resolve! effect = game.effects.first expect(effect).to be_a(Magic::Cards::FabledPassage::Effect) diff --git a/spec/cards/font_of_fertility_spec.rb b/spec/cards/font_of_fertility_spec.rb index 9ad65f5..bf6025c 100644 --- a/spec/cards/font_of_fertility_spec.rb +++ b/spec/cards/font_of_fertility_spec.rb @@ -13,10 +13,12 @@ def p1_library it "searches for a basic land, puts it on the battlefield tapped" do expect(subject.activated_abilities.count).to eq(1) p1.add_mana(green: 2) - action = Magic::Actions::ActivateAbility.new(permanent: subject, ability: subject.activated_abilities.first, player: p1) - action.pay_mana(green: 1) - action.pay(p1, :sacrifice) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) do + _1 + .pay_mana(green: 1) + .pay(:sacrifice) + end + game.stack.resolve! effect = game.effects.first expect(effect).to be_a(Magic::Effects::SearchLibraryForBasicLand) diff --git a/spec/cards/goblin_wizardry_spec.rb b/spec/cards/goblin_wizardry_spec.rb index b56fa84..4314e64 100644 --- a/spec/cards/goblin_wizardry_spec.rb +++ b/spec/cards/goblin_wizardry_spec.rb @@ -8,9 +8,9 @@ context "casting Goblin Wizardry" do before do p1.add_mana(red: 4) - action = Magic::Actions::Cast.new(player: p1, card: goblin_wizardry) - .pay_mana(generic: { red: 3 }, red: 1) - game.take_action(action) + p1.cast(card: goblin_wizardry) do + _1.pay_mana(generic: { red: 3 }, red: 1) + end game.tick! end diff --git a/spec/cards/golgari_guildgate_spec.rb b/spec/cards/golgari_guildgate_spec.rb index a8f847b..d96e1fb 100644 --- a/spec/cards/golgari_guildgate_spec.rb +++ b/spec/cards/golgari_guildgate_spec.rb @@ -6,8 +6,7 @@ let(:card) { described_class.new(game: game) } let!(:permanent) do - action = Magic::Actions::PlayLand.new(player: p1, card: card) - game.take_action(action) + p1.play_land(land: card) p1.permanents.by_name("Golgari Guildgate").first end @@ -17,9 +16,9 @@ end it "taps for either black or green" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: permanent, ability: card.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: permanent.activated_abilities.first) do + _1.pay_tap + end game.resolve_pending_effect(black: 1) expect(game.effects).to be_empty expect(p1.mana_pool[:black]).to eq(1) diff --git a/spec/cards/great_furnace_spec.rb b/spec/cards/great_furnace_spec.rb index 5da19f2..f273dfd 100644 --- a/spec/cards/great_furnace_spec.rb +++ b/spec/cards/great_furnace_spec.rb @@ -7,9 +7,8 @@ it "taps for red mana" do expect(p1).to receive(:add_mana).with(red: 1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - action.pay_tap - game.take_action(action) + + p1.activate_ability(ability: subject.activated_abilities.first) expect(subject).to be_tapped end diff --git a/spec/cards/hellkite_punisher_spec.rb b/spec/cards/hellkite_punisher_spec.rb index 0d45a4a..bd2174d 100644 --- a/spec/cards/hellkite_punisher_spec.rb +++ b/spec/cards/hellkite_punisher_spec.rb @@ -14,15 +14,15 @@ p1.add_mana(red: 2) ability = subject.activated_abilities.first - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: ability) - action.pay_mana(red: 1) - game.take_action(action) + p1.activate_ability(ability: ability) do + _1.pay_mana(red: 1) + end game.stack.resolve! expect(subject.power).to eq(7) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: ability) - action.pay_mana(red: 1) - game.take_action(action) + p1.activate_ability(ability: ability) do + _1.pay_mana(red: 1) + end game.stack.resolve! expect(subject.power).to eq(8) diff --git a/spec/cards/idol_of_endurance_spec.rb b/spec/cards/idol_of_endurance_spec.rb index 0c483eb..2d0ff43 100644 --- a/spec/cards/idol_of_endurance_spec.rb +++ b/spec/cards/idol_of_endurance_spec.rb @@ -26,14 +26,12 @@ end it "can play that card without incurring mana cost" do - p1.add_mana(white: 2) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: idol_of_endurance, ability: idol_of_endurance.activated_abilities.first) - action.pay_mana({ generic: { white: 1 }, white: 1 }) - action.pay_tap - action.finalize_costs!(p1) - action.targeting(wood_elves) - game.take_action(action) + p1.activate_ability(ability: idol_of_endurance.activated_abilities.first) do + _1 + .targeting(wood_elves) + .pay_mana(generic: { white: 1 }, white: 1) + end game.tick! expect(idol_of_endurance).to be_tapped diff --git a/spec/cards/island_spec.rb b/spec/cards/island_spec.rb index e83cefe..86d1ff1 100644 --- a/spec/cards/island_spec.rb +++ b/spec/cards/island_spec.rb @@ -7,8 +7,7 @@ it "taps for a single blue mana" do expect(p1).to receive(:add_mana).with(blue: 1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) + expect(subject).to be_tapped end end diff --git a/spec/cards/jungle_hollow_spec.rb b/spec/cards/jungle_hollow_spec.rb index 123e14e..9a34147 100644 --- a/spec/cards/jungle_hollow_spec.rb +++ b/spec/cards/jungle_hollow_spec.rb @@ -6,8 +6,7 @@ let(:card) { described_class.new(game: game) } let!(:permanent) do - action = Magic::Actions::PlayLand.new(player: p1, card: card) - game.take_action(action) + p1.play_land(land: card) p1.permanents.by_name("Jungle Hollow").first end @@ -21,9 +20,7 @@ end it "taps for either black or green" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: permanent, ability: card.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: permanent.activated_abilities.first) game.resolve_pending_effect(black: 1) expect(game.effects).to be_empty expect(p1.mana_pool[:black]).to eq(1) diff --git a/spec/cards/keen_glidemaster_spec.rb b/spec/cards/keen_glidemaster_spec.rb index fd70e4c..f8258b2 100644 --- a/spec/cards/keen_glidemaster_spec.rb +++ b/spec/cards/keen_glidemaster_spec.rb @@ -9,10 +9,11 @@ it "gives wood elves flying" do expect(subject.activated_abilities.count).to eq(1) p1.add_mana(blue: 3) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - .pay_mana(generic: { blue: 2 }, blue: 1) - .targeting(wood_elves) - action.perform + p1.activate_ability(ability: subject.activated_abilities.first) do + _1 + .pay_mana(generic: { blue: 2 }, blue: 1) + .targeting(wood_elves) + end expect(wood_elves).to be_flying expect(wood_elves.keyword_grants.first.until_eot?).to eq(true) end diff --git a/spec/cards/lathril_blade_of_the_elves_spec.rb b/spec/cards/lathril_blade_of_the_elves_spec.rb index b49c49e..dfb58c1 100644 --- a/spec/cards/lathril_blade_of_the_elves_spec.rb +++ b/spec/cards/lathril_blade_of_the_elves_spec.rb @@ -15,9 +15,9 @@ token.resolve!(p1) end - action = Magic::Actions::ActivateAbility.new(permanent: subject, ability: subject.activated_abilities.first, player: p1) - action.pay_multi_tap(elves) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) do + _1.pay_multi_tap(elves) + end game.stack.resolve! expect(p1.life).to eq(p1.starting_life + 10) @@ -42,4 +42,4 @@ expect(game.battlefield.creatures.controlled_by(p1).count).to eq(3) end end -end \ No newline at end of file +end diff --git a/spec/cards/llanowar_elves_spec.rb b/spec/cards/llanowar_elves_spec.rb index 60c0892..114803c 100644 --- a/spec/cards/llanowar_elves_spec.rb +++ b/spec/cards/llanowar_elves_spec.rb @@ -8,8 +8,7 @@ context "when on battlefield" do it "can be tapped for one green mana" do ability = subject.activated_abilities.first - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: ability) - game.take_action(action) + p1.activate_ability(ability: ability) expect(p1.mana_pool[:green]).to eq(1) end end diff --git a/spec/cards/makeshift_batallion_spec.rb b/spec/cards/makeshift_batallion_spec.rb index 91fc32b..2588f1a 100644 --- a/spec/cards/makeshift_batallion_spec.rb +++ b/spec/cards/makeshift_batallion_spec.rb @@ -16,11 +16,20 @@ it "gets a +1/+1 counter when both elves attack" do current_turn.declare_attackers! - action_1 = Magic::Actions::DeclareAttacker.new(player: p1, attacker: makeshift_batallion, target: p2) - action_2 = Magic::Actions::DeclareAttacker.new(player: p1, attacker: wood_elves_1, target: p2) - action_3 = Magic::Actions::DeclareAttacker.new(player: p1, attacker: wood_elves_2, target: p2) + p1.declare_attacker( + attacker: makeshift_batallion, + target: p2, + ) - game.take_actions(action_1, action_2, action_3) + p1.declare_attacker( + attacker: wood_elves_1, + target: p2, + ) + + p1.declare_attacker( + attacker: wood_elves_2, + target: p2, + ) current_turn.attackers_declared! @@ -31,13 +40,13 @@ it "gets no counters when only one elf attacks" do current_turn.declare_attackers! - current_turn.declare_attacker( - makeshift_batallion, + p1.declare_attacker( + attacker: makeshift_batallion, target: p2, ) - current_turn.declare_attacker( - wood_elves_1, + p1.declare_attacker( + attacker: wood_elves_1, target: p2, ) @@ -51,13 +60,13 @@ it "gets no counters when falconer adept attacks" do current_turn.declare_attackers! - current_turn.declare_attacker( - makeshift_batallion, + p1.declare_attacker( + attacker: makeshift_batallion, target: p2, ) - current_turn.declare_attacker( - falconer_adept, + p1.declare_attacker( + attacker: falconer_adept, target: p2, ) diff --git a/spec/cards/mangara_the_diplomat_spec.rb b/spec/cards/mangara_the_diplomat_spec.rb index 5700d7e..c70647c 100644 --- a/spec/cards/mangara_the_diplomat_spec.rb +++ b/spec/cards/mangara_the_diplomat_spec.rb @@ -57,13 +57,14 @@ it "p1 casts two wood elves, p2 draws" do expect(p2).to receive(:draw!) p1.add_mana({ green: 6 }) - action = Magic::Actions::Cast.new(player: p1, card: wood_elves_1) - action.pay_mana(generic: { green: 2 }, green: 1) + p1.cast(card: wood_elves_1) do + _1.pay_mana(generic: { green: 2 }, green: 1) + end - action_2 = Magic::Actions::Cast.new(player: p1, card: wood_elves_2) - action_2.pay_mana(generic: { green: 2 }, green: 1) + p1.cast(card: wood_elves_2) do + _1.pay_mana(generic: { green: 2 }, green: 1) + end - game.take_actions(action, action_2) game.tick! end end diff --git a/spec/cards/mountain_spec.rb b/spec/cards/mountain_spec.rb index 667dce8..6ecfa8e 100644 --- a/spec/cards/mountain_spec.rb +++ b/spec/cards/mountain_spec.rb @@ -6,9 +6,7 @@ it "taps for a single red mana" do expect(p1).to receive(:add_mana).with(red: 1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) expect(subject).to be_tapped end end diff --git a/spec/cards/plains_spec.rb b/spec/cards/plains_spec.rb index 9b1a736..9c5d6e3 100644 --- a/spec/cards/plains_spec.rb +++ b/spec/cards/plains_spec.rb @@ -6,9 +6,7 @@ it "taps for a single white mana" do expect(p1).to receive(:add_mana).with(white: 1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) expect(subject).to be_tapped end end diff --git a/spec/cards/sanctum_of_tranquil_light_spec.rb b/spec/cards/sanctum_of_tranquil_light_spec.rb index b93015d..ad26cdb 100644 --- a/spec/cards/sanctum_of_tranquil_light_spec.rb +++ b/spec/cards/sanctum_of_tranquil_light_spec.rb @@ -9,10 +9,11 @@ it "taps wood elves" do expect(subject.activated_abilities.count).to eq(1) p1.add_mana(white: 5) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - .pay_mana(generic: { white: 4 }, white: 1) - .targeting(wood_elves) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) do + _1 + .pay_mana(generic: { white: 4 }, white: 1) + .targeting(wood_elves) + end game.stack.resolve! expect(wood_elves).to be_tapped end diff --git a/spec/cards/scavenging_ooze_spec.rb b/spec/cards/scavenging_ooze_spec.rb index 2f659c6..2e58e08 100644 --- a/spec/cards/scavenging_ooze_spec.rb +++ b/spec/cards/scavenging_ooze_spec.rb @@ -20,10 +20,11 @@ def ability it "exiles a card from a graveyard, adds +1/1 counter and p1 gains life" do p1.add_mana(green: 1) starting_life = p1.life - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: scavenging_ooze, ability: ability) - action.pay_mana(green: 1) - action.targeting(wood_elves) - game.take_action(action) + p1.activate_ability(ability: ability) do + _1 + .pay_mana(green: 1) + .targeting(wood_elves) + end game.stack.resolve! aggregate_failures do @@ -47,10 +48,11 @@ def ability it "exiles sol ring, does not add counter and p1 does not gain life" do p1.add_mana(green: 1) starting_life = p1.life - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: scavenging_ooze, ability: ability) - action.pay_mana(green: 1) - action.targeting(sol_ring) - game.take_action(action) + p1.activate_ability(ability: ability) do + _1 + .pay_mana(green: 1) + .targeting(sol_ring) + end game.stack.resolve! aggregate_failures do diff --git a/spec/cards/scute_swarm_spec.rb b/spec/cards/scute_swarm_spec.rb index a664f85..d03e7de 100644 --- a/spec/cards/scute_swarm_spec.rb +++ b/spec/cards/scute_swarm_spec.rb @@ -13,8 +13,7 @@ context "when controller controls less than 6 lands" do it "creates an insect" do subject - action = Magic::Actions::PlayLand.new(player: p1, card: forest) - game.take_action(action) + p1.play_land(land: forest) game.tick! expect(game.battlefield.creatures.controlled_by(p1).by_name("Insect").count).to eq(1) @@ -28,8 +27,7 @@ it "creates a token copy of Scute Swarm" do subject - action = Magic::Actions::PlayLand.new(player: p1, card: forest) - game.take_action(action) + p1.play_land(land: forest) game.tick! scutes = game.battlefield.creatures.controlled_by(p1).by_name("Scute Swarm") @@ -47,8 +45,7 @@ it "creates a token copy of Scute Swarm" do subject ResolvePermanent("Scute Swarm", owner: p1) - action = Magic::Actions::PlayLand.new(player: p1, card: forest) - game.take_action(action) + p1.play_land(land: forest) game.tick! scutes = game.battlefield.creatures.controlled_by(p1).by_name("Scute Swarm") diff --git a/spec/cards/seasoned_hallowblade_spec.rb b/spec/cards/seasoned_hallowblade_spec.rb index 03153e0..9bafd4a 100644 --- a/spec/cards/seasoned_hallowblade_spec.rb +++ b/spec/cards/seasoned_hallowblade_spec.rb @@ -12,9 +12,9 @@ context "activated ability" do it "taps hallowblade, applies indestructible until eot" do expect(subject.activated_abilities.count).to eq(1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - .pay(p1, :discard, p1.hand.cards.first) - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) do + _1.pay(:discard, p1.hand.cards.first) + end expect(subject).to be_tapped expect(subject).to be_indestructible expect(subject.keyword_grants.count).to eq(1) diff --git a/spec/cards/secure_the_scene_spec.rb b/spec/cards/secure_the_scene_spec.rb index 8105f6c..71ce7ac 100644 --- a/spec/cards/secure_the_scene_spec.rb +++ b/spec/cards/secure_the_scene_spec.rb @@ -10,10 +10,10 @@ it "exiles the wood elves, replaces them with a 1/1 White Soldier" do p1.add_mana(white: 5) - action = Magic::Actions::Cast.new(player: p1, card: secure_the_scene) - .pay_mana(generic: { white: 4 }, white: 1) - .targeting(wood_elves) - game.take_action(action) + p1.cast(card: secure_the_scene) do + _1.pay_mana(generic: { white: 4 }, white: 1) + .targeting(wood_elves) + end game.tick! expect(wood_elves.card.zone).to be_exile diff --git a/spec/cards/selfless_savior_spec.rb b/spec/cards/selfless_savior_spec.rb index 87b7f0f..bd1019f 100644 --- a/spec/cards/selfless_savior_spec.rb +++ b/spec/cards/selfless_savior_spec.rb @@ -12,10 +12,9 @@ def ability end it "sacrifices selfless, applies indestructible to elves until eot" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - .pay(p1, :sacrifice, subject) - .targeting(wood_elves) - game.take_action(action) + p1.activate_ability(ability: ability) do + _1.pay(:sacrifice, subject).targeting(wood_elves) + end expect(wood_elves).to be_indestructible expect(wood_elves.keyword_grants.first.until_eot?).to eq(true) end diff --git a/spec/cards/shock_spec.rb b/spec/cards/shock_spec.rb index a66aed5..9b8a4f4 100644 --- a/spec/cards/shock_spec.rb +++ b/spec/cards/shock_spec.rb @@ -9,10 +9,9 @@ it "destroys the Cloudkin Seer" do p1.add_mana(red: 1) - action = Magic::Actions::Cast.new(player: p1, card: shock) - .pay_mana(red: 1) - .targeting(cloudkin_seer) - game.take_action(action) + p1.cast(card: shock) do + _1.pay_mana(red: 1).targeting(cloudkin_seer) + end game.tick! expect(cloudkin_seer).to be_dead end @@ -20,21 +19,19 @@ it "targets the opponent" do p2_life_total = p2.life p1.add_mana(red: 1) - action = Magic::Actions::Cast.new(player: p1, card: shock) - .pay_mana(red: 1) - .targeting(p2) - game.take_action(action) + p1.cast(card: shock) do + _1.pay_mana(red: 1).targeting(p2) + end game.tick! expect(p2.life).to eq(p2_life_total - 2) end it "removes planeswalker loyalty" do p1.add_mana(red: 1) - action = Magic::Actions::Cast.new(player: p1, card: shock) - .pay_mana(red: 1) - .targeting(basri_ket) - game.take_action(action) + p1.cast(card: shock) do + _1.pay_mana(red: 1).targeting(basri_ket) + end game.tick! expect(basri_ket.loyalty).to eq(1) - end + end end diff --git a/spec/cards/siege_striker_spec.rb b/spec/cards/siege_striker_spec.rb index d87aed0..7befdcd 100644 --- a/spec/cards/siege_striker_spec.rb +++ b/spec/cards/siege_striker_spec.rb @@ -22,7 +22,7 @@ expect(siege_striker.power).to eq(1) expect(siege_striker.toughness).to eq(1) - action = Magic::Actions::TapPermanent.new(player: p1, permanent: wood_elves) + action = Magic::Actions::TapPermanent.new(game: game, player: p1, permanent: wood_elves) game.take_action(action) expect(siege_striker.power).to eq(2) @@ -33,7 +33,7 @@ expect(siege_striker.power).to eq(1) expect(siege_striker.toughness).to eq(1) - action = Magic::Actions::TapPermanent.new(player: p1, permanent: wood_elves) + action = Magic::Actions::TapPermanent.new(game: game, player: p1, permanent: wood_elves) game.take_action(action) expect(siege_striker.power).to eq(1) diff --git a/spec/cards/sol_ring_spec.rb b/spec/cards/sol_ring_spec.rb index f37e1d6..0d9887d 100644 --- a/spec/cards/sol_ring_spec.rb +++ b/spec/cards/sol_ring_spec.rb @@ -6,9 +6,7 @@ context "tap" do it "taps for two colorless mana" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) expect(p1.mana_pool[:colorless]).to eq(2) end end diff --git a/spec/cards/speaker_of_the_heavens_spec.rb b/spec/cards/speaker_of_the_heavens_spec.rb index de3bbdc..6b26bee 100644 --- a/spec/cards/speaker_of_the_heavens_spec.rb +++ b/spec/cards/speaker_of_the_heavens_spec.rb @@ -14,7 +14,7 @@ it "activated ability" do p1.gain_life(7) ability = speaker_of_the_heavens.activated_abilities.first - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: speaker_of_the_heavens, ability: ability) + action = p1.prepare_activate_ability(ability: ability) expect(action.can_be_activated?(p1)).to eq(true) action.pay_tap action.finalize_costs!(p1) diff --git a/spec/cards/sure_strike_spec.rb b/spec/cards/sure_strike_spec.rb index 09630a5..05c027c 100644 --- a/spec/cards/sure_strike_spec.rb +++ b/spec/cards/sure_strike_spec.rb @@ -6,14 +6,13 @@ let!(:sure_strike) { described_class.new(game: game) } let!(:onakke_ogre) { ResolvePermanent("Onakke Ogre", owner: p1) } context "with a creature in play" do - + it "grants first strike and 3 power" do p1.add_mana(red: 2) - action = Magic::Actions::Cast.new(player: p1, card: sure_strike) - .pay_mana(generic: { red: 1 }, red: 1) - .targeting(onakke_ogre) - game.take_action(action) + p1.cast(card: sure_strike) do + _1.pay_mana(generic: { red: 1 }, red: 1).targeting(onakke_ogre) + end game.tick! expect(onakke_ogre.power).to eq(7) diff --git a/spec/cards/swamp_spec.rb b/spec/cards/swamp_spec.rb index 3201e10..4c29cc2 100644 --- a/spec/cards/swamp_spec.rb +++ b/spec/cards/swamp_spec.rb @@ -6,9 +6,7 @@ it "taps for a single white mana" do expect(p1).to receive(:add_mana).with(black: 1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: subject, ability: subject.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: subject.activated_abilities.first) expect(subject).to be_tapped end end diff --git a/spec/cards/tempered_veteran_spec.rb b/spec/cards/tempered_veteran_spec.rb index dcb77a7..f39c1d1 100644 --- a/spec/cards/tempered_veteran_spec.rb +++ b/spec/cards/tempered_veteran_spec.rb @@ -12,23 +12,14 @@ it "adds a counter to a creature with a counter" do p1.add_mana(white: 2) wood_elves.add_counter(Magic::Counters::Plus1Plus1) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: tempered_veteran, ability: ability) - action.targeting(wood_elves) - action.pay_mana({ white: 1 }) - action.pay_tap - action.finalize_costs!(p1) - - game.take_action(action) + p1.activate_ability(ability: ability) do + _1.targeting(wood_elves).pay_mana(white: 1) + end game.tick! expect(tempered_veteran).to be_tapped expect(wood_elves.counters.of_type(Magic::Counters::Plus1Plus1).count).to eq(2) end - - it "cannot add a counter to a creature without a counter" do - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: tempered_veteran, ability: ability) - expect(action.valid_targets?(wood_elves)).to eq(false) - end end context "second activated ability" do @@ -36,13 +27,9 @@ it "adds a counter to a creature without a counter" do p1.add_mana(white: 6) - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: tempered_veteran, ability: ability) - action.targeting(wood_elves) - action.pay_mana({ generic: { white: 4 }, white: 2 }) - action.pay_tap - action.finalize_costs!(p1) - - game.take_action(action) + p1.activate_ability(ability: ability) do + _1.targeting(wood_elves).pay_mana({ generic: { white: 4 }, white: 2 }) + end game.tick! expect(tempered_veteran).to be_tapped diff --git a/spec/cards/ugin_the_spirit_dragon_spec.rb b/spec/cards/ugin_the_spirit_dragon_spec.rb index b026b3f..378a904 100644 --- a/spec/cards/ugin_the_spirit_dragon_spec.rb +++ b/spec/cards/ugin_the_spirit_dragon_spec.rb @@ -10,9 +10,9 @@ let(:wood_elves) { ResolvePermanent("Wood Elves", owner: p2) } it "targets the wood elves" do - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: ugin, ability: ability) - .targeting(wood_elves) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) do + _1.targeting(wood_elves) + end game.stack.resolve! expect(subject.loyalty).to eq(9) expect(wood_elves.damage).to eq(3) @@ -20,9 +20,9 @@ it "targets the other player" do p2_starting_life = p2.life - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: ugin, ability: ability) - .targeting(p2) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) do + _1.targeting(p2) + end game.stack.resolve! expect(p2.life).to eq(p2_starting_life - 3) end @@ -34,9 +34,9 @@ let!(:sol_ring) { ResolvePermanent("Sol Ring", owner: p2) } it "exiles wood elves, leaves the sol ring" do - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: ugin, ability: ability) - .value_for_x(3) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) do + _1.value_for_x(3) + end game.stack.resolve! expect(subject.loyalty).to eq(4) # Wood elves has a color, so it goes @@ -68,8 +68,7 @@ end it "controller gains 7 life, draws 7 cards and moves 7 permanents to the battlefield" do - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: ugin, ability: ability) - game.take_action(action) + p1.activate_loyalty_ability(ability: ability) game.stack.resolve! expect(subject.loyalty).to eq(0) expect(subject.zone).to be_nil diff --git a/spec/game/integration/attackers_declared_spec.rb b/spec/game/integration/attackers_declared_spec.rb index afc6584..683c5cd 100644 --- a/spec/game/integration/attackers_declared_spec.rb +++ b/spec/game/integration/attackers_declared_spec.rb @@ -13,8 +13,7 @@ context "with basri ket's delayed trigger" do before do basri_ket.change_loyalty!(7) - action = Magic::Actions::ActivateLoyaltyAbility.new(player: p1, planeswalker: basri_ket, ability: basri_ket.loyalty_abilities[1]) - game.take_action(action) + p1.activate_loyalty_ability(ability: basri_ket.loyalty_abilities[1]) game.stack.resolve! end diff --git a/spec/game/integration/mana/essence_warden_spec.rb b/spec/game/integration/mana/essence_warden_spec.rb index 73714aa..5fc8ab8 100644 --- a/spec/game/integration/mana/essence_warden_spec.rb +++ b/spec/game/integration/mana/essence_warden_spec.rb @@ -11,9 +11,8 @@ end it "cannot cast the essence warden" do - action = Magic::Actions::Cast.new(player: p1, card: essence_warden) + action = p1.prepare_cast(card: essence_warden) expect(action.can_perform?).to eq(false) - end end @@ -27,16 +26,14 @@ end it "casts a forest, then an essence warden" do - action = Magic::Actions::Cast.new(player: p1, card: forest) - expect(action.can_perform?).to eq(true) - game.take_action(action) + p1.play_land(land: forest) game.tick! forest = p1.permanents.by_name("Forest").first - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: forest, ability: forest.activated_abilities.first) - game.take_action(action) + p1.activate_ability(ability: forest.activated_abilities.first) expect(p1.mana_pool[:green]).to eq(1) - action = Magic::Actions::Cast.new(player: p1, card: essence_warden) + + action = p1.prepare_cast(card: essence_warden) expect(action.can_perform?).to eq(true) action.pay_mana(green: 1) game.take_action(action) diff --git a/spec/game/integration/mana/foundry_inspector_sol_ring_spec.rb b/spec/game/integration/mana/foundry_inspector_sol_ring_spec.rb index 37fb492..1b395e6 100644 --- a/spec/game/integration/mana/foundry_inspector_sol_ring_spec.rb +++ b/spec/game/integration/mana/foundry_inspector_sol_ring_spec.rb @@ -22,17 +22,17 @@ it "casts a foundry inspector and then a sol ring" do p1.add_mana(red: 3) - action = Magic::Actions::Cast.new(player: p1, card: foundry_inspector) - expect(action.can_perform?).to eq(true) - action.pay_mana(generic: { red: 3 } ) - game.take_action(action) + p1.cast(card: foundry_inspector) do + _1.pay_mana(generic: { red: 3 }) + end + game.tick! - action = Magic::Actions::Cast.new(player: p1, card: sol_ring) - expect(action.can_perform?).to eq(true) - game.take_action(action) + # Sol Ring cost discounted by 1 by Foundry Inspector, so is free + p1.cast(card: sol_ring) game.tick! + expect(p1.permanents.by_name(sol_ring.name).count).to eq(1) end end diff --git a/spec/game/integration/turns_spec.rb b/spec/game/integration/turns_spec.rb index 78927f3..bc1b111 100644 --- a/spec/game/integration/turns_spec.rb +++ b/spec/game/integration/turns_spec.rb @@ -27,9 +27,7 @@ turn_1.draw! turn_1.first_main! - #game.current_turn.possible_actions - action = Magic::Actions::PlayLand.new(player: p1, card: p1.hand.by_name("Island").first) - game.take_action(action) + p1.play_land(land: p1.hand.by_name("Island").first) expect(p1.permanents.by_name("Island").count).to eq(1) expect(p1.lands_played).to eq(1) # 6 islands on game start draw, + aegis turtle @@ -38,20 +36,19 @@ expect(p1.hand.by_name("Island").count).to eq(6) island = p1.permanents.by_name("Island").first - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: island, ability: island.activated_abilities.first) - action.pay_tap - game.take_action(action) + p1.activate_ability(ability: island.activated_abilities.first) expect(p1.mana_pool).to eq(blue: 1) expect(island).to be_tapped island2 = p1.hand.by_name("Island").first - action = Magic::Actions::PlayLand.new(player: p1, card: island2) + action = p1.prepare_action(Magic::Actions::PlayLand, card: island2) expect(action.can_perform?).to eq(false) aegis_turtle = p1.hand.by_name("Aegis Turtle").first - action = Magic::Actions::Cast.new(player: p1, card: aegis_turtle) - action.pay_mana(blue: 1) - game.take_action(action) + action = p1.cast(card: aegis_turtle) do |action| + action.pay_mana(blue: 1) + end + game.stack.resolve! expect(p1.permanents.by_name("Aegis Turtle").count).to eq(1) @@ -70,17 +67,15 @@ turn_2.draw! turn_2.first_main! - action = Magic::Actions::PlayLand.new(player: p2, card: p2.hand.by_name("Mountain").first) - game.take_action(action) + p2.play_land(land: p2.hand.by_name("Mountain").first) mountain = p2.permanents.by_name("Mountain").first - action = Magic::Actions::ActivateAbility.new(player: p1, permanent: mountain, ability: mountain.activated_abilities.first) - game.take_action(action) + p2.activate_ability(ability: mountain.activated_abilities.first) expect(p2.mana_pool).to eq(red: 1) raging_goblin = p2.hand.by_name("Raging Goblin").first - action = Magic::Actions::Cast.new(player: p2, card: raging_goblin) - action.pay_mana(red: 1) - game.take_action(action) + p2.cast(card: raging_goblin) do |action| + action.pay_mana(red: 1) + end game.stack.resolve! expect(p2.permanents.by_name("Raging Goblin").count).to eq(1) @@ -122,8 +117,5 @@ turn_3.first_main! expect(p1.lands_played).to eq(0) - island = p1.hand.by_name("Island").first - action = Magic::Actions::Cast.new(player: p1, card: island) - expect(action.can_perform?).to eq(true) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 80ed4ff..9fe62d0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,9 +25,10 @@ def add_to_library(name, player:) card end - def cast_action(card:, player:, targeting: nil) - action = Magic::Actions::Cast.new(card: card, player: player) + def cast_action(card:, player:, targeting: nil, &block) + action = Magic::Actions::Cast.new(card: card, player: player, game: game) action.targeting(targeting) if targeting + yield action if block_given? action end @@ -36,8 +37,8 @@ def add_to_stack_and_resolve(action) game.stack.resolve! end - def cast_and_resolve(card:, player:, targeting: nil) - action = cast_action(card: card, player: player, targeting: targeting) + def cast_and_resolve(card:, player:, targeting: nil, &block) + action = cast_action(card: card, player: player, targeting: targeting, &block) game.stack.add(action) game.stack.resolve! end