Archived
283 lines
11 KiB
Python
283 lines
11 KiB
Python
"""Stores which effects have been locked or used in a turn
|
|
Checks the actions of the user and prevent the user to do something illegal
|
|
"""
|
|
|
|
from typing import TYPE_CHECKING, List, Dict, cast, Optional, Callable
|
|
from uuid import UUID
|
|
from model import (
|
|
Serializable,
|
|
CardType,
|
|
Card,
|
|
Effect,
|
|
CardEffects,
|
|
EffectType,
|
|
EffectTimes,
|
|
)
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from model import Player
|
|
|
|
|
|
class Turn(Serializable):
|
|
"""Stores which effects have been locked or used in a turn
|
|
Checks the actions of the user and prevent the user to do something illegal"""
|
|
|
|
object_type = "turn"
|
|
|
|
player: "Player"
|
|
cards_locked: List[Card]
|
|
effects_availables: List[Effect]
|
|
champions_health: Dict[UUID, int]
|
|
discard_markers: int
|
|
damage_reserve: int
|
|
heal_reserve: int
|
|
gold_reserve: int
|
|
|
|
def __init__(self, player: "Player") -> None:
|
|
super().__init__()
|
|
self.player = player
|
|
self.damage_reserve = 0
|
|
self.heal_reserve = 0
|
|
self.gold_reserve = 0
|
|
self.champions_health: Dict[UUID, int] = {}
|
|
self.effects_availables: List[Effect] = []
|
|
self.cards_locked: List[Card] = []
|
|
self.discard_markers = 0
|
|
|
|
async def end_turn(self) -> None:
|
|
"""Resets card champion health and card effects"""
|
|
if len(self.player.hand) > 0:
|
|
return print("You must play all your cards before ending a turn!")
|
|
|
|
self.damage_reserve = 0
|
|
self.heal_reserve = 0
|
|
self.gold_reserve = 0
|
|
self.effects_availables = []
|
|
self.cards_locked = []
|
|
self.discard_markers = 0
|
|
await self.player.game.notify_controller("update", self.serialize())
|
|
await self.player.end_turn()
|
|
|
|
async def lock_card(self, card_id: str):
|
|
if self.discard_markers > 0:
|
|
return print("You cannot lock a card while having discard markers")
|
|
card = self.player.game.search_card_in_pile(card_id, self.player.board)
|
|
if card is None:
|
|
return print("Couldn't find card in player board")
|
|
if card in self.cards_locked:
|
|
return print("Card already locked")
|
|
self.cards_locked.append(card)
|
|
for effect in card.effects:
|
|
for _ in range(effect.amount):
|
|
match effect.times:
|
|
case EffectTimes.PER_CARD_OF_SAME_FACTION:
|
|
for c in self.player.board:
|
|
if c.faction == card.faction:
|
|
self.effects_availables.append(effect)
|
|
case EffectTimes.PER_CHAMPION:
|
|
for c in self.player.board:
|
|
if c.card_role == CardType.CHAMPION:
|
|
self.effects_availables.append(effect)
|
|
case EffectTimes.PER_OTHER_CHAMPION:
|
|
for c in self.player.board:
|
|
if c.card_role == CardType.CHAMPION and c != card:
|
|
self.effects_availables.append(effect)
|
|
case EffectTimes.PER_OTHER_GUARD:
|
|
for c in self.player.board:
|
|
if (
|
|
c.card_role == CardType.CHAMPION
|
|
and c.guard is True
|
|
and c != card
|
|
):
|
|
self.effects_availables.append(effect)
|
|
case None:
|
|
self.effects_availables.append(effect)
|
|
case _:
|
|
raise NotImplementedError(
|
|
"EffectTimes " + effect.times + " not implemented"
|
|
)
|
|
|
|
await self.player.game.notify_controller("update", self.serialize())
|
|
|
|
def discard_card(self, card_id: str):
|
|
if self.discard_markers <= 0:
|
|
return print("You don't have any discard markers!")
|
|
card = self.player.game.search_card_in_pile(card_id, self.cards_locked)
|
|
if card is not None:
|
|
return print("Cannot discard: card is locked")
|
|
return self.player.discard_card(card_id)
|
|
|
|
def sacrifice_card(self, card_id: str):
|
|
card = self.player.game.search_card_in_pile(card_id, self.cards_locked)
|
|
if card is not None:
|
|
return print("Cannot sacrifice : card is locked")
|
|
return self.player.discard_card(card_id)
|
|
|
|
async def use_effect(self, effect_id: str, target: Optional[str]):
|
|
if self.discard_markers > 0:
|
|
return print("You cannot use an effect while having discard markers")
|
|
effect = next(
|
|
(e for e in self.effects_availables if str(e.uuid) == effect_id),
|
|
None,
|
|
)
|
|
if effect is None:
|
|
return print("Effect not available")
|
|
|
|
card = next(
|
|
(m for m in self.player.board if effect in m.effects),
|
|
None,
|
|
)
|
|
if card is None:
|
|
# TODO: Could break for subEffects
|
|
# Maybe store a reference to card in effect to fix this
|
|
return print(
|
|
"Something went wrong while finding the card parent of an effect"
|
|
)
|
|
match effect.effect_type:
|
|
case EffectType.SUICIDE:
|
|
success = False
|
|
success = await self.player.sacrifice_card(str(card.uuid))
|
|
if not success:
|
|
return print("something went wrong while trying to suicide a card")
|
|
for e in card.effects:
|
|
if e != effect:
|
|
self.effects_availables.remove(e)
|
|
for se in e.sub_effects:
|
|
if se != effect:
|
|
self.effects_availables.remove(se)
|
|
case EffectType.FACTION_COMBO:
|
|
if (
|
|
len([c for c in self.player.board if c.faction == card.faction])
|
|
<= 1
|
|
):
|
|
return print("effect cannot run : faction combo not reached")
|
|
case None:
|
|
pass
|
|
case _:
|
|
raise NotImplementedError(
|
|
"EffectType " + effect.effect_type + " not implemented"
|
|
)
|
|
|
|
success = False
|
|
|
|
match effect.effect:
|
|
case CardEffects.GOLD:
|
|
self.gold_reserve += 1
|
|
success = True
|
|
case CardEffects.DAMAGE:
|
|
self.damage_reserve += 1
|
|
success = True
|
|
case CardEffects.HEAL:
|
|
self.heal_reserve += 1
|
|
success = True
|
|
case CardEffects.PERPARE:
|
|
success = await self.prepare(target)
|
|
case CardEffects.STUN:
|
|
success = await self.player.stun(target)
|
|
case CardEffects.SACRIFICE:
|
|
success = await self.sacrifice_card(target)
|
|
case CardEffects.DRAW:
|
|
success = await self.player.draw(1)
|
|
case CardEffects.DRAW_AND_DISCARD:
|
|
self.discard_markers += 1
|
|
success = await self.player.draw(1)
|
|
case CardEffects.DISCARD:
|
|
success = await self.player.make_enemy_discard(target)
|
|
case CardEffects.RESTACK_DISCARDED_CHAMPION:
|
|
success = await self.restack_discarded_champion(target)
|
|
case CardEffects.RESTACK_DISCARDED_CARD:
|
|
success = await self.player.put_on_stack_from_discard(target)
|
|
case CardEffects.STACK_NEXT_ACTION_BOUGHT:
|
|
success = await self.buy_card(target, self.player.buy_on_stack)
|
|
case CardEffects.STACK_NEXT_CARD_BOUGHT:
|
|
success = await self.buy_card(target, self.stack_next_card_bought_check)
|
|
case CardEffects.PLAY_NEXT_CARD_BOUGHT:
|
|
success = await self.buy_card(target, self.player.buy_in_hand)
|
|
case _:
|
|
raise NotImplementedError(
|
|
"CardEffect " + effect.effect + " not implemented"
|
|
)
|
|
return
|
|
|
|
if not success:
|
|
return print("something went wrong while executing effect")
|
|
|
|
self.effects_availables.remove(effect)
|
|
for e in effect.sub_effects:
|
|
self.effects_availables.append(e)
|
|
await self.player.game.notify_controller("update", self.serialize())
|
|
|
|
def prepare(self, card_id: str) -> bool:
|
|
card = next(
|
|
(e for e in self.cards_locked if str(e.uuid) == card_id),
|
|
None,
|
|
)
|
|
if card is None:
|
|
print("Card is not locked")
|
|
return False
|
|
for e in card.effects:
|
|
if e.effect_type != EffectType.CHAMPION_ACTION:
|
|
continue
|
|
if e in self.effects_availables:
|
|
continue
|
|
self.effects_availables.append(e)
|
|
return True
|
|
|
|
def start_turn(self) -> None:
|
|
for c in self.player.board:
|
|
if c.card_type == CardType.CHAMPION:
|
|
self.champions_health[c.uuid] = c.defense
|
|
|
|
def serialize(self):
|
|
data = super().serialize() | {
|
|
"cards_locked": [card.uuid for card in self.cards_locked],
|
|
"effects_availables": [effect.uuid for effect in self.effects_availables],
|
|
}
|
|
|
|
return {k: data[k] for k in data.keys() - ["player"]}
|
|
|
|
async def restack_discarded_champion(self, card_id) -> bool:
|
|
card = self.player.game.search_card_in_pile(card_id, self.player.discard_pile)
|
|
if card is None:
|
|
print("card not found in discard")
|
|
return False
|
|
if card.card_type != CardType.CHAMPION:
|
|
print("card is not a champion")
|
|
return False
|
|
else:
|
|
return await self.player.put_on_stack_from_discard(self, card)
|
|
|
|
async def stack_next_card_bought_check(self, card: Card) -> bool:
|
|
|
|
if self.gold_reserve < card.cost:
|
|
print("Not enough gold to buy card!")
|
|
return False
|
|
|
|
return await self.player.buy_on_stack(card)
|
|
|
|
async def buy_card(
|
|
self, card_id: str, buy_function: Optional[Callable[[Card], bool]] = None
|
|
):
|
|
if buy_function is None:
|
|
buy_function = self.player.buy_card
|
|
card = self.player.game.search_card_in_pile(card_id, self.player.game.market)
|
|
if card is None:
|
|
card = self.player.game.search_card_in_pile(
|
|
card_id, self.player.game.gem_stack
|
|
)
|
|
if card is None:
|
|
print("Card not found in market or gem_stack")
|
|
return False
|
|
|
|
if self.gold_reserve < card.cost:
|
|
print("Not enough gold to buy card!")
|
|
return False
|
|
|
|
if await buy_function(card):
|
|
self.gold_reserve -= card.cost
|
|
await self.player.game.notify_controller("update", self.serialize())
|
|
return True
|
|
|
|
return False
|