Skip to content

Commit

Permalink
fix choice mulitimes bug
Browse files Browse the repository at this point in the history
  • Loading branch information
shinoi2 committed Dec 5, 2023
1 parent 35e2404 commit 4bff59d
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 97 deletions.
203 changes: 106 additions & 97 deletions fireplace/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def __init__(self, *args, **kwargs):
self.callback = ()
self.times = 1
self.event_queue = []
self.choice_callback = []

def __repr__(self):
args = ["%s=%r" % (k, v) for k, v in zip(self.ARGS, self._args)]
Expand Down Expand Up @@ -184,6 +185,12 @@ def matches(self, source, args):
return False
return True

def trigger_choice_callback(self):
callbacks = self.choice_callback
self.choice_callback = []
for callback in callbacks:
callback()


class GameAction(Action):
def trigger(self, source):
Expand Down Expand Up @@ -296,7 +303,7 @@ def do(self, source, target):
log.info("Processing Death for %r", target)
self.broadcast(source, EventListener.ON, target)
if target.deathrattles:
source.game.queue_actions(source, [Deathrattle(target)])
source.game.queue_actions(target.controller, [Deathrattle(target)])


class EndTurn(GameAction):
Expand Down Expand Up @@ -335,73 +342,6 @@ def do(self, source, challenger, defender):
source.game.joust(source, challenger, defender, self.callback)


class Choice(GameAction):
PLAYER = ActionArg()
CARDS = ActionArg()
CARD = ActionArg()

def get_args(self, source):
player = self._args[0]
if isinstance(player, Selector):
player = player.eval(source.game.players, source)
assert len(player) == 1
player = player[0]
cards = self._args[1]
if isinstance(cards, Selector):
cards = cards.eval(source.game, source)
elif isinstance(cards, LazyValue):
cards = cards.evaluate(source)
elif isinstance(cards, list):
eval_cards = []
for card in cards:
if isinstance(card, LazyValue):
eval_cards.append(card.evaluate(source)[0])
elif isinstance(card, str):
eval_cards.append(source.controller.card(card, source))
else:
eval_cards.append(card)
cards = eval_cards

return player, cards

def do(self, source, player, cards):
if len(cards) == 0:
return
log.info("%r choice from %r", player, cards)
self._callback = self.callback
self.callback = ()
self.next_choice = player.choice
player.choice = self
self.source = source
self.player = player
self.cards = cards
self.min_count = 1
self.max_count = 1

def choose(self, card):
if card not in self.cards:
raise InvalidAction("%r is not a valid choice (one of %r)" % (card, self.cards))
for action in self._callback:
self.source.game.trigger(
self.source, [action], [self.player, self.cards, card])
self.player.choice = self.next_choice


class GenericChoice(Choice):
def choose(self, card):
super().choose(card)
for _card in self.cards:
if _card is card:
if card.type == CardType.HERO_POWER:
_card.zone = Zone.PLAY
elif len(self.player.hand) < self.player.max_hand_size:
_card.zone = Zone.HAND
else:
_card.discard()
else:
_card.discard()


class MulliganChoice(GameAction):
PLAYER = ActionArg()

Expand All @@ -422,11 +362,12 @@ def do(self, source, player):
self.max_count = len(player.hand)

def choose(self, *cards):
self.player.draw(len(cards))
for card in cards:
assert card in self.cards
card.zone = Zone.DECK
self.player.choice = None
self.player.draw(len(cards))
for card in cards:
card.zone = Zone.DECK
self.player.shuffle_deck()
self.player.mulligan_state = Mulligan.DONE

Expand Down Expand Up @@ -623,24 +564,34 @@ def trigger(self, source):
times = times.trigger(source)[0]

for i in range(times):
self.trigger_index = i
args = self.get_args(source)
targets = self.get_targets(source, args[0])
args = args[1:]
log.info("%r triggering %r targeting %r", source, self, targets)
for target in targets:
target_args = self.get_target_args(source, target)
ret.append(self.do(source, target, *target_args))
source.game.manager.targeted_action(self, source, target, *target_args)

for action in self.callback:
log.info("%r queues up callback %r", self, action)
ret += source.game.queue_actions(source, [action], event_args=[target] + target_args)
ret += self._trigger(i, source)

self.resolve_broadcasts()

return ret

def _trigger(self, i, source):
if source.controller.choice:
self.choice_callback.append(
lambda: self._trigger(i, source)
)
return []
ret = []
self.trigger_index = i
args = self.get_args(source)
targets = self.get_targets(source, args[0])
args = args[1:]
log.info("%r triggering %r targeting %r", source, self, targets)
for target in targets:
target_args = self.get_target_args(source, target)
ret.append(self.do(source, target, *target_args))
source.game.manager.targeted_action(self, source, target, *target_args)

for action in self.callback:
log.info("%r queues up callback %r", self, action)
ret += source.game.queue_actions(source, [action], event_args=[target] + target_args)
return ret


class Buff(TargetedAction):
"""
Expand Down Expand Up @@ -698,6 +649,65 @@ def do(self, source, target):
target.zone = Zone.HAND


class Choice(TargetedAction):
CARDS = ActionArg()
CARD = ActionArg()

def get_target_args(self, source, target):
cards = self._args[1]
if isinstance(cards, Selector):
cards = cards.eval(source.game, source)
elif isinstance(cards, LazyValue):
cards = cards.evaluate(source)
elif isinstance(cards, list):
eval_cards = []
for card in cards:
if isinstance(card, LazyValue):
eval_cards.append(card.evaluate(source)[0])
elif isinstance(card, str):
eval_cards.append(source.controller.card(card, source))
else:
eval_cards.append(card)
cards = eval_cards

return [cards]

def do(self, source, player, cards):
if len(cards) == 0:
return
log.info("%r choice from %r", player, cards)
player.choice = self
self.source = source
self.player = player
self.cards = cards
self.min_count = 1
self.max_count = 1

def choose(self, card):
if card not in self.cards:
raise InvalidAction("%r is not a valid choice (one of %r)" % (card, self.cards))
self.player.choice = None
for action in self.callback:
self.source.game.trigger(
self.source, [action], [self.cards, card])
self.trigger_choice_callback()


class GenericChoice(Choice):
def choose(self, card):
super().choose(card)
for _card in self.cards:
if _card is card:
if card.type == CardType.HERO_POWER:
_card.zone = Zone.PLAY
elif len(self.player.hand) < self.player.max_hand_size:
_card.zone = Zone.HAND
else:
_card.discard()
else:
_card.discard()


class CopyDeathrattles(TargetedAction):
"""
Copy the deathrattles from a card onto the target
Expand Down Expand Up @@ -899,11 +909,8 @@ def get_target_args(self, source, target):

def do(self, source, target, cards):
log.info("%r discovers %r for %s", source, cards, target)
self._callback = self.callback
self.callback = ()
self.cards = cards
player = source.controller
self.next_choice = player.choice
player.choice = self
self.player = player
self.source = source
Expand All @@ -915,10 +922,11 @@ def do(self, source, target, cards):
def choose(self, card):
if card not in self.cards:
raise InvalidAction("%r is not a valid choice (one of %r)" % (card, self.cards))
self.player.choice = self.next_choice
for action in self._callback:
self.player.choice = None
for action in self.callback:
self.source.game.trigger(
self.source, [action], [self.target, self.cards, card])
self.trigger_choice_callback()


class Draw(TargetedAction):
Expand Down Expand Up @@ -1595,7 +1603,6 @@ def init(self):
def do(self, source, player):
self.init()
self.player = player
self.next_choice = self.player.choice
self.source = source
self.min_count = 1
self.max_count = 1
Expand Down Expand Up @@ -1642,8 +1649,9 @@ def choose(self, card):
elif len(self.choosed_cards) == 2:
self.do_step3()
elif len(self.choosed_cards) == 3:
self.player.choice = None
self.done()
self.player.choice = self.next_choice
self.trigger_choice_callback()


class Upgrade(TargetedAction):
Expand Down Expand Up @@ -1704,7 +1712,6 @@ def do(self, source, target, cards):
log.info("%r adapts %r for %s", source, cards, target)
self.cards = cards
player = source.controller
self.next_choice = player.choice
player.choice = self
self.player = player
self.source = source
Expand All @@ -1716,8 +1723,9 @@ def do(self, source, target, cards):
def choose(self, card):
if card not in self.cards:
raise InvalidAction("%r is not a valid choice (one of %r)" % (card, self.cards))
self.player.choice = None
self.source.game.trigger(self.source, (Battlecry(card, self.target), ), None)
self.player.choice = self.next_choice
self.trigger_choice_callback()


class AddProgress(TargetedAction):
Expand All @@ -1729,7 +1737,8 @@ def do(self, source, target, card):
target.add_progress(card)
if target.progress >= target.progress_total:
source.game.trigger(target, target.get_actions("reward"), event_args=None)
target.zone = Zone.GRAVEYARD
if target.data.quest:
target.zone = Zone.GRAVEYARD


class ClearProgress(TargetedAction):
Expand All @@ -1741,7 +1750,6 @@ def do(self, source, target):
class GlimmerrootAction(TargetedAction):
def do(self, source, player):
self.player = player
self.next_choice = self.player.choice
self.source = source
self.min_count = 1
self.max_count = 1
Expand Down Expand Up @@ -1780,7 +1788,8 @@ def choose(self, card):
card.zone = Zone.HAND
else:
log.info("Choose incorrectly, corrent choice is %r", self.starting_card)
self.player.choice = self.next_choice
self.player.choice = None
self.trigger_choice_callback()


class CreateZombeast(TargetedAction):
Expand All @@ -1803,7 +1812,6 @@ def init(self, source):
def do(self, source, player):
self.init(source)
self.player = player
self.next_choice = self.player.choice
self.source = source
self.min_count = 1
self.max_count = 1
Expand Down Expand Up @@ -1849,8 +1857,9 @@ def choose(self, card):
if len(self.choosed_cards) == 1:
self.do_step2()
elif len(self.choosed_cards) == 2:
self.player.choice = None
self.done()
self.player.choice = self.next_choice
self.trigger_choice_callback()


class LosesDivineShield(TargetedAction):
Expand Down
13 changes: 13 additions & 0 deletions tests/test_ungoro.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,16 @@ def test_molten_reflection():
wisp = game.player1.give(WISP).play()
game.player1.give("UNG_948").play(target=wisp)
assert len(game.player1.field) == 2


def test_volcanosaur():
game = prepare_game()
game.player1.give("LOE_077").play()
volcanosaur = game.player1.give("UNG_002").play()
for _ in range(4):
choice = game.player1.choice
assert choice
choice.choose(choice.cards[0])
choice = game.player1.choice
assert not choice
assert len(volcanosaur.buffs) == 4
2 changes: 2 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
"AT_130", # Sea Reaver
"KAR_096", # Prince Malchezaar
"CFM_637", # Patches the Pirate
"KAR_205", # Silverware Golem
"UNG_836", # Clutchmother Zavas
)

_draftcache = {}
Expand Down

0 comments on commit 4bff59d

Please sign in to comment.