Home > Mobile >  Classes and Objects: How do I set a boolean from a method to create "chance" with a poison
Classes and Objects: How do I set a boolean from a method to create "chance" with a poison

Time:01-30

How do I set a boolean from a method to create "chance" with a poison status, or use later for dodge and other reusable.

Just now learning Simple Inheritance with Super() and not yet gotten to Complex Inheritance. Not sure if that would make a difference in how I could of coded this but using what I know, here is what I have.

What I am trying to do is create a method inside the Hero Class, and make the hero only be poisoned if a condition is met or a boolean is passed. I keep getting an issue with the logic. Still new to programming.

I've re-written the code multiple times using Idle for speed and it just keeps giving me problems, even when i place the for-loop inside the hero.PoisionedCheck(True) I only get the displayed text and not the forloop activated. Then I will get an error that "amount" parameter is not defined or called before assignment, then when I fix that I will get that scorpion.health and scorpion.energy is not defined. I'm running in circles.

Thank you for reading.

import time

class Monster:
    name= 'Monster'

    def __init__(self,health, energy):
        self.health = health
        self.energy = energy

    def attack(self, amount):
            print('Monster has attacked')
            print(f'{amount} damage was delt!')
            self.energy -= 20
        
    def move(self,speed):
            print(f'{self.name} has moved, at {speed} speed')

class Scorpion(Monster):
    
    name='Scorpion'
    def __init__(self,  health, energy):
        super().__init__(health, energy)
    
    def attack(self, amount):
        print('Scorpion has attacked')
        print('Poison has take effect!')
    
        # Loops Poison display repeated with time delay.
        # for repeater amount of times.
        repeater = 5
        for poison in range(repeater):
            poison = 0
            amount = poison   amount
            print(f'Poisoned: {amount} damage')
            time.sleep(0.5)
    
        poison = repeater
        amount *= poison
        print(f'Poison did a total damage of {amount} to {hero.name}')
        hero.health -= amount
        

class Hero(Monster):
    name = 'Rockwood'

    def __init__(self, health, energy) -> None:
        self.health = health
        self.energy = energy
    
    # How do I use this in a boolean to check Scorpion attack?
    # Lets say later adding *import random* later to determine chances of poison.
    # def PoisonedCheck(self, posioned):
    #     self.poisoned = posioned

    #     if posioned == True:
    #         print("Testing if become posioned- For loop goes after here to create damage.")
    #         posioned = True
        
    #     else:
    #         print('Became else.. ')
    #         posioned = False
        


monster = Monster(health=100, energy=100)
scorpion = Scorpion(health = 200, energy = 150)
hero = Hero(health=100, energy=100)

scorpion.attack(3)
print()

scorpion.move(110)
hero.move(50)
print(f"{hero.name} has {hero.health} health remaining. ")
print()

CodePudding user response:

To be honest, I am not really sure what you are trying to achieve, hopefully this answers your question or gets you going.

Firstly I would suggest adding a target parameter to Scorpion's attack behaviour and modifying it's behaviour. That way you can define multiple heroes and have them combat the scorpion, for example:

scorpion = Scorpion(health = 200, energy = 150)
hero_one = Hero(health=100, energy=100)
hero_two = Hero(health=200, energy=100)

scorpion.attack(hero_one)
scorpion.attack(hero_two)

Secondly, Scorpion doesn't seem to lose energy after attacking and doesn't deal initial damage. You could fix that by changing Scorpion attack behaviour to first call parent attack function:

class Scorpion(Monster):
...
    def attack(self, amount, target):
        super().attack(amount, target)
        print('Poison has take effect!')
        ...

But Monster attack behaviour doesn't accept second argument, so my suggestion is to modify Monster's attack function with second parameter to also deal initial damage to target (and correctly subtract target's health with damage amount):

class Monster:
...
    def attack(self, amount, target):
        print(f'{self.name} has attacked')
        print(f'{amount} damage was delt!')
        target.health -= amount
        self.energy -= 20

To track poisoned status, easiest you can do is to introduce state in Monster class constructor called poisoned and set it to False (initial state is that monster and every child object is not poisoned).

Then you can change target's poisoned state in Scorpion's attack method, like so:

class Scorpion(Monster):
...
    def attack(self, amount, target):
        ...
        target.poisoned = True
        ...

This way you can see if one of Heroes is poisoned, like so:

  print(hero_one.poisoned) --> False or True (if attacked by scorpion)

To calculate a chance (lets say 50%) if Scorpion's attack will poison the target or not, extract poison application behaviour into separate private function and modify it like this:

class Scorpion(Monster):
...
def attack(self, amount, target):
    super().attack(amount, target)
    self.__poison_target(amount, target)


def __poison_target(self, amount, target):
    # A coin toss to decide if target will be poisoned or not.
    should_apply_poison = random.randint(0, 1) == 1
    if should_apply_poison:
        print('Poison has take effect!')
        target.poisoned = True
        # Loops Poison display repeated with time delay.
        # for repeater amount of times.
        repeater = 5
        for poison in range(repeater):
            poison = 0
            amount = poison   amount
            print(f'Poisoned: {amount} damage')
            time.sleep(0.5)

        poison = repeater
        amount *= poison
        print(f'Poison did a total damage of {amount} to {target.name}')
        target.health -= amount
 ...

Or you can delegate damage dealing behaviour from Scorpion to Hero by creating abstract class Poisonable which you can use to check if the target is Poisonable (in your case - Hero inherits from Poisonable). This way Scorpion class doesn't care if target is successfuly poisoned or not.

Poisonable class is contract which all classes that inherit from it must abide by (e.g. implement their own version of resistance, damage taking from poison). This way every poisoned creature can react differently.

class Poison:
    def __init__(self):
        self.damage_per_tick = 5
        self.ticks = 3

class Scorpion(Monster):
    name = 'Scorpion'

    def __init__(self, health, energy):
        super().__init__(health, energy)
        self.poison = Poison()

    def attack(self, amount, target):
        super().attack(amount, target)
        self.__poison_target(target)


    def __poison_target(self, target):
        if isinstance(target, Poisonable):
            target.suffer_poison_damage(self.poison)


class Poisonable(ABC):
    @abc.abstractmethod
    def resist_poison(self) -> bool:
        pass

    @abc.abstractmethod
    def suffer_poison_damage(self, poison: Poison) -> None:
        pass

class Hero(Monster, Poisonable, ABC):
    name = 'Rockwood'

    def __init__(self, health, energy) -> None:
        super().__init__(health, energy)

    def resist_poison(self) -> bool:
        # resist poison chance
        return random.randint(0, 1) == 1

    def suffer_poison_damage(self, poison: Poison) -> None:
        if self.resist_poison():
            pass
        else:
            print('Poison has take effect!')
            # Loops Poison display repeated with time delay.
            # for repeater amount of times.
            total_amount = 0
            for poison_tick in range(poison.ticks):
                total_amount  = poison.damage_per_tick
                print(f'Poisoned: {poison.damage_per_tick} damage')
                time.sleep(0.5)

            print(f'Poison did a total damage of {total_amount} to {self.name}')
            self.health -= total_amount
  • Related