Skip to content

Commit

Permalink
Add Read The Tides
Browse files Browse the repository at this point in the history
  • Loading branch information
radar committed Jan 19, 2024
1 parent 821ab3a commit c2de838
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 12 deletions.
24 changes: 15 additions & 9 deletions lib/magic/actions/cast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ class Cast < Action
class InvalidTarget < StandardError; end

def_delegators :@card, :enchantment?, :artifact?, :multi_target?
attr_reader :card, :targets, :value_for_x, :controller
attr_reader :card, :targets, :value_for_x, :controller, :mode

def initialize(card:, value_for_x: nil, controller: card.controller, **args)
def initialize(card:, value_for_x: nil, controller: card.controller, mode: nil, **args)
super(**args)
@card = card
@targets = []
@value_for_x = value_for_x
@mode = mode
end

def inspect
"#<Actions::Cast card: #{card.name}, player: #{player.inspect}>"
"#<Actions::Cast card: #{card.name}, player: #{player.inspect}, mode: #{mode.inspect}>"
end
alias_method :name, :inspect

Expand Down Expand Up @@ -117,14 +118,19 @@ def perform
end

def resolve!
resolve_method = card.method(:resolve!)
if mode
resolver = mode.method(:resolve)
else
resolver =card.method(:resolve!)
end

args = {}
args[:target] = targets.first if resolve_method.parameters.include?([:keyreq, :target])
args[:targets] = targets if resolve_method.parameters.include?([:keyreq, :targets])
args[:kicked] = kicker_cost.paid? if resolve_method.parameters.include?([:key, :kicked])
args[:value_for_x] = mana_cost.x if resolve_method.parameters.include?([:keyreq, :value_for_x])
args[:target] = targets.first if resolver.parameters.include?([:keyreq, :target])
args[:targets] = targets if resolver.parameters.include?([:keyreq, :targets])
args[:kicked] = kicker_cost.paid? if resolver.parameters.include?([:key, :kicked])
args[:value_for_x] = mana_cost.x if resolver.parameters.include?([:keyreq, :value_for_x])

resolve_method.call(**args)
resolver.call(**args)

if card.instant? || card.sorcery?
card.move_to_graveyard!(player)
Expand Down
12 changes: 11 additions & 1 deletion lib/magic/card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Card
def_delegators :@game, :logger, :battlefield, :exile, :current_turn

include Cards::Keywords
attr_reader :game, :controller, :owner, :name, :cost, :kicker_cost, :type_line, :countered, :keyword_grants, :keywords, :protections, :delayed_responses, :counters
attr_reader :game, :controller, :owner, :name, :cost, :kicker_cost, :type_line, :countered, :keyword_grants, :keywords, :protections, :delayed_responses, :counters, :modes
attr_accessor :tapped

attr_accessor :zone
Expand All @@ -14,6 +14,7 @@ class Card
KICKER_COST = {}
KEYWORDS = []
PROTECTIONS = []
MODES = []

class << self
def creature_types(types)
Expand Down Expand Up @@ -56,6 +57,10 @@ def protections(*protections)
const_set(:PROTECTIONS, *protections)
end

def modes(*modes)
const_set(:MODES, modes)
end

def enters_the_battlefield(&block)
etb = Class.new(TriggeredAbility::EnterTheBattlefield)
etb.define_method(:perform, &block)
Expand All @@ -78,6 +83,7 @@ def initialize(game: Game.new, owner:)
@keywords = self.class::KEYWORDS
@keyword_grants = []
@protections = self.class::PROTECTIONS
@modes = self.class::MODES
@controller = @owner = owner
end

Expand Down Expand Up @@ -188,6 +194,10 @@ def replacement_effects
{}
end

def choose_mode(mode)
mode.new(source: self)
end

private

def move_zone!(new_zone)
Expand Down
30 changes: 30 additions & 0 deletions lib/magic/cards/read_the_tides.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Magic
module Cards
ReadTheTides = Sorcery("Read the Tides") do
cost generic: 5, blue: 1

class DrawThreeCards < Magic::Effect
def resolve
game.add_effect(Effects::DrawCards.new(source: source, player: controller, number_to_draw: 3))
end
end

class ReturnUpToTwoTargetCreatures < Magic::Effect
def choices
game.battlefield.creatures
end

def targeting(*targets)
@targets = targets
self
end

def resolve
targets.each(&:return_to_hand)
end
end

modes DrawThreeCards, ReturnUpToTwoTargetCreatures
end
end
end
4 changes: 4 additions & 0 deletions lib/magic/effect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ def game
source.game
end

def controller
source.controller
end

def requires_targets?
false
end
Expand Down
2 changes: 1 addition & 1 deletion lib/magic/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Game

attr_reader :logger, :battlefield, :exile, :choices, :stack, :players, :emblems, :current_turn

def_delegators :@stack, :choices, :resolve_choice!, :effects, :add_effect, :resolve_pending_effect, :next_effect
def_delegators :@stack, :choices, :skip_choice!, :resolve_choice!, :effects, :add_effect, :resolve_pending_effect, :next_effect
def_delegators :@current_turn, :take_action, :take_actions, :can_cast_sorcery?

def self.start!(players: [])
Expand Down
2 changes: 1 addition & 1 deletion spec/cards/ghostly_pilferer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
expect(p1).not_to receive(:draw!)
choice = game.choices.last
expect(choice).to be_a(Magic::Choice::Effect)
game.skip_choice!
game.resolve_choice!
expect(game.choices).to be_none
end
end
Expand Down
35 changes: 35 additions & 0 deletions spec/cards/read_the_tides_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require "spec_helper"

RSpec.describe Magic::Cards::ReadTheTides do
include_context "two player game"

let(:read_the_tides) { Card("Read The Tides") }

it "chooses draw three cards" do
expect(p1).to receive(:draw!).exactly(3).times

mode_class = read_the_tides.modes.first

p1.add_mana(blue: 6)
p1.cast(card: read_the_tides, mode: read_the_tides.choose_mode(mode_class)) do
_1.pay_mana(blue: 1, generic: { blue: 5 })
end

game.tick!
end

context "wood elves on the field" do
let!(:wood_elves) { ResolvePermanent("Wood Elves", owner: p2) }

it "chooses return a single target" do
mode_class = read_the_tides.modes.last
p1.add_mana(blue: 6)
p1.cast(card: read_the_tides, mode: read_the_tides.choose_mode(mode_class).targeting(wood_elves)) do
_1.pay_mana(blue: 1, generic: { blue: 5 })
end

game.tick!
expect(p2.hand.by_name("Wood Elves").count).to eq(1)
end
end
end

0 comments on commit c2de838

Please sign in to comment.