Source code for pyscoundrel.models.weapon

"""Weapon model for PyScoundrel."""

from dataclasses import dataclass, field
from typing import List, Optional

from .card import Card, CardType


[docs] @dataclass class Weapon: """ An equipped weapon in Scoundrel. Weapons have a unique mechanic: once used on a monster, they can only be used on monsters of equal or lower value than the last monster killed. """ card: Card slain_monsters: List[Card] = field(default_factory=list) def __post_init__(self): """Validate that the card is actually a weapon.""" if self.card.card_type != CardType.WEAPON: raise ValueError(f"Card {self.card} is not a weapon!") @property def damage(self) -> int: """Get the weapon's damage value.""" return self.card.value @property def last_kill_value(self) -> Optional[int]: """ Get the value of the last monster killed with this weapon. Returns None if the weapon is unused. """ if not self.slain_monsters: return None return self.slain_monsters[-1].value @property def max_kill_value(self) -> Optional[int]: """ Get the maximum monster value this weapon can currently kill. After first use, weapons can only kill monsters <= last kill value. If unused, returns None (can kill any monster). """ return self.last_kill_value @property def is_used(self) -> bool: """Check if the weapon has been used to kill a monster.""" return len(self.slain_monsters) > 0
[docs] def can_kill(self, monster: Card) -> bool: """ Check if this weapon can be used against a monster. Args: monster: The monster card to check Returns: True if weapon can be used, False otherwise """ if monster.card_type != CardType.MONSTER: raise ValueError(f"Card {monster} is not a monster!") # Unused weapons can kill any monster if not self.is_used: return True # Used weapons can only kill monsters <= last kill value last_kill = self.last_kill_value return monster.value <= last_kill
[docs] def attack(self, monster: Card) -> int: """ Use the weapon to attack a monster. Args: monster: The monster to attack Returns: Damage taken by the player (monster damage - weapon damage, min 0) Raises: ValueError: If weapon cannot be used on this monster """ if not self.can_kill(monster): raise ValueError( f"Weapon {self.card} cannot kill {monster} (last kill: {self.last_kill_value})" ) # Calculate damage: monster value - weapon damage (min 0) damage_taken = max(0, monster.value - self.damage) # Record the kill self.slain_monsters.append(monster) return damage_taken
def __str__(self) -> str: if self.is_used: return f"{self.card.display_name} (max kill: {self.max_kill_value})" return f"{self.card.display_name} (unused)" def __repr__(self) -> str: return f"Weapon(card={self.card!r}, kills={len(self.slain_monsters)})"