This repository has been archived on 2024-07-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
back-python/src/model/turn.py
T
2024-04-26 16:07:57 +02:00

382 lines
15 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, Optional, Callable
from uuid import UUID
from model import (
Serializable,
CardType,
Card,
Effect,
CardEffects,
EffectType,
EffectTimes,
Team,
)
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[str, 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_OTHER_CARD_OF_SAME_FACTION:
for c in self.player.board:
if c.faction == card.faction and c != card:
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())
async 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")
self.discard_markers -= 1
await self.player.game.notify_controller("update", self.serialize())
return await 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.sacrifice_card(card_id)
async def use_effect_with_check(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 not None:
# TODO: Could break for subEffects
# Maybe store a reference to card in effect to fix this
# Also breaks for Sacrifice effects
match effect.effect_type:
case EffectType.SUICIDE:
for e in card.effects:
if e != effect:
while e in self.effects_availables:
self.effects_availables.remove(e)
for se in e.sub_effects:
if se != effect:
while se in self.effects_availables:
self.effects_availables.remove(se)
await self.player.sacrifice_card(str(card.uuid))
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 EffectType.CHAMPION_ACTION:
pass
case _:
raise NotImplementedError(
"EffectType " + effect.effect_type + " not implemented"
)
else:
print(
"Something went wrong while finding the card parent of an effect\nAre you using an effect from an already used card ?"
)
success = await self.use_effect(effect_id, target)
if not success:
return print("something went wrong while executing effect")
## Remove effects for OR Conditions
if effect.mutex is not None and len(effect.mutex) > 0:
for e in effect.mutex:
if e != effect:
while e in self.effects_availables:
self.effects_availables.remove(e)
self.effects_availables.remove(effect)
for e in effect.sub_effects:
for _ in range(e.amount):
self.effects_availables.append(e)
print([str(e.uuid) for e in self.effects_availables])
await self.player.game.notify_controller("update", self.serialize())
async def use_effect(self, effect_id: str, target: str):
effect = next(
(e for e in self.effects_availables if str(e.uuid) == effect_id),
None,
)
if effect is None:
print("effect couldn't found!")
return False
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 = 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 success
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
async def start_turn(self) -> None:
self.champions_health = {}
for c in self.player.board:
if c.card_type == CardType.CHAMPION:
self.champions_health[str(c.uuid)] = c.defense
await self.player.game.notify_controller("update", self.serialize())
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(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
async def heal(self, amount: int):
while amount > 0:
if self.heal_reserve <= 0:
return print("You cannot heal if your heal_reserve is empty")
self.player.team.health += 1
self.heal_reserve -= 1
amount -= 1
await self.player.game.notify_controller("update", self.serialize())
await self.player.game.notify_controller("update", self.player.team.serialize())
def check_for_guard(self, team: "Team"):
for p in self.player.game.players:
if p.team != team:
continue
for c in p.board:
if c.guard == True:
return True
return False
async def damage_team(self, target: str):
team = next(
(t for t in self.player.game.teams if str(t.uuid) == target),
None,
)
if team is None:
return print("team not found")
if team == self.player.team:
return print("KYS!")
if self.check_for_guard(team):
return print("You cannot attack this team if a guard is present")
if self.damage_reserve > 0:
self.damage_reserve -= 1
team.health -= 1
await self.player.game.notify_controller("update", self.serialize())
await self.player.game.notify_controller("update", team.serialize())
async def damage_champion(self, target: str):
if self.damage_reserve <= 0:
print("No damage left to attack")
return
for p in self.player.game.players:
if self.player.team == p.team:
continue
for c in p.board:
if str(c.uuid) != target:
continue
if c.card_type != CardType.CHAMPION:
continue
if not c.guard and self.check_for_guard(p.team):
print("You cannot attack this champion if a guard is present")
return
self.damage_reserve -= 1
p.turn.champions_health[str(c.uuid)] -= 1
if p.turn.champions_health[str(c.uuid)] <= 0:
await self.player.stun(c.uuid)
await self.player.game.notify_controller("update", self.serialize())
await self.player.game.notify_controller("update", p.turn.serialize())
return
print("card not found")