diff --git a/src/model/card.py b/src/model/card.py index c2ae0aa..cf5395d 100644 --- a/src/model/card.py +++ b/src/model/card.py @@ -10,7 +10,7 @@ class Effect(Serializable): effect: CardEffects amount: int sub_effects: List["Effect"] - mutex: List["Effects"] + mutex: List["Effect"] effect_type: Optional[EffectType] = None times: Optional[EffectTimes] = None @@ -21,15 +21,18 @@ class Effect(Serializable): sub_effects: Optional[List["Effect"]] = None, effect_type: Optional[EffectType] = None, times: Optional[EffectTimes] = None, + mutex: Optional[List["Effect"]] = None, ): super().__init__() - self.effect = effect - self.amount = amount + if mutex is None: + mutex = [] if sub_effects is None: sub_effects = [] + self.effect = effect + self.amount = amount self.sub_effects = sub_effects self.effect_type = effect_type - self.mutex: List["Effect"] = [] + self.mutex = mutex self.times = times def serialize(self) -> dict: @@ -49,7 +52,6 @@ class Card(Serializable): cost: int | None role: CardRole card_type: CardType - card_role: CardRole faction: CardFaction effects: List[Effect] defense: Optional[int] diff --git a/src/model/card_loader.py b/src/model/card_loader.py index 476df48..5653fe6 100644 --- a/src/model/card_loader.py +++ b/src/model/card_loader.py @@ -1,7 +1,7 @@ from json import JSONDecodeError from pathlib import Path from pydantic import BaseModel, ValidationError -from typing import List +from typing import List, Dict from model.card_validator import CardParser, EffectParser from model.card import Effect, Card @@ -44,15 +44,39 @@ def create_card(card: CardParser, effects: List[Effect]) -> Card: def create_card_effects(card: CardParser) -> List[Effect]: combined: List[Effect] = [] + mutexes: Dict[int, List[Effect]] = {} + for mutex in set([c.mutex for c in card.effects if c.mutex is not None]): + mutexes[mutex] = [] + for effect in card.effects: - combined.append(create_effect(effect)) + real_effect = create_effect(effect) + combined.append(real_effect) + if effect.mutex in mutexes: + mutexes[effect.mutex].append(real_effect) + + for index, effect in enumerate(card.effects): + if effect.mutex in mutexes: + combined[index].mutex = mutexes[effect.mutex] + return combined def create_effect(effect: EffectParser) -> Effect: sub_effects: List[Effect] = [] + mutexes: Dict[int, List[Effect]] = {} + for mutex in set([c.mutex for c in effect.sub_effects if c.mutex is not None]): + mutexes[mutex] = [] + for s in effect.sub_effects: - sub_effects.append(create_effect(s)) + real_effect = create_effect(s) + sub_effects.append(real_effect) + if s.mutex in mutexes: + mutexes[s.mutex].append(real_effect) + + for index, effect in enumerate(effect.sub_effects): + if effect.mutex in mutexes: + sub_effects[index].mutex = mutexes[effect.mutex] + return Effect( effect=effect.effect, amount=effect.amount, diff --git a/src/model/player.py b/src/model/player.py index a0b90cb..8163032 100644 --- a/src/model/player.py +++ b/src/model/player.py @@ -181,8 +181,8 @@ class Player(Serializable): return True async def sacrifice_card(self, card_id: str) -> bool: - board_card = self.source_card(card_id, self.board) - if board_card is None: + card = self.source_card(card_id, self.board) + if card is None: card = self.source_card(card_id, self.discard_pile) if card is None: print("Card not found in board or hand or discard") @@ -260,8 +260,8 @@ class Player(Serializable): await p.notify_update() return True - print("Cannot stun : card not found") - return False + print("Cannot stun : card not found") + return False async def make_enemy_discard(self, player_id: str) -> bool: p = next( diff --git a/src/model/turn.py b/src/model/turn.py index 409b75b..72fcf96 100644 --- a/src/model/turn.py +++ b/src/model/turn.py @@ -2,7 +2,7 @@ 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 typing import TYPE_CHECKING, List, Dict, Optional, Callable from uuid import UUID from model import ( Serializable, @@ -114,7 +114,7 @@ class Turn(Serializable): 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]): + 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( @@ -128,51 +128,81 @@ class Turn(Serializable): (m for m in self.player.board if effect in m.effects), None, ) - if card is None: + if card is not 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" - ) + # 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: + self.effects_availables.append(e) + 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 + self.gold_reserve += effect.amount success = True case CardEffects.DAMAGE: - self.damage_reserve += 1 + self.damage_reserve += effect.amount success = True case CardEffects.HEAL: - self.heal_reserve += 1 + self.heal_reserve += effect.amount success = True case CardEffects.PERPARE: - success = await self.prepare(target) + success = self.prepare(target) case CardEffects.STUN: success = await self.player.stun(target) case CardEffects.SACRIFICE: @@ -198,15 +228,7 @@ class Turn(Serializable): 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()) + return success def prepare(self, card_id: str) -> bool: card = next( @@ -280,3 +302,30 @@ class Turn(Serializable): 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()) + + async def damage(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.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())