2

I am trying to create a text-based rpg game in python for learning purposes. I have a problem with function to increase player attributes.

My player character class code:

class Hero:
def __init__(self, Hstrength, Hdexterity, Hendurance, Hmagic, Hname, Hlocation):
    self.level = 1
    self.xp_needed = 100
    self.attribute_points = 0

    self.strength = Hstrength
    self.dexterity = Hdexterity
    self.endurance = Hendurance
    self.magic = Hmagic

    self.name = Hname
    self.location = Hlocation

And a function I came up with to let player improve attributes (defined in another file):

def train(character):
    if (character.attribute_points > 0):
        character_attributes = {
            "str": character.strength, 
            "dex": character.dexterity, 
            "end": character.endurance, 
            "mag": character.magic
            }
        print("Which attribute you want to train?")
        for attribute in character_attributes: 
            print(attribute)
        a = input(">>")
        if a in character_attributes:
            character_attributes[a] += 1
            character.attribute_points -= 1
            print(f"Your {a} is now {character_attributes[a]}")
        else:
            print("Invalid attribute")
    else:
        print("Not enough attribute points")

But it does not change the value of attribute, just prints value bigger by one than the attribute.

How can I make this work, or let the player choose which attribute to improve and then increment it other way?

Filip22022
  • 23
  • 3

3 Answers3

1

You create a local reference character_attributes that contains the literal values of the character attributes:

>>> character_attributes = {
            "str": character.strength, 
            "dex": character.dexterity, 
            "end": character.endurance, 
            "mag": character.magic
            }
>>> print(character_attributes)
{"str": 4, "dex": 2, "end": 4, "mag": 0}

Changing these integer values only changes the values of character_attributes but does not flow back into character. If you want to actually change the character attribute you have to set the attribute using setattr(character, attr, value):

def train(character):
    if (character.attribute_points > 0):
        character_attributes = {
            "str": "strength", 
            "dex": "dexterity", 
            "end": "endurance", 
            "mag": "magic"
            }
        print("Which attribute you want to train?")
        for attribute in character_attributes: 
            print(attribute)
        a = input(">>")
        if a in character_attributes:
            attr = character_attributes[a]
            base_value = getattr(character, attr)
            setattr(character, attr, base_value  + 1)
            character.attribute_points -= 1
            print(f"Your {character_attributes[a]} is now {getattr(character, attr)}")
        else:
            print("Invalid attribute")
    else:
        print("Not enough attribute points")
Robin De Schepper
  • 4,942
  • 4
  • 35
  • 56
1
character_attributes[a] += 1

you are changing the character_attributes dictionary defined in the train function. this dictionary contains the values of your character "attributes", not references to your Hero objects's attributes (here attribute in the sense of "python class attribute).

For the sake of the example, say all of your character's attribute currently have a value of one.

The following code :

character_attributes = {
    "str": character.strength, 
    "dex": character.dexterity, 
    "end": character.endurance, 
    "mag": character.magic
}

creates the following dictionary :

character_attributes = {
    "str": 1,
    "dex": 1, 
    "end": 1, 
    "mag": 1
}

which has no reference to the source of the values that have been copied there, and character_attributes[a] += 1 only changes that dictionary.

to change the value of the actual character attributes, you need to use the actual attribute on the left side of your operation, for instance :

character.dexterity += 1

Since the name of the attribute you intend to change is dynamic, you can't use . but can instead use the getattr and setattr functions :

  • getattr(obj, name) gives you the value of object's name attribute. So for example getattr(character, "magic") is equivalent to character.magic
  • setattr(obj, name, value) sets the value of object's name attribute to value. For example setattr(character, "magic", 1) is equivalent to character.magic = 1

Here is an example of using these in your code :

character_attributes = {
    "str": "strength", 
    "dex": "dexterity", 
    "end": "endurance", 
    "mag": "magic"
}
a = input(">>")
if a in character_attributes:
    setattr(character, a, getattr(character, a)+1)
    character.attribute_points -= 1
    print(f"Your {a} is now {getattr(character, a)}")

iodbh
  • 688
  • 1
  • 7
  • 14
0

When you create the dictionary character_attributes, you're creating a new dictionary which merely shares the same values as your character class, and when you make the change, you're changing the dictionary, not the actual values.

This might be useful to you for iterating through the class attributes: Iterate over object attributes in python python, and you can use that info along with the setattr() function to make the changes directly.

askman
  • 447
  • 4
  • 14