Skip to content

Commit

Permalink
Fix bugs (#522)
Browse files Browse the repository at this point in the history
* master update

* Rewrite Shudderwock and Yogg-Saron

* secret dump

* outlands changes sync to master

* fix classes

* secret

* fix bug: [ULD_134] BEEEESgit commit -m secret!

* 修复bug, 部分召唤多个卡牌实现效果只召唤一个

* 沙暴元素

* update

* black

* blacklist in test

* black
  • Loading branch information
shinoi2 authored Apr 23, 2024
1 parent 9d6b66d commit 39c9721
Show file tree
Hide file tree
Showing 64 changed files with 458 additions and 281 deletions.
110 changes: 71 additions & 39 deletions fireplace/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,13 +493,10 @@ def do(self, source, card, target, index, choose):
card.zone = Zone.PLAY

# Remember cast on friendly characters
if (
card.type == CardType.SPELL
and target
and target.type == CardType.MINION
and target.controller == source
):
card.cast_on_friendly_minions = True
if card.type == CardType.SPELL and target and target.controller == source:
card.cast_on_friendly_characters = True
if target.type == CardType.MINION:
card.cast_on_friendly_minions = True

source.game.manager.game_action(self, source, card, target, index, choose)
# NOTE: A Play is not a summon! But it sure looks like one.
Expand All @@ -518,7 +515,7 @@ def do(self, source, card, target, index, choose):

# "Can't Play" (aka Counter) means triggers don't happen either
if not card.cant_play:
if card.trigger_outcast and card.get_actions("outcast"):
if card.play_outcast and card.get_actions("outcast"):
source.game.trigger(card, card.get_actions("outcast"), event_args=None)
elif trigger_battlecry:
source.game.queue_actions(
Expand Down Expand Up @@ -908,8 +905,7 @@ class Predamage(TargetedAction):
AMOUNT = IntArg()

def do(self, source, target, amount):
for i in range(target.incoming_damage_multiplier):
amount *= 2
amount <<= target.incoming_damage_multiplier
target.predamage = amount
if amount:
self.broadcast(source, EventListener.ON, target, amount)
Expand All @@ -930,11 +926,14 @@ def do(self, source, target, cards):
if not isinstance(cards, list):
cards = [cards]

if cards:
target.shuffle_deck()

for card in cards:
if card.controller != target:
card.zone = Zone.SETASIDE
card.controller = target
if len(target.deck) >= target.max_deck_size:
if card.zone != Zone.DECK and len(target.deck) >= target.max_deck_size:
log.info("Put(%r) fails because %r's deck is full", card, target)
continue
card.zone = Zone.DECK
Expand Down Expand Up @@ -1086,11 +1085,6 @@ def has_extra_battlecries(self, player, card):
return False

def do(self, source, card, target=None):
if source.type == CardType.MINION and (
source.dead or source.silenced or source.zone != Zone.PLAY
):
return

if target is None:
old_requirements = source.requirements
source.requirements = card.requirements
Expand Down Expand Up @@ -1121,6 +1115,9 @@ class Destroy(TargetedAction):
"""

def do(self, source, target):
if getattr(target, "dormant", False):
log.info("%r is dormant cannot be destroyed", target)
return
if target.delayed_destruction:
# If the card is in PLAY, it is instead scheduled to be destroyed
# It will be moved to the graveyard on the next Death event
Expand Down Expand Up @@ -1541,7 +1538,7 @@ class FillMana(TargetedAction):
AMOUNT = IntArg()

def do(self, source, target, amount):
target.used_mana -= amount
target.used_mana = max(0, target.used_mana - amount)
source.game.manager.targeted_action(self, source, target, amount)


Expand Down Expand Up @@ -1736,30 +1733,35 @@ def get_summon_index(self, source_index):
return source_index + ((self.trigger_index + 1) % 2)


class SummonTiger(TargetedAction):
class SummonCustomMinion(TargetedAction):
"""
Summon a Tiger with stats equal to its Cost.
Summon custom minion with cost/atk/max_health
"""

TARGET = ActionArg()
CARD = CardArg()
COST = IntArg()
ATK = IntArg()
HEALTH = IntArg()

def do(self, source, target, cost):
if cost <= 0:
def do(self, source, target, cards, cost, atk, health):
if health <= 0:
return
tiger = target.controller.card("TRL_309t", source=source)
tiger.custom_card = True
if not isinstance(cards, list):
cards = [cards]
for card in cards:
card.custom_card = True

def create_custom_card(tiger):
tiger.atk = cost
tiger.max_health = cost
tiger.cost = cost
def create_custom_card(card):
card.cost = cost
card.atk = atk
card.max_health = health

tiger.create_custom_card = create_custom_card
tiger.create_custom_card(tiger)
card.create_custom_card = create_custom_card
card.create_custom_card(card)

if tiger.is_summonable():
source.game.queue_actions(source, [Summon(target, tiger)])
if card.is_summonable():
source.game.queue_actions(source, [Summon(target, card)])


class Shuffle(TargetedAction):
Expand Down Expand Up @@ -1899,12 +1901,10 @@ def get_target_args(self, source, target):
spell_target = ret[0]
return [spell_target]

def do(self, source, card, targets):
if source.type == CardType.MINION and (
source.dead or source.silenced or source.zone != Zone.PLAY
):
return
def choose_target(self, source, card):
return random.choice(card.targets)

def do(self, source, card, targets):
player = source.controller
old_choice = player.choice
player.choice = None
Expand All @@ -1917,7 +1917,7 @@ def do(self, source, card, targets):
if card.requires_target() and not target:
if len(card.targets) > 0:
if target not in card.targets:
target = random.choice(card.targets)
target = self.choose_target(source, card)
else:
log.info("%s cast spell %s don't have a legal target", source, card)
return
Expand All @@ -1935,7 +1935,17 @@ def do(self, source, card, targets):
log.info("Choosing card %r" % (choice))
player.opponent.choice.choose(choice)
player.choice = old_choice
source.game.queue_actions(source, [Deaths()])


class CastSpellTargetsEnemiesIfPossible(CastSpell):
def choose_target(self, source, card):
enemy_targets = []
for entity in card.targets:
if entity.controller == source.controller.opponent:
enemy_targets.append(entity)
if enemy_targets:
return random.choice(enemy_targets)
return random.choice(card.targets)


class Evolve(TargetedAction):
Expand All @@ -1951,7 +1961,7 @@ def do(self, source, target, amount):
card_set = RandomMinion(cost=cost).find_cards(source)
if card_set:
card = random.choice(card_set)
return source.game.queue_actions(source, [Morph(target, card)])
return source.game.queue_actions(source, [Morph(target, card)])[0]


class ExtraAttack(TargetedAction):
Expand Down Expand Up @@ -2228,3 +2238,25 @@ def do(self, source, player):
AddProgress(galakrond, source),
],
)


class Awaken(TargetedAction):
def do(self, source, target):
if not target.dormant:
return
target.dormant = False
target.turns_in_play = 0
source.game.manager.targeted_action(self, source, target)
actions = target.get_actions("awaken")
if actions:
source.game.trigger(target, actions, event_args=None)


class Dormant(TargetedAction):
TARGET = ActionArg()
AMOUNT = IntArg()

def do(self, source, target, amount):
target.dormant = True
target.dormant_turns += amount
source.game.manager.targeted_action(self, source, target, amount)
59 changes: 48 additions & 11 deletions fireplace/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import TYPE_CHECKING

from hearthstone.enums import (
CardClass,
CardType,
GameTag,
MultiClassGroup,
Expand Down Expand Up @@ -72,6 +73,8 @@ def __init__(self, data: "cardxml.CardXML"):
self._zone = Zone.INVALID
self._progress: int = 0
self.progress_total: int = data.scripts.progress_total
self.card_class = CardClass.INVALID
self.multi_class_group = MultiClassGroup.INVALID
self.tags.update(data.tags)

def dump(self):
Expand Down Expand Up @@ -193,10 +196,9 @@ def zone(self):

@property
def classes(self):
if hasattr(self, "multi_class_group"):
if self.multi_class_group != MultiClassGroup.INVALID:
return MultiClassGroup(self.multi_class_group).card_classes
else:
return [self.card_class]
return [self.card_class]

@zone.setter
def zone(self, value):
Expand Down Expand Up @@ -292,6 +294,7 @@ class PlayableCard(BaseCard, Entity, TargetableByAuras):
echo = boolean_property("echo")
has_overkill = boolean_property("has_overkill")
has_discover = boolean_property("has_discover")
libram = boolean_property("libram")

def __init__(self, data):
self.cant_play = False
Expand All @@ -304,6 +307,7 @@ def __init__(self, data):
self.morphed = None
self.turn_drawn = -1
self.turn_played = -1
self.cast_on_friendly_characters = False
self.cast_on_friendly_minions = False
self.play_left_most = False
self.play_right_most = False
Expand Down Expand Up @@ -382,7 +386,7 @@ def played_this_turn(self):
return self.turn_played == self.game.turn

@property
def trigger_outcast(self):
def play_outcast(self):
return self.play_left_most or self.play_right_most

@property
Expand Down Expand Up @@ -763,7 +767,7 @@ def dump(self):
data["atk"] = self.atk
data["max_health"] = self.max_health
data["damage"] = self.damage
data["immue"] = self.immune
data["immune"] = self.immune
return data

def _set_zone(self, zone):
Expand Down Expand Up @@ -888,6 +892,7 @@ def attack_targets(self):
targets = self.controller.opponent.field
if self.rush and not self.turns_in_play:
targets = self.controller.opponent.field
targets = targets.filter(dormant=False)

taunts = []
if not self.ignore_taunt:
Expand Down Expand Up @@ -1139,6 +1144,7 @@ def __init__(self, data):
self.silenced = False
self._summon_index = None
self.dormant = False
self.dormant_turns = data.scripts.dormant_turns
self.reborn = False
super().__init__(data)

Expand All @@ -1148,18 +1154,19 @@ def dump(self):
data["divine_shield"] = self.divine_shield
data["silenced"] = self.silenced
data["dormant"] = self.dormant
data["reborn"] = self.reborn
return data

@property
def ignore_scripts(self):
return self.silenced
return self.silenced or self.dormant

@property
def left_minion(self):
assert self.zone is Zone.PLAY, self.zone
ret = CardList()
index = self.zone_position - 1
left = self.controller.field[:index]
left = self.controller.field[:index].filter(dormant=False)
if left:
ret.append(left[-1])
return ret
Expand All @@ -1169,7 +1176,7 @@ def right_minion(self):
assert self.zone is Zone.PLAY, self.zone
ret = CardList()
index = self.zone_position - 1
right = self.controller.field[index + 1 :]
right = self.controller.field[index + 1 :].filter(dormant=False)
if right:
ret.append(right[0])
return ret
Expand All @@ -1194,6 +1201,12 @@ def asleep(self):
and (not self.charge and not self.rush)
)

@property
def events(self):
if self.dormant:
return self.data.scripts.dormant_events
return super().events

@property
def exhausted(self):
if self.asleep:
Expand Down Expand Up @@ -1316,7 +1329,21 @@ def dump_hidden(self):
if self.zone == Zone.SECRET:
data = super().dump_hidden()
data["type"] = int(CardType.SPELL)
data["name"] = "秘密"
data["cost"] = self.cost
if self.card_class == CardClass.MAGE:
data["id"] = "SECRET_MAGE"
data["name"] = "法师奥秘"
elif self.card_class == CardClass.HUNTER:
data["id"] = "SECRET_HUNTER"
data["name"] = "猎人奥秘"
elif self.card_class == CardClass.PALADIN:
data["id"] = "SECRET_PALADIN"
data["name"] = "圣骑士奥秘"
elif self.card_class == CardClass.ROGUE:
data["id"] = "SECRET_ROGUE"
data["name"] = "盗贼奥秘"
data["rarity"] = int(Rarity.INVALID)
data["description"] = "小心了!这张卡牌的效果在某个特殊情况下便会触发..."
data["spelltype"] = int(self.spelltype)
data["classes"] = [int(card_class) for card_class in self.classes]
return data
Expand Down Expand Up @@ -1552,10 +1579,10 @@ class HeroPower(PlayableCard):
steady_shot_can_target = boolean_property("steady_shot_can_target")

def __init__(self, data):
super().__init__(data)
self.activations_this_turn = 0
self.additional_activations_this_turn = 0
self.old_power = None
self._upgraded_hero_power = None
super().__init__(data)

def dump(self):
data = super().dump()
Expand Down Expand Up @@ -1583,6 +1610,16 @@ def update_scripts(self):
if not self.heropower_disabled:
yield from super().update_scripts

@property
def upgraded_hero_power(self):
if self._upgraded_hero_power:
return cards.db.dbf[self._upgraded_hero_power]
return None

@upgraded_hero_power.setter
def upgraded_hero_power(self, value):
self._upgraded_hero_power = value

def _set_zone(self, value):
if value == Zone.PLAY:
if self.controller.hero.power:
Expand Down
4 changes: 2 additions & 2 deletions fireplace/cards/CardDefs.xml
Git LFS file not shown
Loading

0 comments on commit 39c9721

Please sign in to comment.