matteoferla/DnD-battler

View on GitHub
DnD_battler/creature/_action.py

Summary

Maintainability
A
1 hr
Test Coverage
from ..victory import Victory
from ._adv_base import CreatureAdvBase
from typing import *

class CreatureAction(CreatureAdvBase):

    def ready(self):
        self.dodge = 0
        # there should be a few more.
        # conditions.

    def isalive(self):
        if self.hp > 0:
            return True

    def take_damage(self, points, damage_type='unspecified'):
        self.hp -= points
        self.log.debug(self.name + ' took ' + str(points) + ' of damage. Now on ' + str(self.hp) + ' hp.')
        self.assess_concentration(points / 2)

    def assess_concentration(self, dc) -> Union[None, bool]:
        if dc < 10:
            dc = 10
        if not self.concentrating:
            return None
        elif self[self.spellcasting_ability_name].roll() < dc:
                self.conc_fx()
                self.log.debug(self.name + ' has lost their concentration')
                return False
        else:
            return True

    def reset(self, hard=False):
        """
        Resets the creature back to health (a long rest). a hard reset resets its scores
        :param hard: bool, false keeps tallies
        :return: None
        """
        self.hp = self.starting_hp
        if self.concentrating:
            self.conc_fx()  # TODO this looks fishy
        self.healing_spells = self.starting_healing_spells
        if hard:
            self.tally = {'damage': 0, 'hp': 0, 'hits': 0, 'misses': 0, 'rounds': 0, 'healing_spells': 0, 'battles': 0,
                          'dead': 0}

    def check_advantage(self, opponent):
        adv = 0
        if opponent.dodge:
            adv += -1
        if (opponent.condition == 'netted') or (opponent.condition == 'restrained'):
            adv += 1
        # Per coding it is impossible that a netted creature attempts an attack.
        if (self.condition == 'netted') or (self.condition == 'restrained'):
            adv += -1
        return adv

    def net(self, opponent):
        self.alt_attack['attack'].advantage = self.check_advantage(opponent)
        if self.alt_attack['attack'].roll() >= opponent.armor.ac:
            opponent.condition = 'netted'
            self.tally['hits'] += 1
            self.log.debug(self.name + " netted " + opponent.name)
        else:
            self.tally['misses'] += 1

    def cast_barkskin(self):
        if self.concentrating == 0:
            self.temp = self.armor.ac
            self.armor.ac = 16
            self.concentrating = 1
        elif self.concentrating == 1:
            self.armor.ac = self.temp
            self.concentrating = 0

    def cast_nothing(self, state='activate'):  # Something isn't quite right if this is invoked.
        pass

    def heal(self, points):
        self.hp += points
        self.log.debug(self.name + ' was healed by ' + str(points) + '. Now on ' + str(self.hp) + ' hp.')

    def assess_wounded(self):
        targets = self.arena.find('bloodiest allies')
        if len(targets) > 0:
            weakling = targets[0]
            if weakling.starting_hp > (self.healing.num_faces[0] + self.healing.bonus + weakling.hp):
                self.log.debug(self.name + " wants to heal " + weakling.name)
                return weakling
            else:
                return 0
        else:
            raise ValueError('A dead man wants to heal folk')

    def cast_healing(self, weakling):
        if self.healing_spells > 0:
            weakling.heal(self.healing.roll())
            self.healing_spells -= 1

    def multiattack(self, assess=0):
        # todo fix the fact that it does all the attacks
        if assess:
            return 0  # the default
        for i in range(len(self.attacks)):
            try:
                opponent = self.arena.find(self.arena.target, self)[0]
            except IndexError:
                raise Victory()
            self.log.debug(f"{self.name} attacks {opponent.name} with {self.attacks[i].name}")
            # This was the hit method:
            #damage = self.attacks[i].attack(opponent.armor.ac, advantage=self.check_advantage(opponent))
            self.attacks[i]()  # deals damage

    # TODO
    def check_action(self, action):
        return getattr(self, action)(assess=1)

    # TODO
    def do_action(self, action):
        # do it.
        pass

    # TODO
    def TBA_act(self):
        if not self.arena.find('alive enemy'):
            raise Victory()
        x = {'nothing': 'cast_nothing'}
        choice = [self.check_action(x) for x in self.actions]
        best = sorted(choice.keys(), key=choice.get)[0]
        self.do_action(best)

    def act(self):
        if not self.arena.find('alive enemy'):
            raise Victory()
        # BONUS ACTION
        # heal  -healing word, a bonus action.
        if self.healing_spells > 0:
            weakling = self.assess_wounded()
            if weakling != 0:
                self.cast_healing(weakling)
        # Main action!
        economy = len(self.arena.find('allies')) > len(self.arena.find('opponents')) > 0
        # Buff?
        if self.condition == 'netted':
            # NOT-RAW: DC10 strength check or something equally easy for monsters
            self.log.debug(self.name + " freed himself from a net")
            self.condition = 'normal'
        elif self.buff_spells > 0 and self.concentrating == 0:
            self.conc_fx()
            self.log.debug(self.name + ' buffs up!')
            # greater action economy: waste opponent's turn.
        elif economy and self is self.arena.find('weakest allies')[0]:
            self.log.debug(self.name + " is dodging")
            self.dodge = 1
        elif economy and self.alt_attack['name'] == 'net':
            opponent = self.arena.find('fiersomest enemy alive', self)[0]
            if opponent.condition != 'netted':
                self.net(opponent)
            else:
                self.multiattack()
        else:
            self.multiattack()