Kage
Kage

Reputation: 23

Can I call a function from within a list? (python2.7)

I think I have all the terms right in my question...but I'm not sure. Basically what I am trying to do is get the little details of my inventory and equipment set in a text game.

What I want to do is have a weapon, lets go with a sword, and i want it to change certain aspects of the character itself. Lets go with the hp of the character increases by like...5, what i would have in this case is, (don't mind the sloppy work!)

global basehp
basehp = 10
global armorhp
armorhp = 0
global skillhp
skillhp = 0
global hpmod
hpmod = (skillhp + armorhp)
global righthand
righthand = 0

so setting the example with hp this is what i actually have in my program, now what i want to do is change armorhp to match what the armor would change it to, say i have my sword that adds 5 to my hp right? what i am working with now is just not working, at first i thought to just have it equip and add 5 to armorhp, then i started thinking about UNequiping it...the effect would be the same...i shall show you what i mean

def hpsword:
    armorhp = (armorhp + 5)

def prompt():
    x = raw_input("Type command>>")
    if x == 'equip':
        print "equip what?"
        print inventory
        y = raw_input(">>>>")
        if y == 'sword':
            if 'hpsword' in inventory:
                global righthand
                righthand = 1
                inventory.remove ('hpsword')
                equipmentlist.append ('hpsword')

Now from right here you'll notice i left some things out, like my lists, i'm getting to that but this is where i can really explain more of what i mean, i want to use each item like that, but i can't figure out how...i'm confusing i know, i have a lot on my mind, but i want to just do something like....

def this sword():
    this sword does this thing!

then when it gets equiped add the effect it does, such as +5 health, then remove it and take the effect with it, without having to do 100 lines of code with a bunch of statements that are oh add this effect but then when you don't want to use it anymore reset righthand to 0 and then if it was this number there take this many away from that before you change it to number 2 and add 10 more.

i'm also trying to avoid the equip sword, gain 5 health, unequip, still at 5? good, equip, YAY 10!!!

if i make any sense at all i'm shocked...but if anyone can figure out what i mean and give me a few pointers that would be great!

EDIT:

Reading up on what was left in a comment, (Thanks for that Eevee!) I think I am starting to figure out classes a little...very little.

New question then, would i then make a class for the main character, like,

class character(object):

then add in the init

def __init__(self)

then would i define inventory like,

def __init__(self)

def inventory

or would i do this,

def __init__(self, inventory)

sorry if i ask too many questions, just trying to get it figured out and i've noticed people here are nice enough to at least look at questions from wannabe programmers like myself :D

ANOTHER EDIT!!!!

Again, thanks a ton Eevee! That makes some sense, though reading through it i'm still a little confused as to how i would add it to my code and then what i would need to change for some different items.

To try and explain some, when i say different items, you gave me

class Sword(object):
    def visit_health_bonus(self, character):
        return 5

and if i wanted a sword that did something different i would just create a new class with a different name, but then basically the same thing?

class Sword2(object):
    def visit_strength_bonus(self, character):
        return 2

and that would give me a sword that would add 2 to my strength right?

now onto my other question, when i attempt to use this, what would i really need to get it to work right, i'm seeing a lot of things that i don't think would run without having some other bits

class Sword(object):
    def visit_health_bonus(self, character): 
        return 5

# Then, every turn, recalculate health:
health = player.base_health
for equipment in player.equipment:   //would i need another class here? (player)
    # Pretend this lists everything the player has equipped; you might
    # have to check each hand manually
    health += equipment.visit_health_bonus(player)  //also going to think a class (equipment)

Upvotes: 1

Views: 141

Answers (3)

qwwqwwq
qwwqwwq

Reputation: 7309

It sounds like you are missing out on some of the awesome utility of object oriented programming, similar to what Marcin said you might what to make a series of classes like so:

class Weapon:
    def __init__(self, name, armor_hp_bonus = 0, skill_hp_bonus = 0):
       self.skill_hp_bonus = skill_hp_bonus
       self.armor_hp_bonus = armor_hp_bonus
       self.__name__ = name

class Hero:
    def __init__(self):
        self.basehp = 10
        self.armorhp = 0
        self.skillhp = 0
        self.righthand = None
        self.inventory = []

    def choose_weapon(self):
        while True:
            for idx, item in enumerate(self.inventory):
                print '{i}: {weapon}'.format(i=idx, weapon=item.__name__)
                choice = raw_input("Pick your weapon: ")
                try:
                    self.equip(self.inventory.pop(int(choice)))
                    return
                except IndexError:
                    print "Invalid choice"
                    continue

    def equip(self, weapon):
        if self.righthand:
            print "Weapon already equipped to right hand!"
            return
        else:
            self.righthand = weapon
            self.armorhp += weapon.armor_hp_bonus
            self.skillhp += weapon.skill_hp_bonus

    def unequip(self):
        if not self.righthand:
            print "No weapon to unequip!"
            return
        self.skillhp -= self.righthand.skill_hp_bonus
        self.armorhp -= self.righthand.armor_hp_bonus
        self.inventory.append(self.righthand)
        self.righthand = None

    def acquire_weapon(self, weapon):
        self.inventory.append(weapon)

    def calc_effective_hp(self):
        return self.basehp + self.skillhp + self.armorhp

A class lets you keep all the variables you are having trouble keeping track of in one place. The skill bonus of one weapong will never get confused with the skill bonus of another weapon this way, because the variable storing that information is contained within the object. One you set up your classes correctly, the rest of your code is a lot shorter and cleaner because you can just call a method and rest assured that everything is being done correctly:

sword_of_1000_truths = Weapon('sword_of_1000_truths', skill_hp_bonus = 1337)
gandalf = Hero()

gandalf.acquire_weapon(sword_of_1000_truths)
gandalf.choose_weapon()

>> 0 sword_of_1000_truths
>> Pick your weapon: 0


print gandalf.calc_effective_hp()
>> 1347

gandalf.unequip()

print gandalf.calc_effective_hp()
>> 10

The methods such as equip and unequip make sure to increment and decrement the Hero's hp correctly, while keeping track of the logic of the game, i.e. you cant have two weapons in one hand, and you cant unequip a weapon if you don't have one equipped etc. This helps eliminate those ugly 100 lines of code you mention.

Good luck! Hope this helps

Upvotes: 1

Eevee
Eevee

Reputation: 48546

I'm assuming you're familiar with objects. If not, you should really read up on them (LPTHW is really good) before continuing, as they're pretty central to Python. If so, you should really be using a Player object instead of half a dozen globals.

The really easy approach is to have the sword be an object that does stuff when equipped and unequipped:

class Sword(object):
    health_bonus = 5

    def on_equip(self, character):
        character.health += self.health_bonus

    def on_unequip(self, character):
        character.health -= self.health_bonus

Then you call on_equip when the player equips the sword, and on_unequip when the player unequips it. Other weapons can have similar event hooks, maybe for different stats.

That's all well and good, but something about it bugs me: you have to remember to call on_unequip whenever the player loses an item, and there might be more than one way that happens. You also have to track the player's "base" health separately, and if it changes, updating the "calculated" health requires unequipping and reequipping the weapon, which doesn't make a lot of sense.

So let's try something else. The "visitor pattern" Marcin referred to means (roughly) to loop through all the parts of a larger whole and call the same method on them. In this case, you want to ask all of the player's equipment how much extra health it grants.

class Sword(object):
    def visit_health_bonus(self, character):
        return 5

# Then, every turn, recalculate health:
health = player.base_health
for equipment in player.equipment:
    # Pretend this lists everything the player has equipped; you might
    # have to check each hand manually
    health += equipment.visit_health_bonus(player)

Now you calculate the total health every time it's needed, so you don't have to worry about updating it when, say, the player levels up. You also don't have to worry about doing any extra bookkeeping when something gets unequipped: as soon as it disappears from the list of equipment, it stops contributing.

This is still a little inflexible, as it doesn't easily support e.g. a sword that doubles your attack power when fighting dragons. The most general way to solve this involves a lot of tangling and extra objects and figuring out what effects need to happen first, and I've yet to make it work to my satisfaction. Hopefully the above approaches are a good enough starting point. :)

The most flexible way to solve this is to have the action of "attack with equipped weapon" itself be an object and have equipment, the player, the target, etc. all react to the attack by increasing stats. But then you have questions of priority that are really fiddly to resolve so let's not do that.

Upvotes: 1

Marcin
Marcin

Reputation: 49826

A couple of things:

  1. You have 5 global variables. These should be moved into a class, and your function become a method on that class.

  2. The usual way to work something like this is for the sword to support a particular interface defining what modifications it makes, which the character class can read. Alternatively, use the visitor pattern, and have the character pass itself to a method of the sword on equip/unequip.

Upvotes: 0

Related Questions