Inventory system -- attribute of object or separate class?

Hey peeps,

I’ve got the inner workings figured out, in the abstract at least; early prototype works by passing item’s unique ID within the database to a function so that all data needed to either store or spawn in an object can be quickly accessed. Things such as equipping/unequipping, dropping/picking up and moving between containers is no problem. All I really need to express the contents of an inventory is a dictionary.

What I’m wondering is, should I develop the inventory system into a class of it’s own? I have to write all sorts of functions to deal with the aforementioned dict anyway, so I thought maybe just dump everything into a class and call it a day. Way I can totally dance around this: leave inventory as just an attribute of the object that holds the dictionary and write all inventory-specific functions to a separate module, then import only the functions I need when I need them and leave it at that, as I don’t really need any of them constantly at hand.

So what do you think is more optimal, in your experience?

my inventory is a object but is ran by my UI manager (position / rot etc) - I spawn it in a overlay scene and set it’s owner and it does the rest from there :smiley:

I convert objects into data

[ [‘SpawnName’, spawnNameValue],[‘Name’,name],[‘Color’,list(obj.color)],[‘Damage’, Value] ]

From experience, it is better to use a class, in a separate file.

Depending on how experienced you are, or get, you could be able to test the design without having to run Blender.

This is something I am currently doing on a project, and this as proved to be very valuable and saved me a lot of struggle.

Of course the design has to be ever so slightly different, where you must think about correctly laying out your code.

But overall it is so powerfull that it is worth the pain.

I quote:

Writing tests is painful but it hurts so good!

In the case where you still couple your class to BGE, it is not a big issue. Your logic will still be encapsulated in one piece of software that does one job and does it good.

At the end of the day it is up to you.

can a class have a not predefined number of attributes?

ie a mixed bag of objects it is handling?

future proof?

You can do this kind of things yes, why?

What do you mean by “future proof”?

so what I do right now is

do_not_copy = [ 'Name', "Color", "SpawnName"]
props = [ ('Name', obj.name), ('Color', obj.color), ('SpawnName', spawnName)] 


for propName in obj.getPropertyNames():
    if propName not in do_not_copy:
        props.append( (propName, obj[propName]) 

converting a object into data to reconstruct it

Serialization is a different matter.

I suppose this could just be a dictionary though :smiley:

Dictionary[propName] = obj[propName]

Yes it can, but the purpose of objects is to put the methods/implementation next to the data. One is not better than the other, it is just a matter of writing things in a more logical way.

# this accepts a dictionary using keywords as input to create objects
# I would use upbge and create unique names per game object to avoid issues later and use 
# the slot number of the inventory as a key to store the dictionary
# so the inventory as far as structure is concerned is a dictionary of dictionary.

def Name(obj, prop, objDict):
    obj.name = objDict[prop] 

def Color( obj, prop, objDict):
    obj.color = objDict[prop] 

def Mass( obj, prop, objDict):
    obj.mass = objDict[prop] 
        

helperFunctions = { "Name":Name, "Color":Color, "Mass":Mass  } 


def newObject(objectDict,  owner):
    newOb = own.scene.addObject( objectDict['SpawnName'], owner, 0)
    for key in objectDict:
        newOb[key] = objectDict[key]
        if key in helperFunctions:
            command = helperFunctions[key]
            command( obj, key, objectDict )

Thank you both for the input c:

I can use a database to draw item templates from, then assign a separate, unique ID to each item and store them in a savegame-specific dict. Get mesh and base values from template database and use savegame dict to reference individual instances. Then store items in the inventory so I can get access like so

inventory[slot] = itemDict[item_id]

Then things can be setup so selecting a slot returns relevant item data. I like it.
What I’m going to test: subclassing dictionary so I can get this functionality + any extras needed.

What i don’t get it why people always want to use the objects as storage, you got a GD so use it as the database. referencing to the right objects is not hard, data can be called on the fly,etc.

i store my inventory data in the GD as a dict with categories like this:

{'ammo': {'uzi': 1, 'gun': 2}, 'potions': {'energy': 1, 'mana': 1, 'health': 1}, 'crafting': {'steen': 1, 'touw': 1, 'hout': 1}, 'fruit': {'orange': 1}}

Makes it very easy to use/sort or whatever. you only need some data of the item you like, for example in witch category it’s located.

it’s in the resources if you want to take a look at it. or do i understand you in the wrong way now?

i like the flexibility of classes and here is the basis for my inventory system.

from random import *
import time, hashlib

#----------------------------------------------------
# function description goes here
#----------------------------------------------------
def genkey():
    rn = randrange(1,10000)
    mtime = time.time()
    combined = str(mtime)+str(rn)
    gen = hashlib.md5(combined.encode('utf-8'))
    return gen.hexdigest() 



#----------------------------------------------------
# class description goes here
#----------------------------------------------------
class basicContainer:
    
    def __init__(self):
        self.name = "container"
        self.items = {}
        
        super().__init__()
        
    
    def __getitem__(self, key):
        return getattr(self, key)
    
    def __setitem__(self, key, value):
        setattr(self, key, value)
    
    def __len__(self):
        return len(self.items)
    
    def __iter__(self):
        for key in self.items:
            yield self.items[key]
        return self
    
    def add(self,item):
        if hasattr(item,"key"):
            self.items[item["key"]] = item
        else:
            self.items[genkey()] = item

    def remove(self,item):
        if hasattr(item,"key"):
            if item["key"] in self.items:
                outobj = self.items[item["key"]]
                del self.items[item["key"]]
                return outobj
        else:
            for key in self.items.keys():
                if item == self.items[key]:
                    outobj = self.items[key]
                    del self.items[key]
                    return outobj
                
        if hasattr(item,"name"):
            print("ERROR :",item.name,"Not in",self.name)
        else:
            print("ERROR : Item Not in",self.name)
            
        return None

each actor has their own inventory and so can chests / containers / lockers backpacks etc.

Where do you guys (everyone) stand on writing inventories to a text file stored on the HD somewhere the user couldn’t find it and alter it? I tested this awhile back and had good results.
The main thing is that when the user quits the game, shuts off the PC, then starts and plays the game again, the data is still there. Guns, bullets, money, etc.
Another advantage is you can use blend files as levels. Instead of libloading. The new blend (level) can read from the text file and load all the data. This allows you to keep the blend files small enough to run on weaker PCs, while making huge games.

Curious what your thoughts are on this.

Libloading doesn’t prevent you from having levels in files.

And yes, writing data to the disk is something most software application do, games are in that category. But don’t expect the user to be unable to find his savefile, as long as it is on disk you cannot hide it…

And although data persistance is not part of the original question, using classes/objects you can make methods to dump on disk/load from disk the data. You can do it without classes too, it’s up to you…

a simple way to prevent the casual player to cheat by changing the save file, is to obfuscate the save file.

here is a simple example where the save file gets compressed to make it a little harder for the cheater.

import zlib
import json

s = {"test":"this is a test"}

js = json.dumps(s)
cp = zlib.compress(js.encode('utf-8'))
# print compressed dict
print(cp)


dcp = zlib.decompress(cp)
jsd = json.loads(dcp)
# print decompressed dict
print(jsd)

the output from this.
b’x\x9c\xabVI-.Q\xb2RP\xc9\xc8,V\x00\xa2D\x05\xb0H-\x00s\xcb\x08\xb0’
{‘test’: ‘this is a test’}

And now you also have to obfuscate your source code because someone could just look at it and use the same functions as you to edit the file.

Depends on how much trouble you want to go through in order to give more trouble to people wanting to have fun with your game and the savefiles…

is exactly what i do, i compile it into a module that i can import.
(a .so file on linux or a .pyd on windows)

It’s a single player game I’m working on so people can only cheat at the expense of their own fun c:
Heck, I’ll even look into making modding easy if I have the time for it.

I got down to business

Currently I’m going with a grid system that’s already smart enough to know not to add items that won’t fit. Working on adding the drag-n-drop thingie.

The code is pretty lightweight so it has that going for it. The inventory itself is a subclass of dict and an attribute of object. So all I have to do for any one game object to have a container (or even multiple ones) is create a new instance of inventory and pass some parameters(display name, grid size, etc).