diff --git a/game/combat.py b/game/combat.py index 78abc3e..eefab6c 100644 --- a/game/combat.py +++ b/game/combat.py @@ -8,7 +8,7 @@ from game.messages import MessageLog -@dataclass(slots=True) +@dataclass(eq=False, slots=True, kw_only=True) class Stats: # current hit points hp: int = field(init=False) @@ -18,15 +18,19 @@ class Stats: ac: int # hit dice (character level) hd: int - # damage die (unarmed strike) - dmg: int + # damage dice (unarmed strike) + base_dmg: tuple[int, int] = field(init=False) # xp value (current experience) xp: int # strength strength: int = 10 - def __post_init__(self) -> None: + # damage dice expression + dmg_dice: InitVar[str] + + def __post_init__(self, dmg_dice: str) -> None: self.hp = self.max_hp + self.base_dmg = parse_dice(dmg_dice) @dataclass(eq=False, slots=True, kw_only=True) @@ -73,7 +77,7 @@ def melee_attack(attacker: Actor, defender: Actor, level: Level, log: MessageLog thac0 = 21 - attacker.stats.hd hit = roll(1, d=20) + to_hit_bonus >= thac0 - armor_class if hit: - damage_dice = 1, attacker.stats.dmg + damage_dice = attacker.stats.base_dmg if isinstance(attacker, Player) and attacker.inventory.weapon_slot: damage_dice = attacker.inventory.weapon_slot.weapon.base_dmg damage = roll(*damage_dice) + damage_bonus diff --git a/game/game_loop.py b/game/game_loop.py index 31f5d7f..591b227 100644 --- a/game/game_loop.py +++ b/game/game_loop.py @@ -42,7 +42,7 @@ def new_game() -> tuple[Player, Level, MessageLog]: y=level.entry_y, glyph=Glyph.PLAYER, name="Rodney", - stats=Stats(max_hp=12, ac=10, hd=1, dmg=4, xp=0, strength=16), + stats=Stats(max_hp=12, ac=10, hd=1, dmg_dice='1d4', xp=0, strength=16), gold=0, inventory=inventory, ) diff --git a/game/monsters.py b/game/monsters.py index 2ba8ea6..8b239fb 100644 --- a/game/monsters.py +++ b/game/monsters.py @@ -1,6 +1,7 @@ from __future__ import annotations -from dataclasses import KW_ONLY, dataclass +from copy import deepcopy +from dataclasses import KW_ONLY, InitVar, dataclass, field from game.actor_ai import ActorAI, GreedyAI, IdleAI, MeanAI from game.combat import Stats @@ -14,46 +15,51 @@ class MonsterType: ch: str name: str _: KW_ONLY - hd: int - ac: int - dmg: int - xp_value: int + stats: Stats = field(init=False) ai: type[ActorAI] = MeanAI generate: bool = True + hd: InitVar[int] + ac: InitVar[int] + dmg_dice: InitVar[str] + xp_value: InitVar[int] + + def __post_init__(self, hd: int, ac: int, dmg_dice: str, xp_value: int) -> None: + object.__setattr__(self, 'stats', Stats(max_hp=0, hd=hd, ac=ac, dmg_dice=dmg_dice, xp=xp_value)) + def spawn(self, x: int, y: int) -> Actor: - return Actor(x=x, y=y, glyph=Glyph.MONSTER, char=self.ch, name=self.name, - stats=Stats(max_hp=roll(self.hd, d=8), hd=self.hd, ac=self.ac, dmg=self.dmg, xp=self.xp_value), - ai=self.ai()) + stats = deepcopy(self.stats) + stats.hp = stats.max_hp = roll(stats.hd, d=8) + return Actor(x=x, y=y, glyph=Glyph.MONSTER, char=self.ch, name=self.name, stats=stats, ai=self.ai()) monsters: list[MonsterType] = [ - MonsterType('K', "kobold", hd=1, ac=7, dmg=4, xp_value=1), - MonsterType('J', "jackal", hd=1, ac=7, dmg=2, xp_value=2), - MonsterType('B', "bat", hd=1, ac=3, dmg=2, xp_value=1, ai=IdleAI), - MonsterType('S', "snake", hd=1, ac=5, dmg=3, xp_value=2), - MonsterType('H', "hobgoblin", hd=1, ac=5, dmg=8, xp_value=3), - MonsterType('E', "floating eye", hd=1, ac=9, dmg=0, xp_value=5, ai=IdleAI, generate=False), - MonsterType('A', "giant ant", hd=2, ac=3, dmg=6, xp_value=9), - MonsterType('O', "orc", hd=1, ac=6, dmg=8, xp_value=5, ai=GreedyAI), - MonsterType('Z', "zombie", hd=2, ac=8, dmg=8, xp_value=6), - MonsterType('G', "gnome", hd=1, ac=5, dmg=6, xp_value=7, ai=IdleAI), - MonsterType('L', "leprechaun", hd=3, ac=8, dmg=1, xp_value=10, ai=IdleAI, generate=False), - MonsterType('C', "centaur", hd=4, ac=4, dmg=13, xp_value=15, ai=IdleAI), - MonsterType('R', "rust monster", hd=5, ac=2, dmg=0, xp_value=20, generate=False), - MonsterType('Q', "quasit", hd=3, ac=2, dmg=10, xp_value=32), - MonsterType('N', "nymph", hd=3, ac=9, dmg=0, xp_value=37, ai=IdleAI, generate=False), - MonsterType('Y', "yeti", hd=4, ac=6, dmg=13, xp_value=50, ai=IdleAI), - MonsterType('T', "troll", hd=6, ac=4, dmg=31, xp_value=120), - MonsterType('W', "wraith", hd=5, ac=4, dmg=6, xp_value=55, ai=IdleAI), - MonsterType('F', "violet fungi", hd=8, ac=3, dmg=0, xp_value=80, generate=False), - MonsterType('I', "invisible stalker", hd=8, ac=3, dmg=19, xp_value=120, ai=IdleAI, generate=False), - MonsterType('X', "xorn", hd=7, ac=-2, dmg=39, xp_value=190), - MonsterType('U', "umber hulk", hd=8, ac=2, dmg=41, xp_value=200), - MonsterType('M', "mimic", hd=7, ac=7, dmg=14, xp_value=100, ai=IdleAI, generate=False), - MonsterType('V', "vampire", hd=8, ac=1, dmg=10, xp_value=350), - MonsterType('P', "purple worm", hd=15, ac=6, dmg=35, xp_value=4000, ai=IdleAI), - MonsterType('D', "dragon", hd=10, ac=-1, dmg=50, xp_value=6800), + MonsterType('K', "kobold", hd=1, ac=7, dmg_dice='1d4', xp_value=1), + MonsterType('J', "jackal", hd=1, ac=7, dmg_dice='1d2', xp_value=2), + MonsterType('B', "bat", hd=1, ac=3, dmg_dice='1d2', xp_value=1, ai=IdleAI), + MonsterType('S', "snake", hd=1, ac=5, dmg_dice='1d3', xp_value=2), + MonsterType('H', "hobgoblin", hd=1, ac=5, dmg_dice='1d8', xp_value=3), + MonsterType('E', "floating eye", hd=1, ac=9, dmg_dice='0d0', xp_value=5, ai=IdleAI, generate=False), + MonsterType('A', "giant ant", hd=2, ac=3, dmg_dice='1d6', xp_value=9), + MonsterType('O', "orc", hd=1, ac=6, dmg_dice='1d8', xp_value=5, ai=GreedyAI), + MonsterType('Z', "zombie", hd=2, ac=8, dmg_dice='1d8', xp_value=6), + MonsterType('G', "gnome", hd=1, ac=5, dmg_dice='1d6', xp_value=7, ai=IdleAI), + MonsterType('L', "leprechaun", hd=3, ac=8, dmg_dice='1d1', xp_value=10, ai=IdleAI, generate=False), + MonsterType('C', "centaur", hd=4, ac=4, dmg_dice='2d6', xp_value=15, ai=IdleAI), + MonsterType('R', "rust monster", hd=5, ac=2, dmg_dice='0d0', xp_value=20, generate=False), + MonsterType('Q', "quasit", hd=3, ac=2, dmg_dice='1d10', xp_value=32), + MonsterType('N', "nymph", hd=3, ac=9, dmg_dice='0d0', xp_value=37, ai=IdleAI, generate=False), + MonsterType('Y', "yeti", hd=4, ac=6, dmg_dice='2d6', xp_value=50, ai=IdleAI), + MonsterType('T', "troll", hd=6, ac=4, dmg_dice='4d7', xp_value=120), + MonsterType('W', "wraith", hd=5, ac=4, dmg_dice='1d6', xp_value=55, ai=IdleAI), + MonsterType('F', "violet fungi", hd=8, ac=3, dmg_dice='0d0', xp_value=80, generate=False), + MonsterType('I', "invisible stalker", hd=8, ac=3, dmg_dice='4d4', xp_value=120, ai=IdleAI, generate=False), + MonsterType('X', "xorn", hd=7, ac=-2, dmg_dice='5d7', xp_value=190), + MonsterType('U', "umber hulk", hd=8, ac=2, dmg_dice='7d5', xp_value=200), + MonsterType('M', "mimic", hd=7, ac=7, dmg_dice='3d4', xp_value=100, ai=IdleAI, generate=False), + MonsterType('V', "vampire", hd=8, ac=1, dmg_dice='1d10', xp_value=350), + MonsterType('P', "purple worm", hd=15, ac=6, dmg_dice='4d8', xp_value=4000, ai=IdleAI), + MonsterType('D', "dragon", hd=10, ac=-1, dmg_dice='3d16', xp_value=6800), ]