Question

For context, I'm working on an inventory system in an RPG, and I'm prototyping it with python code.

What I don't understand is how to make separate variables for each instance of an item without declaring them manually. For a short example:

class Player(object):
    def __init__(self):
        self.Items = {}

class Item(object):
    def __init__(self):
        self.Equipped = 0

class Leather_Pants(Item):
    def __init__(self):
        #What do i place here?
    def Pick_Up(self, owner):
        owner.Items[self.???] = self #What do i then put at "???"
    def Equip(self):
        self.Equipped = 1
PC = Player()
#Below this line is what i want to be able to do
Leather_Pants(NPC) #<-Create a unique instance in an NPC's inventory
Leather_Pants(Treasure_Chest5) #Spawn a unique instance of pants in a treasure chest
Leather_Pants1.Pick_Up(PC) #Place a specific instance of pants into player's inventory
PC.Items[Leather_Pants1].Equip() #Make the PC equip his new leather pants.

If I did something silly in the above code, please point it out.

What I want to do if the code doesn't make it clear is that I want to be able to dynamically create variables for all items as I spawn them, so no two items will share the same variable name which will serve as an identifier for me.

I don't mind if I have to use another class or function for it like "Create_Item(Leather_Pants(), Treasure_Chest3)"

What's the best way to go about this, or if you think I'm doing it all wrong, which way would be more right?

Was it helpful?

Solution

As a general rule, you don't want to create dynamic variables, and you want to keep data out of your variable names.

Instead of trying to create variables named pants0, pants1, etc., why not just create, say, a single list of all leather pants? Then you just do pants[0], pants[1], etc. And none of the other parts of your code have to know anything about how the pants are being stored. So all of your problems vanish.

And meanwhile, you probably don't want creating a Leather_Pants to automatically add itself to the global environment. Just assign it normally.

So:

pants = []
pants.append(Leather_Pants(NPC))
pants.append(Leather_Pants(chests[5]))
pants[1].pickup(PC)

The pants don't have to know that they're #1. Whenever you call a method on them, they've got a self argument that they can use. And the player's items don't need to map some arbitrary name to each item; just store the items directly in a list or set. Like this:

class Player(object):
    def __init__(self):
        self.Items = set()

class Item(object):
    def __init__(self):
        self.Equipped = 0

class Leather_Pants(Item):
    def __init__(self):
        pass # there is nothing to do here
    def Pick_Up(self, owner):
        self.owner.Items.add(self)
    def Equip(self):
        self.Equipped = 1

OTHER TIPS

Abernat has tackled a few issues, but I thought I weigh in with a few more.

You appear to be using OOP, but are mixing a few issues with your objects. For example, my pants don't care if they are worn or not, I care though for a whole host of reasons. In python terms the Pants class shouldn't track whether it is equipped (only that it is equippable), the Player class should:

class CarryableItem:
   isEquipable = False
class Pants(CarryableItem):
   isEquipable = True      

class Player:
  def __init__(self):
    self.pants = None   # Its chilly in here
    self.shirt = None   # Better take a jumper
    self.inventory = [] # Find some loot
  def equip(self,item):
    if is.isEquipable:
      pass # Find the right slot and equip it,
           # put the previously equipped item in inventory, etc...

Also, its very rare that an item will need to know who its owner is, or that its been grabbed, so verbs like that again should go onto the Player:

class Player:
  maxCarry = 10
  def take(Item):
    if len(inventory) < maxCarry:
       inventory.append(item)

Lastly, although we've tried to move most verbs on to actors which actually do things, sometimes this isn't always the case. For example, when instantiating a chest:

import random
class StorageItem:
  pass
class Chest(StorageItem):
  __init__(self):
    self.storage = random.randint(5)
    self.loot = self.spawnLoot()
  def spawnLoot(self):
    for i in range(self.storge):
      # Lets make some loot
      item = MakeAnItem # Scaled according to type level of dungeon, etc.
      loot.append(item)
  def remove(item):
    self.loot[self.loot.index(item)]=None

Now the question about what to do when a Player wants to plunder a chest?

class Player:
  def plunder(storage):
    for item in storage.loot:
      # do some Ui to ask if player wants it.
      if item is not None and self.wantsItem(item) or \
          (self.name="Jane" and self.wantsItem(item) and self.doesntWantToPay):
        self.take(item)
        storage.remove(item)

edit: Answering the comment:

If you are curious about calculating armor class, or the like, that again is a factor of the user, not the item. For example:

class Player:
  @property
  def clothing(self):
    return [self.pants,self.top]
  @property
  def armorClass(self):
    ac = self.defence
    for item in self.clothing:
      def = item.armorClass
        if self.race="orc":
          if item.material in ["leather","dragonhide"]:
            def *= 1.5 # Orcs get a bonus for wearing gruesome dead things 
        elif item.material in ["wool","silk"]:
          def *= 0.5 # Orcs hate the fineries of humans
      ac += def
    return ac
pants = Pants(material="leather")
grum = Player(race="orc")
grum.equip(pants)

print grum.armorClass
>>> 17 # For example?
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top