Sign in to follow this  

Inventory / item logic

This topic is 673 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm creating a harvest moon type game and currently working on my inventory and item system. I have a class InventoryHelper, which holds my inventory array and methods like putItem and removeItem. My array takes my class Item.

My Item class holds a constructor which takes a string Name and int Quantity as an argument. Also in the class file, are static methods that create certain items like createSeed. All these methods do is set the name and quantity and then return Seed.

The question is, am I going about this the right way? Eventually I'm going to end up with hundreds of different methods for creating different items which is probably not the best practice, but I am curious to know what other approaches I can take to this.

Share this post


Link to post
Share on other sites
Thats what I was thinking but I wouldn't know how to implement it.
Pretty much each item is going to have multiple attributes associated with it like sell and buy price, if it's a tool, If it's edible etc. So if I created a base item class and inherit each item from it, that'd be a lot of code.

Share this post


Link to post
Share on other sites

in Caveman, constant info about objects such as name, weight , base price, damage done, model for drawing, etc is stored in an array of structs - basically object_type_structs.

 

an instance of an object is stored in a struct with type, quantity, quality, and location (used by dropped objects) - basically object_instance_structs.

 

a stufflist is an array of object_instance_structs. IE a list of objects.

 

stufflists are used for character inventories, containers, and dropped objects.

 

stufflists have methods like add_item, remove_item, num_carried, remove_best, remove_worst, etc.

 

constant data about an item type is stored in the object_type_structs, IE how to draw it, base price, weight, etc.

 

variable data about instances of items in the game world are stored in object instance structs (type, quantity, quality, location).

 

in OO terms, you'd probably have two basic types of objects, a list of object types (one instance), and stufflists (many instances). the list of object types would basically be a read only database. so maybe just some getter and setter methods, or load from disk for data driven. most of the work would be done by stufflist methods such as add_item, remove_item, etc..

 

the combo of the stufflists and the object types list forms a small relational database (related on type).

 

its also possible to store the constant object type info with each instance of the object. redundant data, but sometimes done for optimization. don't do it until you have the proof you need to. if you find you do, its a simple matter to add the extra variables to an object_instance _struct definition, and copy the values from the object_type_struct to the object_instance_struct when you add an object to the game.

 

remember - not every little thing has to be an object.  in the case of inventories, it appears the pile of stuff is the basic object, not the individual items. makes sense, an individual item is just the special case of a pile of stuff of a single type of quantity one.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

I don't think that's a very effecient way to do things.

 

Personally, I use a generic Item class with generic variables.

 

For example:

Class Item{
string name ; //something lazy and quick to type for you, used internally. Also allows "duplicates" since two can have the same 'display name', but not 'name'
string displayname ; //what's shown to the player
string itemType ;
Texture itemIcon ;
int maxStack = 1 ; //maxiumum stack-size of this item So armor = 1, apples = 10, or whatever
itemString1;
itemString2;
...and so forth
itemInt1;
itemInt2;
..etc
itemfloat1 ;
...
}

You then can use the generic variables for different things depending on the 'itemType'

 

So Armor would look like this:

ItemName: ironarmor

DisplayName: Iron Armor

itemType: chestArmor

itemInt1: 5

itemInt2: 10

itemFloat1: 150

 

so, if itemType == "chestArmor", and it's in the player's equiptment slot, it adds 5 to the player's armor value.  ItemInt2 corresponds to weight, Float1 corresponds to it's cost in shops. I use generic variable names because itemInt1 doesn't necessecarily correspond to "armor value".For itemType "food" it corresponds to how much health is healed when it's consumed. The obvious problem is it's not very human-readable, so you need a cheatsheet or //notes to make sense of it.

 

Alternatively, if you want a more human-readable form of item, you can just put every named variable on every item. So "Apple" would still have an "ArmorValue" variable, but it's just set to 0, and isn't equipable anyway and IronArmor can't be consumed, even though it has the "restoreHealth" Variable. Personally, I feel it's kind of a pain, because if you have alot of different item types, your list of variables can get quite long.

 

So you can populate your new items however you like, stick them in an "all items" array, and create a "AddItemToInventory( string itemName)" method. The method looks through the AllItems array, and finds the one with that name, and puts one in your inventory, or increments the count of existing ones. Personally, I used 4 arrays for my inventory:

 

Item Equipment[], Int EquiptmentCount[], Item Inventory[] , Int InventoryCount[] For the Equipment Array, the index corresponds to the body-slot. (head, torso, legs, etc)

 

I don't know if this is really the best way to go about it, but it works for me. I've tried tons of different inventory setups, and this one seems to be the easiest to handle and it's totally self-contained, so I've been able to reuse it alot.

Edited by SirWeeble

Share this post


Link to post
Share on other sites
I almost used a hashmap for my inventory thinking it would be better but I needed a way to get a specific item from the inventory. But my approach is like SirWeeble

class Item
itemName
quantity

public Item Seed(int quantity)
ItemName = "seed"
Quantity = quantity

But if I have 100 items I'll have 100 different methods like the above method.

Share this post


Link to post
Share on other sites

so, if itemType == "chestArmor", and it's in the player's equiptment slot, it adds 5 to the player's armor value. ItemInt2 corresponds to weight, Float1 corresponds to it's cost in shops. I use generic variable names because itemInt1 doesn't necessecarily correspond to "armor value".For itemType "food" it corresponds to how much health is healed when it's consumed. The obvious problem is it's not very human-readable, so you need a cheatsheet or //notes to make sense of it.

 

I would advocate strongly against this approach. Not only is it not human-readable (which you might get away with, if you are working on a very small project, alone), but it makes it impossible to use this data in any kind of generic code. Meaning every piece of code that deals with items has to always consider the exact type of the item to determine the data layout it has.

 

 

Alternatively, if you want a more human-readable form of item, you can just put every named variable on every item. So "Apple" would still have an "ArmorValue" variable, but it's just set to 0, and isn't equipable anyway and IronArmor can't be consumed, even though it has the "restoreHealth" Variable. Personally, I feel it's kind of a pain, because if you have alot of different item types, your list of variables can get quite long.

 

If your items really have that many properties, and there isn't much overlap, I would consider making the item a key-value store of sorts. This allows items to have a unique set of properties, that are still accessible in a generic way. For example:

// A very simplistic item class, that is only a key-value store of floats.
// You will probably need something more sophisticated, but this illustrates the concept well.
class Item
{
public:
    bool hasAttribute(EItemAttribute) const;
    float getAttributeValue(EItemAttribute) const;
private:
    std::map<EItemAttribute, float> attributes;
}

...

// This is just an example usage, to illustrate the concept of generic access to items
bool Player::equipWeapon(Item* item)
{
    if (item.hasAttribute(E_ATTRIBUTE_ATTACK_VALUE))
    {
         m_activeWeapon = item;
         return true;
    }
    return false;
}

// This is just an example usage, to illustrate the concept of generic access to items
bool Player::attack(Foo* target)
{
    float attackValue = 0.0f;
    if (m_activeWeapon)
    {
        attackValue = m_activeWeapon->getAttributeValue(E_ATTRIBUTE_ATTACK_VALUE));
        return target->onAttacked(this, attackValue);
    }
    return false;
}

As for filling out the data in the first place, you don't have to write that in code, just read it in from a file in whatever format you like (XML, JSON, your own format, whichever suits you the most).

 

As per the difference between "item instances", and what you could call "item prototypes", refer to the post by Norman Barrows. Note that depending on your requirements, you might or might not need to differentiate between the two.

Share this post


Link to post
Share on other sites
Normans post says to have an array that stores the info for the item and the item itself. Right now I only have the latter, but if I created a hashmap<Item, Integer> I won't need to constantly be creating instances of class Item? If I understand correctly

Share this post


Link to post
Share on other sites

Normans post says to have an array that stores the info for the item and the item itself. Right now I only have the latter, but if I created a hashmap<Item, Integer> I won't need to constantly be creating instances of class Item? If I understand correctly

 

Basically, yes. You have your item "prototypes" in a map by item type id, then an item "instance" is an item type id (which you can use to look up the general parameters of the item, that are constant throughout all instances), plus properties that can be different for different instances, if you need them (an example would be item durability). Then you need to create the "prototype" only once, typically by reading a file that lists all item prototypes and their properties.

Share this post


Link to post
Share on other sites
I think I get it. When I get onto my computer later I'll post some code and see if I have it right. I don't think the properties of my items will be changing much so I can keep it simple.

Inventory[I] = new Item(itemHashmap.getKey("seed");

But I'd still need to write the 100 different constructors to populate the itemHashmap or use XML.

Share this post


Link to post
Share on other sites


But I'd still need to write the 100 different constructors to populate the itemHashmap or use XML.

 

Well, the data has to come from somewhere, right? smile.png

 

P.S.: I know I wrote it didn't matter, but XML is truly unnecessarily verbose for something as simple as this, consider JSON or something even simpler.

Share this post


Link to post
Share on other sites

My 2 cents: the "better" way is definitely reading in the data from a file, but early on, it doesn't really matter. If you don't feel like fiddling with file parsing right now, write it in code (a couple items will likely be enough for testing while you build the game), in a well separated place. Then, when you have the time/mood for it, move it into data files. Perhaps you will be stuck on a difficult design issue and it will be refreshing to take a breather and work on some file parsing, while still feeling like you accomplished something meaningful smile.png

Share this post


Link to post
Share on other sites

in addition to non-generic variables, each object_type_struct in caveman also contains seven arrays of generic variables. with all the benefits and hassles thereof.

 


Thats a good idea. Its always good to take a break from one piece of the project and move to the next

 

have to be careful that doesn't lead to a number of unfinished features at once. usually best to do "one thing at a time, do it very well, then move on".  gets you milestones quicker, and tends to keep the project in a more ready to go (as it stands so far) condition at all times.

 

for a break, but with continued productivity, i find having a second project, perhaps for fun or learning or experimentation is an excellent option. you don't get multiple unfinished features in the primary project due to boredom, and when the first project is done, you might already have a head start on a second one worth pursuing. handy tools and libs that you could use but are not mission critical for the primary project (and thus at the top of it's todo list) are great candidates for second projects.

Share this post


Link to post
Share on other sites

Alright so this is what I have so far. The items are going to need more parameters eventually, but I'll handle them with strings and integers in the constructor of each item.

    HashMap<String, Item> ItemList();

    private static void Hoe(){
        Item hoe = new Item();
        hoe.itemName = "hoe";
        ItemList.put(hoe.itemName, hoe);
    }

Each of my item constructors will look like this, and then I populate the HashMap by calling the static method at the start of the game.

putItem(Item.ItemList.get("seed"), 2);

public void putItem(Item item, int itemQuantity){
        for(int i = 0; i < playerInventory.length; i++){
            if(playerInventory[i].itemName == item.itemName){
                playerInventory[i].itemQuantity += itemQuantity;
                break;
            }
            else if(playerInventory[i].itemName == "empty") {
                playerInventory[i] = item;
                playerInventory[i].itemQuantity += itemQuantity;
                item.itemIndexInInventory = i;
                break;
            }
        }
    }

This is how I add the item to my inventory. It works right now but as I add more parameters like different seed types and strength of tools, I'm sure ill need to change some things.

Share this post


Link to post
Share on other sites

For stuff like this I find that a component system is really easy. That way, the only thing that everything that goes into the inventory needs is the IsAnItem component. After that, you can give seeds the IsSeed component and have that allow it to be planted, maybe it has a variable that indicates what type of plant it will grow into. A hoe, then, would instead have a IsTool component that handles whatever its uses are. But the inventory ONLY cares about the IsAnItem component to see if it can hold onto it.

 

Hope that's useful.

Share this post


Link to post
Share on other sites


Alright so this is what I have so far.

 

I can see a couple potential issues based on the code you posted. You didn't post the code for the Item class itself (which would have been the most relevant part), but based on the usage it has fields like itemQuantity and itemIndexInInventory. If you wanted to separate the "prototypes" from the "instances" like we discussed, these fields definitely do not belong in the prototypes. Seems to me you have the two mixed together, however I cannot tell for sure without seeing more of the code. Like you said, you will probably have to change some things as you add more code, and you will probably see what does and doesn't work yourself. That's okay - it's really rare to get everything right on the first try (especially since the definition of "right" tends to change a lot during development smile.png )

Share this post


Link to post
Share on other sites
I think the most effiecient method would be to set item properties depending on the item's id. For example if thr item ID is 11 then it would be a sword. You could store the properties of a certain item in a file, and on runtime make an index of ID properties, so everytime you want to check for an items properties you don't have to load a whole file.

As for the inventory, an array is a good idea if you want the inventory size to be static, but if you want to expand/shrink inventory, I would recommend using an std::vector. Also you could just have that array or vector contain a list of ints that represent an ID.

Share this post


Link to post
Share on other sites

This topic is 673 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this