Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs #500

Merged
merged 5 commits into from
Dec 13, 2023
Merged
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: 0 additions & 3 deletions fireplace/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,6 @@ class Silence(TargetedAction):
def do(self, source, target):
log.info("Silencing %r", self)
self.broadcast(source, EventListener.ON, target)
old_health = target.health
target.clear_buffs()
for attr in target.silenceable_attributes:
if getattr(target, attr):
Expand All @@ -1286,8 +1285,6 @@ def do(self, source, target):
# Wipe the event listeners
target._events = []
target.silenced = True
if target.health < old_health:
target.damage = max(target.damage - (old_health - target.health), 0)


class Summon(TargetedAction):
Expand Down
14 changes: 10 additions & 4 deletions fireplace/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def events(self):
return self.data.scripts.Hand.events
if self.zone == Zone.DECK:
return self.data.scripts.Deck.events
return self.base_events + self._events
return self.base_events + list(self._events)

@property
def cost(self):
Expand Down Expand Up @@ -338,7 +338,7 @@ def discard(self):
self.zone = Zone.GRAVEYARD
if old_zone == Zone.HAND:
actions = self.get_actions("discard")
self.game.trigger(self, actions, event_args=None)
self.game.cheat_action(self, actions)

def draw(self):
if len(self.controller.hand) >= self.controller.max_hand_size:
Expand All @@ -352,7 +352,7 @@ def draw(self):
if self.game.step > Step.BEGIN_MULLIGAN:
# Proc the draw script, but only if we are past mulligan
actions = self.get_actions("draw")
self.game.trigger(self, actions, event_args=None)
self.game.cheat_action(self, actions)

def heal(self, target, amount):
return self.game.cheat_action(self, [actions.Heal(target, amount)])
Expand Down Expand Up @@ -1041,9 +1041,15 @@ def _set_zone(self, zone):
# Can happen if a Destroy is queued after a bounce, for example
self.logger.warning("Trying to remove %r which is already gone", self)
return
if hasattr(self.owner, "health"):
old_health = self.owner.health
self.owner.buffs.remove(self)
if self in self.game.active_aura_buffs:
self.game.active_aura_buffs.remove(self)
if hasattr(self.owner, "health"):
if self.owner.health < old_health:
self.owner.damage = max(self.owner.damage - (old_health - self.owner.health), 0)

super()._set_zone(zone)

def apply(self, target):
Expand Down Expand Up @@ -1089,7 +1095,7 @@ def _set_zone(self, zone):
if zone == Zone.PLAY:
if self.controller.weapon:
self.log("Destroying old weapon %r", self.controller.weapon)
self.game.trigger(self, [actions.Destroy(self.controller.weapon)], event_args=None)
self.controller.weapon.destroy()
self.controller.weapon = self
elif self.zone == Zone.PLAY:
self.controller.weapon = None
Expand Down
4 changes: 2 additions & 2 deletions fireplace/cards/brawl/pick_your_fate.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class TB_PickYourFate_9:


class TB_PickYourFate_9_Ench:
update = Refresh(FRIENDLY_MINIONS + DEATHRATTLE, "TB_PickYourFate_9_EnchMinion")
update = Refresh(FRIENDLY_MINIONS + DEATHRATTLE, buff="TB_PickYourFate_9_EnchMinion")


TB_PickYourFate_9_EnchMinion = buff(+1, +1)
Expand All @@ -222,7 +222,7 @@ class TB_PickYourFate_10:


class TB_PickYourFate_10_Ench:
update = Refresh(FRIENDLY_MINIONS + BATTLECRY, "TB_PickYourFate_10_EnchMinion")
update = Refresh(FRIENDLY_MINIONS + BATTLECRY, buff="TB_PickYourFate_10_EnchMinion")


TB_PickYourFate_10_EnchMinion = buff(+1, +1)
Expand Down
2 changes: 1 addition & 1 deletion fireplace/cards/kobolds/neutral_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class LOOT_134e:
class LOOT_136:
"""Sneaky Devil"""
# <b>Stealth</b> Your other minions have +1 Attack.
update = Refresh(FRIENDLY_MINIONS - SELF, "LOOT_136e")
update = Refresh(FRIENDLY_MINIONS - SELF, buff="LOOT_136e")


LOOT_136e = buff(atk=1)
Expand Down
5 changes: 4 additions & 1 deletion fireplace/dsl/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@ def copy(self, source, entity):
ret.damage = entity.damage
for buff in entity.buffs:
# Recreate the buff stack
entity.buff(ret, buff.id)
new_buff = buff.source.buff(ret, buff.id)
if buff in source.game.active_aura_buffs:
new_buff.tick = buff.tick
source.game.active_aura_buffs.append(new_buff)
return ret
17 changes: 9 additions & 8 deletions fireplace/dsl/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Selector:
Set operations preserve ordering (necessary for cards like Echo of
Medivh, where ordering matters)
"""

def eval(self, entities: List[BaseEntity], source: BaseEntity) -> List[BaseEntity]:
return entities

Expand Down Expand Up @@ -534,14 +535,14 @@ def CONTROLLED_BY(selector):

NEUTRAL = AttrValue(GameTag.CLASS) == CardClass.NEUTRAL

LEFTMOST_FIELD = FuncSelector(lambda entities, source:
source.game.player1.field[:1] + source.game.player2.field[:1])
RIGTHMOST_FIELD = FuncSelector(lambda entities, source:
source.game.player1.field[-1:] + source.game.player2.field[-1:])
LEFTMOST_HAND = FuncSelector(lambda entities, source:
source.game.player1.hand[:1] + source.game.player2.hand[-1:])
RIGTHMOST_HAND = FuncSelector(lambda entities, source:
source.game.player1.hand[:1] + source.game.player2.hand[-1:])
LEFTMOST_FIELD = FuncSelector(
lambda entities, source: source.game.player1.field[:1] + source.game.player2.field[:1])
RIGTHMOST_FIELD = FuncSelector(
lambda entities, source: source.game.player1.field[-1:] + source.game.player2.field[-1:])
LEFTMOST_HAND = FuncSelector(
lambda entities, source: source.game.player1.hand[:1] + source.game.player2.hand[-1:])
RIGTHMOST_HAND = FuncSelector(
lambda entities, source: source.game.player1.hand[:1] + source.game.player2.hand[-1:])
OUTERMOST_HAND = LEFTMOST_HAND + RIGTHMOST_HAND

CARDS_PLAYED_THIS_GAME = FuncSelector(
Expand Down
2 changes: 1 addition & 1 deletion fireplace/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def is_card(self):

@property
def events(self):
return self.base_events + self._events
return self.base_events + list(self._events)

@property
def update_scripts(self):
Expand Down
13 changes: 10 additions & 3 deletions fireplace/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ def card(self, id, source=None, parent=None, zone=Zone.SETASIDE):

def prepare_for_game(self):
self.summon(self.starting_hero)
# self.game.trigger(self, [Summon(self, self.starting_hero)], event_args=None)
self.starting_hero = self.hero
for id in self.starting_deck:
card = self.card(id, zone=Zone.DECK)
Expand All @@ -190,8 +191,14 @@ def prepare_for_game(self):
# Draw initial hand (but not any more than what we have in the deck)
hand_size = min(len(self.deck), self.start_hand_size)
# Quest cards are automatically included in the player's mulligan as the left-most card
quests = [card for card in self.deck if card.data.quest]
starting_hand = quests + random.sample(self.deck, hand_size - len(quests))
quests = []
exclude_quests = []
for card in self.deck:
if card.data.quest:
quests.append(card)
else:
exclude_quests.append(card)
starting_hand = quests + random.sample(exclude_quests, hand_size - len(quests))
# It's faster to move cards directly to the hand instead of drawing
for card in starting_hand:
card.zone = Zone.HAND
Expand Down Expand Up @@ -298,6 +305,6 @@ def summon(self, card):
Puts \a card in the PLAY zone
"""
if isinstance(card, str):
card = self.card(card, zone=Zone.PLAY)
card = self.card(card)
self.game.cheat_action(self, [Summon(self, card)])
return card
49 changes: 48 additions & 1 deletion tests/test_classic.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,13 @@ def test_angry_chicken():
assert chicken.enrage
assert not chicken.enraged
assert chicken.atk == chicken.health == 2
game.skip_turn()
game.player1.give(MOONFIRE).play(target=chicken)
assert chicken.enraged
assert chicken.atk == 1 + 1 + 5
assert chicken.health == 1
stormwind.destroy()
game.player1.give(FIREBALL).play(target=stormwind)
assert len(game.player1.field) == 1
assert chicken.atk == chicken.health == 1
assert not chicken.enraged

Expand Down Expand Up @@ -3747,3 +3749,48 @@ def test_ysera_awakens():
assert game.player1.hero.health == game.player2.hero.health == 30 - 5
assert len(game.board) == 1
assert ysera.health == 12


def test_mirror_entity_aura():
# https://github.com/jleclanche/fireplace/issues/221
game = prepare_game()
game.end_turn()
game.player2.give("CS2_222").play() # Stormwind Champion
game.end_turn()

mirror = game.player1.give("EX1_294")
mirror.play()
game.end_turn()

# Mirror entity copies the exact nature of the card when it hits the field.
blademaster = game.player2.give("CS2_181")
blademaster.play()
assert len(game.player1.field) == 1
assert len(game.player2.field) == 2
assert game.player1.field[0].health == 4
assert game.player1.field[0].max_health == 7
assert game.player2.field[1].health == 4
assert game.player2.field[1].max_health == 8


def test_stormwind_champion_heal():
# https://github.com/jleclanche/fireplace/issues/226
game = prepare_game()

goldshire = game.player1.summon(GOLDSHIRE_FOOTMAN)
assert goldshire.atk == 1
assert goldshire.health == 2
stormwind = game.player1.give("CS2_222")
stormwind.play()
assert goldshire.atk == 2
assert goldshire.health == 3

game.player1.give(MOONFIRE).play(target=goldshire)
assert goldshire.atk == 2
assert goldshire.health == 2
game.end_turn()

# Destroy with Fireball
game.player2.give(FIREBALL).play(target=stormwind)
assert goldshire.atk == 1
assert goldshire.health == 2
12 changes: 12 additions & 0 deletions tests/test_kobolds.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,15 @@ def test_crushing_walls():
game.player2.give("LOOT_522").play()
assert len(game.player1.field) == 1
assert game.player1.field[0].id == CHICKEN


def test_dragon_soul():
game = prepare_game()
game.player1.give("LOOT_209").play()
for _ in range(3):
game.player1.give(MOONFIRE).play(target=game.player2.hero)
assert len(game.player1.field) == 1
game.skip_turn()
for _ in range(3):
game.player1.give(MOONFIRE).play(target=game.player2.hero)
assert len(game.player1.field) == 2