2

I'm making a simple text-based game. My full code is 150 lines, so I'll include what I think is relevant. Let me know if you need more.

The problem is that this line:

print("You deal " + str(hero.damage) + " damage to the monster")

returns only 5 instead of 5 + level as wanted.

class Hero:

    def __init__(self):
        self.level = 0
        self.damage = 5 + self.level #This is the problem line. Self.level seems to be treated as equal to 0 even when it is higher than 0.
        self.experience = 0
    def level_up(self): #triggered on monster death
        xp_required = 15
        if self.experience >= xp_required:
            self.level += 1
hero = Hero()

I know hero.level_up() is successful because:

print(hero.level)

returns a level that gets correctly updated as appropriate.

I'm guessing either:

self.damage only gets calculated once, then stores that value even after components of it have changed.

or:

There is some kind of issue with calling __init__ values within __init__.

or:

The calculation is done on the class Hero, not the object Hero()

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 3
    Mind that `self.damage= 5+self.level` is **an assignment**. **Not an equation**. After it is executed, it is not *updated* anymore. You need to do that yourself. – Willem Van Onsem Mar 26 '17 at 23:49
  • Thanks dude. Problem fixed. Simply copy paste: self.damage = 5 + self.level into level_up(). If you want to add your comment as an answer, I'll mark it as answered. damn.. I was nearly there. I actually mentioned the answer in my original question, at the bottum XD. Just didn't direct my attention there enough because I wasn't sure which of the problems it was, and figured it might've been a different problem also. – Elliott Johnson Mar 26 '17 at 23:57

2 Answers2

7

You are right, self.damage is only written to once: In the __init__ constructor which is itself only called once (when the object is created). And at that time, self.level is zero, so self.damage will be always 5.

You have two options:

  1. Either you change your code to always update the damage as well whenever you change the level. In order to avoid spreading the damage calculation logic into multiple places, you would create a helper method to update the damage value:

    def update_damage(self):
        self.damage = 5 + self.level
    
    def level_up(self):
        if self.experience <= 15:
            self.level += 1
            self.update_damage()
    

    If the damage value is only ever dependent on the level, you could also make it a update_level(new_level) method; but I opted out of that solution here since it’s not uncommon to have some other things affect damage later on as well (e.g. damage potions or whatever).

  2. Or you change damage into a calculated property, so the damage value is calculated whenever you access it. That has the overhead that it needs to be calculated every time instead of the value being stored somewhere, but on the other hand, you have the ultimate truth about the damage value and you do not need to remember to update it elsewhere:

    def __init__(self):
        self.level = 0
        self.experience = 0 
    
    @property
    def damage(self):
        return 5 + self.level
    
poke
  • 369,085
  • 72
  • 557
  • 602
  • I didn't know about `@property`. That seems really useful for a few situations. – Unlocked Mar 26 '17 at 23:59
  • @Unlocked Indeed, properties replace getters and setters (yes, setters too!) with a more natural interface to access or store values, making them very useful (and pythonic). For more on that topic, see also [When and how to use the builtin function property() in python](http://stackoverflow.com/q/1554546/216074) and [How does the property decorator work?](http://stackoverflow.com/q/17330160/216074) – poke Mar 27 '17 at 00:09
  • Your post taught me two things. Thanks :). 1: variables are stored once and that's final. 2: @property is a thing. – Elliott Johnson Mar 27 '17 at 00:10
5

Your guess about self.damage being calculated once is correct. As @Willem Van Onsem mentions, self.damage= 5+self.level is an assignment and not an equation. You will need to manually update it each time you change self.level.

The most suitable approach to me seems to be wrap it into a property such as:

@property
def damage(self):
    return 5 + self.level

The way that you are treating equality is more closely related to reactive programming which is often emulated with properties in python.

Dair
  • 15,910
  • 9
  • 62
  • 107