Skip to content

Commit

Permalink
Add multiattack feature to monsters
Browse files Browse the repository at this point in the history
  • Loading branch information
leomartius committed Oct 22, 2024
1 parent 6f178ff commit d6ecec8
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 24 deletions.
26 changes: 15 additions & 11 deletions game/combat.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Stats:
# hit dice (character level)
hd: int
# damage dice (unarmed strike)
base_dmg: tuple[int, int] = field(init=False)
base_dmg: list[tuple[int, int]] = field(init=False)
# xp value (current experience)
xp: int
# strength
Expand Down Expand Up @@ -49,7 +49,7 @@ def ac(self) -> int:
@dataclass(eq=False, slots=True, kw_only=True)
class Weapon:
# weapon damage dice
base_dmg: tuple[int, int] = field(init=False)
base_dmg: list[tuple[int, int]] = field(init=False)
# to hit bonus
plus_hit: int = 0
# damage bonus
Expand All @@ -64,8 +64,10 @@ def __post_init__(self, dmg_dice: str) -> None:

def melee_attack(attacker: Actor, defender: Actor, level: Level, log: MessageLog) -> None:
assert isinstance(attacker, Player) != isinstance(defender, Player)
damage_dice = attacker.stats.base_dmg
to_hit_bonus, damage_bonus = strength_bonuses(attacker)
if isinstance(attacker, Player) and attacker.inventory.weapon_slot:
damage_dice = attacker.inventory.weapon_slot.weapon.base_dmg
to_hit_bonus += attacker.inventory.weapon_slot.weapon.plus_hit
damage_bonus += attacker.inventory.weapon_slot.weapon.plus_dmg
if defender.ai and defender.ai.is_helpless():
Expand All @@ -75,15 +77,17 @@ def melee_attack(attacker: Actor, defender: Actor, level: Level, log: MessageLog
armor_class = defender.inventory.armor_slot.armor.ac
if defender.ai:
defender.ai.on_attacked(defender)
thac0 = 21 - attacker.stats.hd
hit = roll(1, d=20) + to_hit_bonus >= thac0 - armor_class
if hit:
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
damage = max(0, damage)
defender.stats.hp = max(0, defender.stats.hp - damage)
action_hit = False
action_dmg = 0
for n, d in damage_dice:
thac0 = 21 - attacker.stats.hd
attack_hit = roll(1, d=20) + to_hit_bonus >= thac0 - armor_class
if attack_hit:
attack_dmg = roll(n, d) + damage_bonus
action_dmg += max(0, attack_dmg)
action_hit = True
if action_hit:
defender.stats.hp = max(0, defender.stats.hp - action_dmg)
log.append(hit_message(attacker, defender))
if defender.stats.hp == 0:
level.entities.remove(defender)
Expand Down
11 changes: 7 additions & 4 deletions game/dice.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ def percent(p: int) -> bool:
return random.randrange(100) < p


def parse_dice(dice: str) -> tuple[int, int]:
assert re.match(r"^\d+d\d+$", dice)
n, d = dice.split('d')
return int(n), int(d)
def parse_dice(expression: str) -> list[tuple[int, int]]:
assert re.match(r"^\d+d\d+(/\d+d\d+)*$", expression)
result = []
for dice in expression.split('/'):
n, d = dice.split('d')
result.append((int(n), int(d)))
return result
18 changes: 9 additions & 9 deletions game/monsters.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,21 @@ def spawn(self, x: int, y: int, extra_hd: int) -> Actor:
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('C', "centaur", hd=4, ac=4, dmg_dice='1d6/1d6', xp_value=15, ai=IdleAI),
MonsterType('R', "rust monster", hd=5, ac=2, dmg_dice='0d0/0d0', xp_value=20, generate=False),
MonsterType('Q', "quasit", hd=3, ac=2, dmg_dice='1d2/1d2/1d4', 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('Y', "yeti", hd=4, ac=6, dmg_dice='1d6/1d6', xp_value=50, ai=IdleAI),
MonsterType('T', "troll", hd=6, ac=4, dmg_dice='1d8/1d8/2d6', 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('X', "xorn", hd=7, ac=-2, dmg_dice='1d3/1d3/1d3/4d6', xp_value=190),
MonsterType('U', "umber hulk", hd=8, ac=2, dmg_dice='3d4/3d4/2d5', 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),
MonsterType('P', "purple worm", hd=15, ac=6, dmg_dice='2d12/2d4', xp_value=4000, ai=IdleAI),
MonsterType('D', "dragon", hd=10, ac=-1, dmg_dice='1d8/1d8/3d10', xp_value=6800),
]


Expand Down

0 comments on commit d6ecec8

Please sign in to comment.