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 #514

Merged
merged 2 commits into from
Jan 9, 2024
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
89 changes: 50 additions & 39 deletions fireplace/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,11 +832,10 @@ class Damage(TargetedAction):
TARGET = ActionArg()
AMOUNT = IntArg()

def get_target_args(self, source, target):
return [target.predamage]

def do(self, source, target, amount):
amount = target._hit(target.predamage)
def do(self, source, target, amount=None):
if not amount:
amount = target.predamage
amount = target._hit(amount)
target.predamage = 0
if (source.type == CardType.MINION or source.type == CardType.HERO) and source.stealthed:
# TODO this should be an event listener of sorts
Expand Down Expand Up @@ -938,12 +937,15 @@ def do(self, source, card, target=None):
source.game.main_power(source, actions, target)

if (
player.extra_combos and card.type == CardType.MINION and
player.minion_extra_combos and card.type == CardType.MINION and
card.has_combo and player.combo
) or (
player.extra_battlecries and card.has_battlecry
) or (
player.minion_extra_battlecries and card.type == CardType.MINION and
card.has_battlecry
):
source.game.main_power(source, actions, target)
elif player.extra_battlecries and card.has_battlecry:
source.game.main_power(source, actions, target)

if card.overload:
source.game.queue_actions(card, [Overload(player, card.overload)])
Expand Down Expand Up @@ -1612,9 +1614,6 @@ def get_target_args(self, source, target):
spell_target = [None]
if ret:
spell_target = ret[0]
else:
if target.target:
return [target.target]
return [spell_target]

def do(self, source, card, targets):
Expand Down Expand Up @@ -1831,18 +1830,23 @@ def do_step3(self):

def done(self):
card = self.choosed_cards[0]
new_card = self.player.card(self.potions_card[card.id])
self.potions = self.potions_choice_map[card.id][:]
card1 = self.choosed_cards[1]
card2 = self.choosed_cards[2]
self.potions = self.potions_choice_map[card.id][:]
if self.potions.index(card1.id) > self.potions.index(card2.id):
card1, card2 = card2, card1
new_card.requirements.update(card1.requirements)
new_card.requirements.update(card2.requirements)
new_card.data.scripts.play = card1.data.scripts.play + card2.data.scripts.play
new_card.requirements = card1.requirements | card2.requirements
new_card.tags[GameTag.CARDTEXT_ENTITY_0] = card1.data.strings[GameTag.CARDTEXT]
new_card.tags[GameTag.CARDTEXT_ENTITY_1] = card2.data.strings[GameTag.CARDTEXT]

new_card = self.player.card(self.potions_card[card.id])
new_card.custom_card = True

def create_custom_card(new_card):
new_card.data.scripts.play = card1.data.scripts.play + card2.data.scripts.play
new_card.requirements = card1.requirements | card2.requirements
new_card.tags[GameTag.CARDTEXT_ENTITY_0] = card1.data.strings[GameTag.CARDTEXT]
new_card.tags[GameTag.CARDTEXT_ENTITY_1] = card2.data.strings[GameTag.CARDTEXT]

new_card.create_custom_card = create_custom_card
new_card.create_custom_card(new_card)
self.player.give(new_card)

def choose(self, card):
Expand Down Expand Up @@ -2047,28 +2051,35 @@ def do_step2(self):
self.cards = [self.player.card(id) for id in random.sample(self.second_ids, 3)]

def done(self):
zombeast = self.player.card("ICC_828t")
card1 = self.choosed_cards[0]
card2 = self.choosed_cards[1]
zombeast.tags[GameTag.CARDTEXT_ENTITY_0] = card2.data.strings[GameTag.CARDTEXT]
zombeast.tags[GameTag.CARDTEXT_ENTITY_1] = card1.data.strings[GameTag.CARDTEXT]
zombeast.data.scripts = card1.data.scripts
int_mergeable_attributes = (
"atk", "cost", "max_health", "incoming_damage_multiplier", "spellpower",
"windfury",
)
bool_mergeable_attributes = (
"has_deathrattle", "charge", "has_inspire", "stealthed", "cant_attack",
"cant_be_targeted_by_opponents", "cant_be_targeted_by_abilities",
"cant_be_targeted_by_hero_powers", "heavily_armored", "min_health",
"rush", "taunt", "poisonous", "ignore_taunt", "cannot_attack_heroes",
"unlimited_attacks", "cant_be_damaged", "lifesteal",
"cant_be_targeted_by_op_abilities", "cant_be_targeted_by_op_hero_powers",
)
for attribute in int_mergeable_attributes:
setattr(zombeast, attribute, getattr(card1, attribute) + getattr(card2, attribute))
for attribute in bool_mergeable_attributes:
setattr(zombeast, attribute, getattr(card1, attribute) or getattr(card2, attribute))

zombeast = self.player.card("ICC_828t")
zombeast.custom_card = True

def create_custom_card(zombeast):
zombeast.tags[GameTag.CARDTEXT_ENTITY_0] = card2.data.strings[GameTag.CARDTEXT]
zombeast.tags[GameTag.CARDTEXT_ENTITY_1] = card1.data.strings[GameTag.CARDTEXT]
zombeast.data.scripts = card1.data.scripts
int_mergeable_attributes = (
"atk", "cost", "max_health", "incoming_damage_multiplier", "spellpower",
"windfury",
)
bool_mergeable_attributes = (
"has_deathrattle", "charge", "has_inspire", "stealthed", "cant_attack",
"cant_be_targeted_by_opponents", "cant_be_targeted_by_abilities",
"cant_be_targeted_by_hero_powers", "heavily_armored", "min_health",
"rush", "taunt", "poisonous", "ignore_taunt", "cannot_attack_heroes",
"unlimited_attacks", "cant_be_damaged", "lifesteal",
"cant_be_targeted_by_op_abilities", "cant_be_targeted_by_op_hero_powers",
)
for attribute in int_mergeable_attributes:
setattr(zombeast, attribute, getattr(card1, attribute) + getattr(card2, attribute))
for attribute in bool_mergeable_attributes:
setattr(zombeast, attribute, getattr(card1, attribute) or getattr(card2, attribute))

zombeast.create_custom_card = create_custom_card
zombeast.create_custom_card(zombeast)
self.player.give(zombeast)

def choose(self, card):
Expand Down
1 change: 1 addition & 0 deletions fireplace/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ def __init__(self, data):
self.upgrade_counter = 0
self.cast_on_friendly_minions = False
self.play_right_most = False
self.custom_card = False
super().__init__(data)

@property
Expand Down
2 changes: 1 addition & 1 deletion fireplace/cards/blackrock/collectible.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class BRM_029:

class BRM_030:
"""Nefarian"""
play = Find(ENEMY_HERO + CLASS_CARD) & (
play = Find(ENEMY_HERO - NEUTRAL) & (
Give(CONTROLLER, RandomSpell(card_class=ENEMY_CLASS)) * 2
) | (
Give(CONTROLLER, "BRM_030t") * 2
Expand Down
2 changes: 1 addition & 1 deletion fireplace/cards/boomsday/neutral_rare.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class BOT_066:
class BOT_098:
"""Unpowered Mauler"""
# Can only attack if you cast a spell this turn.
update = Find(CARDS_PLAYED_THIS_GAME + SPELL) | Refresh(SELF, {GameTag.CANT_ATTACK: True})
update = Find(CARDS_PLAYED_THIS_TRUN + SPELL) | Refresh(SELF, {GameTag.CANT_ATTACK: True})


class BOT_102:
Expand Down
3 changes: 2 additions & 1 deletion fireplace/cards/icecrown/rogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class ICC_809:

class ICC_811:
"""Lilian Voss"""
play = Morph(FRIENDLY_HAND + SPELL, RandomSpell(card_class=ENEMY_CLASS))
play = Find(ENEMY_HERO - NEUTRAL) & (
Morph(FRIENDLY_HAND + SPELL, RandomSpell(card_class=ENEMY_CLASS)))


class ICC_910:
Expand Down
3 changes: 2 additions & 1 deletion fireplace/cards/kobolds/rogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class LOOT_211:
class LOOT_412:
"""Kobold Illusionist"""
# <b>Deathrattle:</b> Summon a 1/1 copy of a minion from your hand.
deathrattle = Summon(CONTROLLER, Buff(RANDOM(FRIENDLY_HAND + MINION), "LOOT_412e"))
deathrattle = Summon(CONTROLLER, RANDOM(FRIENDLY_HAND + MINION)).then(
Buff(Summon.CARD, "LOOT_412e"))


class LOOT_412e:
Expand Down
2 changes: 1 addition & 1 deletion fireplace/cards/troll/hunter.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class TRL_065:
"""Zul'jin"""
# [x]<b>Battlecry:</b> Cast all spells you've played this game <i>(targets chosen
# randomly)</i>.
play = CastSpell(CARDS_PLAYED_THIS_GAME + SPELL)
play = CastSpell(Copy(CARDS_PLAYED_THIS_GAME + SPELL))


class TRL_065h:
Expand Down
2 changes: 1 addition & 1 deletion fireplace/cards/troll/neutral_epic.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class TRL_535:
"""Snapjaw Shellfighter"""
# [x]Whenever an adjacent minion takes damage, this _minion takes it instead.
events = Predamage(SELF_ADJACENT).on(
Predamage(Predamage.TARGET, 0), Hit(SELF, Predamage.AMOUNT)
Predamage(Predamage.TARGET, 0), Damage(SELF, Predamage.AMOUNT)
)


Expand Down
6 changes: 3 additions & 3 deletions fireplace/cards/troll/rogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class TRL_092:
OWN_TURN_BEGIN.on(Unstealth(SELF)),
)
update = (
Refresh(CONTROLLER, {enums.EXTRA_BATTLECRIES: True}),
Refresh(CONTROLLER, {enums.EXTRA_COMBOS: True}),
Refresh(CONTROLLER, {enums.MINION_EXTRA_BATTLECRIES: True}),
Refresh(CONTROLLER, {enums.MINION_EXTRA_COMBOS: True}),
)


Expand All @@ -53,7 +53,7 @@ class TRL_409:
"""Gral, the Shark"""
# [x]<b>Battlecry:</b> Eat a minion in your deck and gain its stats.
# <b>Deathrattle:</b> Add it to your hand.
play = (
play = Find(FRIENDLY_DECK + MINION) & (
Retarget(SELF, RANDOM(FRIENDLY_DECK + MINION)),
Reveal(TARGET),
Destroy(TARGET),
Expand Down
8 changes: 6 additions & 2 deletions fireplace/cards/witchwood/priest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ class GIL_142:
# Each turn this is in your hand, transform it into a card your opponent is holding.
class Hand:
events = OWN_TURN_BEGIN.on(
Morph(SELF, ExactCopy(RANDOM(ENEMY_HAND))).then(Buff(Morph.CARD, "GIL_142e"))
Find(ENEMY_HAND) & (
Morph(SELF, ExactCopy(RANDOM(ENEMY_HAND))).then(Buff(Morph.CARD, "GIL_142e"))
)
)


class GIL_142e:
class Hand:
events = OWN_TURN_BEGIN.on(
Morph(OWNER, ExactCopy(RANDOM(ENEMY_HAND))).then(Buff(Morph.CARD, "GIL_142e"))
Find(ENEMY_HAND) & (
Morph(OWNER, ExactCopy(RANDOM(ENEMY_HAND))).then(Buff(Morph.CARD, "GIL_142e"))
)
)
events = REMOVED_IN_PLAY

Expand Down
2 changes: 1 addition & 1 deletion fireplace/cards/witchwood/shaman.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class GIL_820:
"""Shudderwock"""
# [x]<b>Battlecry:</b> Repeat all other <b>Battlecries</b> from cards you played this
# game <i>(targets chosen randomly)</i>.
play = Battlecry(RANDOM(CARDS_PLAYED_THIS_GAME + BATTLECRITES - SELF) * 30, None)
play = Battlecry(RANDOM(CARDS_PLAYED_THIS_GAME + BATTLECRY - ID("GIL_820")) * 30, None)


##
Expand Down
9 changes: 7 additions & 2 deletions fireplace/dsl/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ def copy(self, source, entity):
Return a copy of \a entity
"""
log.info("Creating a copy of %r", entity)
return source.controller.card(entity.id, source)
new_entity = source.controller.card(entity.id, source)
if entity.custom_card:
new_entity.custom_card = True
new_entity.create_custom_card = entity.create_custom_card
new_entity.create_custom_card(new_entity)
return new_entity

def evaluate(self, source) -> list[str]:
if isinstance(self.selector, LazyValue):
Expand Down Expand Up @@ -53,7 +58,7 @@ def copy(self, source, entity):
for buff in entity.buffs:
# Recreate the buff stack
new_buff = source.controller.card(buff.id)
new_buff.source = source
new_buff.source = buff.source
attributes = ["atk", "max_health", "_xatk", "_xhealth", "_xcost", "store_card"]
for attribute in attributes:
if hasattr(buff, attribute):
Expand Down
3 changes: 2 additions & 1 deletion fireplace/dsl/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ def check(self, source):
if isinstance(self.selector, Selector):
entities = self.selector.eval(source.game, source)
else:
entities = [self.selector.evaluate(source)]
entity = self.selector.evaluate(source)
entities = [entity] if entity else []
for target in entities:
if target.dead:
return True
Expand Down
1 change: 0 additions & 1 deletion fireplace/dsl/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,6 @@ def CONTROLLED_BY(selector):
BATTLECRY = EnumSelector(GameTag.BATTLECRY)
CHARGE = EnumSelector(GameTag.CHARGE)
COMBO = EnumSelector(GameTag.COMBO)
BATTLECRITES = EnumSelector(GameTag.BATTLECRY)
DAMAGED = EnumSelector(GameTag.DAMAGE)
DEATHRATTLE = EnumSelector(GameTag.DEATHRATTLE)
DIVINE_SHIELD = EnumSelector(GameTag.DIVINE_SHIELD)
Expand Down
3 changes: 2 additions & 1 deletion fireplace/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
CANT_BE_TARGETED_BY_OP_HERO_POWERS = -23
KEEP_BUFF = -24,
DAMAGED_THIS_TURN = -25
EXTRA_COMBOS = -26
MINION_EXTRA_COMBOS = -26
MINION_EXTRA_BATTLECRIES = -27
3 changes: 2 additions & 1 deletion fireplace/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ class PlayerManager(Manager):
enums.ALWAYS_WINS_BRAWLS: "always_wins_brawls",
enums.CAST_ON_FRIENDLY_MINIONS: "cast_on_friendly_minions",
enums.EXTRA_BATTLECRIES: "extra_battlecries",
enums.EXTRA_COMBOS: "extra_combos",
enums.MINION_EXTRA_BATTLECRIES: "minio_extra_battlecries",
enums.MINION_EXTRA_COMBOS: "minio_extra_combos",
enums.KILLED_THIS_TURN: "killed_this_turn",
enums.DISCARDED: "discarded",
enums.MURLOCS_COST_HEALTH: "murlocs_cost_health",
Expand Down
8 changes: 5 additions & 3 deletions fireplace/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class Player(Entity, TargetableByAuras):
cant_overload = slot_property("cant_overload")
choose_both = slot_property("choose_both")
extra_battlecries = slot_property("extra_battlecries")
extra_combos = slot_property("extra_combos")
minion_extra_battlecries = slot_property("minion_extra_battlecries")
minion_extra_combos = slot_property("minion_extra_combos")
extra_deathrattles = slot_property("extra_deathrattles")
extra_end_turn_effect = slot_property("extra_end_turn_effect")
healing_double = slot_property("healing_double", sum)
Expand Down Expand Up @@ -303,8 +304,9 @@ def draw(self, count=1):
return ret

def give(self, id):
cards = self.game.cheat_action(self, [Give(self, id)])[0]
return cards[0][0]
cards = self.game.cheat_action(self, [Give(self, id)])[0][0]
if len(cards) > 0:
return cards[0]

def concede(self):
ret = self.game.cheat_action(self, [Concede(self)])
Expand Down
42 changes: 42 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,45 @@ def test_lifesteal_and_auchenai():
game.player1.give("TRL_512").play(target=game.player2.hero)
assert game.player1.hero.health == 29
assert game.player2.hero.health == 29


def test_mirror_entity_and_pumpkin_peasant():
game = prepare_empty_game()
game.player1.give("GIL_201")
game.end_turn()
game.player2.give("EX1_294").play()
game.end_turn()
for _ in range(3):
game.skip_turn()
game.player1.hand[0].play()
minion1 = game.player1.field[0]
minion2 = game.player2.field[0]
assert minion1.id == minion2.id
assert minion1.atk == minion2.atk
assert minion1.health == minion2.health


def test_nefarian_and_ragnaros_hero():
game = prepare_empty_game()
game.end_turn()
game.player2.give("BRM_027").play().destroy()
assert game.player2.hero.card_class == CardClass.NEUTRAL
game.end_turn()
game.player1.give("BRM_030").play()
assert len(game.player1.hand) == 2
assert game.player1.hand[0].id == "BRM_030t"
assert game.player1.hand[0].id == "BRM_030t"


def test_lilian_voss_andragnaros_hero():
game = prepare_empty_game()
game.end_turn()
game.player2.give("BRM_027").play().destroy()
assert game.player2.hero.card_class == CardClass.NEUTRAL
game.end_turn()
game.player1.give(MOONFIRE)
game.player1.give(MOONFIRE)
game.player1.give("ICC_811").play()
assert len(game.player1.hand) == 2
assert game.player1.hand[0].id == MOONFIRE
assert game.player1.hand[0].id == MOONFIRE
Loading