matthew ruane
matthew ruane

Reputation: 15

Class Enemy Has Function combat_roll() but can't seem to access it Python 2.7

I really never bothered to learn classes in Python because all the docs I find are a bit too technical for me. So, yet again I have sat down to try to conquer them. I would love to be able to use classes for the text based games that I make, so that is how I try every time.

My class code (its messy now because I have been trying everything I can think of) is

class enemy:
    def __init__(self, name, weapon, hp, attack, defense, xp):
        self.self = self
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defense = defense
        self.xp = xp

        #self.attack_text1 = attack_text1
        self.attack_text1 = name, " attacks you with ", weapon, "."
        #self.attack_damage = attack_damage
        self.attack_damage = random.randint(0,2) + attack
        #self.total_damage = total_damage
        self.total_damage = self.attack_damage - player.defense
        if self.total_damage < 1:
            self.total_damage = 0

        #self.attack_text2 = attack_text2
        self.attack_text2 = name, " deals ", self.total_damage, " to you." 

        @staticmethod #tried this from a solution found on stack overflow
        def combat_roll(self):
            self.roll = random.randrange(0,20) + attack

        combat_roll(self)

I have a function called combat() that pits the player against an instance of the enemy class.

The instance is called like in that function:

def combat():
    goblin_weapon = weapon("goblin dagger", 1, 5)
    goblin = enemy("ugly goblin", goblin_weapon, 3,2,1,150)

    goblin_damage = goblin.attack - player.defense
    print "a ", goblin.name, " attacks you with ", goblin_weapon, "."
    print goblin.combat_roll
    if goblin.roll > player.defense:
        print goblin.name, "does ", goblin_damage, " damage."
    else:
        print goblin.name, "misses."

My goal is essentially somewhat of a d20 feel to the combat. attack bonus + d20 rolls vs armor class. I was hoping that the enemy class could have its own function that could handle its own dice rolls.

I've lost the original state of my code. I have checked google and searched here for solutions but none of them worked. I am sure it is because I haven't seen the solution in a way that works with my brain, because this can't be that complicated. Why can't I just call the function like I reference class variables?

enemy.combat_roll()

you know?

Anyway, thanks for the help!

Upvotes: 0

Views: 108

Answers (2)

Izaak van Dongen
Izaak van Dongen

Reputation: 2545

As you say, your code is a little all over the place, and it isn't entirely complete. I've tried to rewrite and fill it out a bit in the spirit of what I think you were going for:

import random

#dice roll for dealing damage as a global function
def get_damage(dice_size, bonus_damage):
    return random.randrange(dice_size) + bonus_damage

#class for a player
class Player:
    #initialises the player's name, defense value and hp
    def __init__(self, name, defense, hp):
        self.name = name
        self.defense = defense
        self.hp = hp

    #when something wants a string representation of a player (like format()
    #might) it just uses the player's name
    def __str__(self):
        return self.name

    #a method where a player takes damage from some enemy (if it quacks like an
    #enemy..). This is where your attack_text2 has gone.
    def take_damage(self, enemy, dmg):
        self.hp -= dmg
        print("{} deals {} damage to {}".format(enemy, dmg, self))

#class for a weapon
class Weapon:
    #__init__ taking 4 arguments, like in your code, except it wasn't clear what
    #the last two arguments were.
    def __init__(self, name, foo, bar):
        self.name = name
        self.foo = foo
        self.bar = bar

    #as with Player
    def __str__(self):
        return self.name

#class for an enemy
class Enemy:
    #this has changed: init only needs to *init*ialise the object not do any
    #more, so all it does is stored relevant info as a field.
    def __init__(self, name, weapon, hp, attack, defense, xp):
        self.self = self
        self.name = name
        self.weapon = weapon
        self.hp = hp
        self.attack = attack
        self.defense = defense
        self.xp = xp

    #as with Weapon
    def __str__(self):
        return self.name

    #this is roughly where the second half of your init has gone. Engages in
    #combat with a player, generating a random amount of damage, and doing all
    #of the defense/attack malarkey. The damage is not stored as a field (using
    #self.) as it is only needed for the duration of the combat
    def combat(self, player):
        print("{} attacks {} with {}.".format(self, player, self.weapon))
        damage = get_damage(20, self.attack)
        total_damage = damage - player.defense
        if total_damage < 0:
            total_damage = 0
        player.take_damage(self, total_damage)

#function constructing a couple of objects and using them to do a 'fight'
#I've used some key word arguments with the goblin and the player. I'd advise
#you to keep doing the same, as it helps a lot with clarity.
def play():
    dagger = Weapon("a goblin dagger", 1, 5)
    goblin = Enemy("an ugly goblin", dagger, hp=3, attack=2, defense=1, xp=150)
    aragorn = Player("Aragorn", defense=3, hp=100)
    goblin.combat(aragorn)
    print("{} now has {} hp".format(aragorn, aragorn.hp))

play()

I've included some comments trying to explain what's changed and what does what, and why, hopefully.

I hope it's stuck to your vision of the code. The main changes were to just refactor your bits of code around a bit. In your __init__ for the enemy, you originally had the code that's now ended up in Enemy.combat, for example. You also hadn't included a Weapon or Player class, although your code constructed a weapon and referred to player, so I've added those in. Let me know if you'd like any more clarification.

Here is a sample of this code in action:

an ugly goblin attacks Aragorn with a goblin dagger.
an ugly goblin deals 10 damage to Aragorn
Aragorn now has 90 hp

Upvotes: 0

Eagle-Eye
Eagle-Eye

Reputation: 1498

You're declaring the method only within __init__, instead of within enemy. To my understanding, it will only be available if you declare it within the enemy class itself.

class enemy:
    def __init__(self, name, weapon, hp, attack, defense, xp):
        self.self = self
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defense = defense
        self.xp = xp

        #self.attack_text1 = attack_text1
        self.attack_text1 = name, " attacks you with ", weapon, "."
        #self.attack_damage = attack_damage
        self.attack_damage = random.randint(0,2) + attack
        #self.total_damage = total_damage
        self.total_damage = self.attack_damage - player.defense
        if self.total_damage < 1:
            self.total_damage = 0

        #self.attack_text2 = attack_text2
        self.attack_text2 = name, " deals ", self.total_damage, " to you." 
        combat_roll(self)

    def combat_roll(self):
        self.roll = random.randrange(0,20) + attack

Upvotes: 1

Related Questions