Skip to content

Commit

Permalink
add Lathril, Blade of the Elves
Browse files Browse the repository at this point in the history
  • Loading branch information
radar committed Jul 15, 2023
1 parent 4db4eee commit 14e89c1
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 9 deletions.
6 changes: 6 additions & 0 deletions lib/magic/actions/activate_ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def pay_tap
pay(player, :tap)
end

def pay_multi_tap(targets)
pay(player, :multi_tap, targets)
end

def perform
if targets.any?
if ability.single_target?
Expand All @@ -65,6 +69,8 @@ def pay(player, cost_type, payment = nil)
Costs::Mana
when :tap
Costs::Tap
when :multi_tap
Costs::MultiTap
when :discard
Costs::Discard
when :sacrifice
Expand Down
8 changes: 6 additions & 2 deletions lib/magic/cards/epicure_of_blood.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ def event_handlers
Events::LifeGain => -> (receiver, event) do
return unless event.player == receiver.controller

effect = Effects::DealDamageToOpponents.new(source: receiver, damage: 1)
effect.resolve(target_choices(receiver))
effect = Effects::LoseLife.new(
source: receiver,
targets: game.opponents(receiver.controller),
life: 1
)
game.add_effect(effect)
end
}
end
Expand Down
48 changes: 48 additions & 0 deletions lib/magic/cards/lathril_blade_of_the_elves.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module Magic
module Cards
LathrilBladeOfTheElves = Creature("Lathril, Blade of the Elves") do
creature_type "Elf Noble"
cost generic: 2, black: 1, green: 1
power 2
toughness 3
keywords :menace
end

class LathrilBladeOfTheElves < Creature
class ActivatedAbility < Magic::ActivatedAbility
def costs = [Costs::Tap.new(source), Costs::MultiTap.new(-> (c) { c.type?("Elf") }, 10)]

def resolve!
effect = Effects::LoseLife.new(
source: source,
targets: game.opponents(source.controller),
life: 10
)
game.add_effect(effect)

effect = Effects::GainLife.new(
source: source,
target: source.controller,
life: 10
)
game.add_effect(effect)
end
end

def activated_abilities = [ActivatedAbility]

def event_handlers
{
Events::CombatDamageDealt => -> (receiver, event) do
return unless event.target.player?

event.damage.times do
token = Tokens::ElfWarrior.new(game: game)
token.resolve!(receiver.controller)
end
end
}
end
end
end
end
22 changes: 22 additions & 0 deletions lib/magic/costs/multi_tap.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Magic
module Costs
class MultiTap
attr_reader :condition

def initialize(condition, count = 1)
@condition = condition
end

def pay(_player, targets)
targets.each(&:tap!)
end

def can_pay?(_player)
permanent.untapped?
end

def finalize!(_player)
end
end
end
end
19 changes: 19 additions & 0 deletions lib/magic/effects/gain_life.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Magic
module Effects
class GainLife < TargetedEffect
attr_reader :life, :target
def initialize(life:, target:, **args)
@life = life
@target = target
end

def requires_choices?
false
end

def resolve
target.gain_life(life)
end
end
end
end
19 changes: 19 additions & 0 deletions lib/magic/effects/lose_life.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Magic
module Effects
class LoseLife < TargetedEffect
attr_reader :life, :targets
def initialize(life:, targets:, **args)
@life = life
@targets = targets
end

def requires_choices?
false
end

def resolve
targets.each { _1.lose_life(life) }
end
end
end
end
15 changes: 13 additions & 2 deletions lib/magic/effects/targeted_effect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@ module Effects
class TargetedEffect < Effect
class InvalidTarget < StandardError; end;

attr_reader :source, :targets, :choices
attr_reader :source, :targets

def initialize(source:, targets: [], choices: source.target_choices)
def initialize(source:, targets: [], choices: [])
@targets = [*targets]
@source = source
@choices = [*choices]
end

def choices
if @choices.count > 0
@choices
elsif source.respond_to?(:target_choices)
source.target_choices
else
[]
end
end


def requires_choices?
true
end
Expand Down
15 changes: 10 additions & 5 deletions lib/magic/player.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Magic
class Player
include Targetable
extend Forwardable

attr_reader :name, :game, :lost, :library, :graveyard, :exile, :mana_pool, :hand, :life, :starting_life, :counters
Expand Down Expand Up @@ -42,24 +43,28 @@ def lose!
@lost = true
end

def gain_life(life)
def gain_life(gain)
game.notify!(
Events::LifeGain.new(
player: self,
life: life,
life: gain,
)
)
end

def take_damage(source:, damage:)
def lose_life(loss)
game.notify!(
Events::LifeLoss.new(
player: self,
life: damage,
life: loss,
)
)
end

def take_damage(source:, damage:)
lose_life(damage)
end

def lands_played
game.current_turn.actions.count { |action| action.player == self && action.is_a?(Magic::Actions::PlayLand) }
end
Expand Down Expand Up @@ -178,7 +183,7 @@ def receive_event(event)
when Events::PlayerLoses
lose!
when Events::DamageDealt, Events::CombatDamageDealt
take_damage(source: event.source, damage: event.damage)
lose_life(event.damage)
end
end

Expand Down
7 changes: 7 additions & 0 deletions lib/magic/targetable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Magic
module Targetable
def player?
self.is_a?(Player)
end
end
end
14 changes: 14 additions & 0 deletions lib/magic/tokens/elf_warrior.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Magic
module Tokens
class ElfWarrior < Creature
POWER = 1
TOUGHNESS = 1
NAME = "Elf Warrior"
TYPE_LINE = "Token Creature -- Elf Warrior"

def colors
[:green]
end
end
end
end
45 changes: 45 additions & 0 deletions spec/cards/lathril_blade_of_the_elves_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'spec_helper'

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

subject! { ResolvePermanent("Lathril, Blade Of The Elves", owner: p1) }

it "has menace" do
expect(subject).to have_keyword(Magic::Cards::Keywords::MENACE)
end

it "activated ability" do
elves = 10.times.map do
token = Magic::Tokens::ElfWarrior.new(game: game)
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)
game.stack.resolve!

expect(p1.life).to eq(p1.starting_life + 10)
expect(p2.life).to eq(p2.starting_life - 10)
end

context "when in combat" do
before do
skip_to_combat!
end

it "create elves based on combat damage dealt" do
current_turn.declare_attackers!

current_turn.declare_attacker(
subject,
target: p2,
)

go_to_combat_damage!

expect(game.battlefield.creatures.controlled_by(p1).count).to eq(3)
end
end
end

0 comments on commit 14e89c1

Please sign in to comment.