RPG-like inventory class organization

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By VictorSeven

Hi all,

I am making a RPG-like game, and I want to have a lot of items, where each item does a different thing. I am wondering what is the best way to manage this situation.

The way I am currently implementing this is: in a script called items.gd I create a class for each item, and then I return them with an index, as:

class item1:
    const uid=0
    func use():
        pass

class item2:
    const uid=1
    func use():
        pass

func get_item(index):
    if (index == item1.uid): return item1.new()
    elif (index == item2.uid): return item2.new()

With this, I can get any item as:

item = get_item(0) #item is item1 object
item.use()

This is ok, but when the number of items is big, the amount of code I to use for this approach grows a lot. I thought that each item could extend from a BaseItem class that has the general behaviour for functions and stores common variables (as the name or texture).

However, even doing this I still need to implement the get_item function, returning by hand each one of the classes. The best that comes to my mind is to put all the classes in an array and something like:

func get_item(index):
    var classes = [item1, item2]
    for c in classes:
        if (index == c.uid): return c.new()

But still, hard-coding the classes array seems very brute-force to me. Is there any way to solve this problem in an elegant way using GDScript?

:bust_in_silhouette: Reply From: picnic

Yes, you need one item class with some identifying member/field/property/tag, call it what you will. As a simple example:

extends Node

class InventoryItem:
    var type
    var name

    func _init(t, n):
    type  = t
    name = n
	
func wield():
    if type == 99:
        print("You can't wield that!")
    elif type == 42:
	print("You wield the ", name)
	

func _ready():
    var a = InventoryItem.new(99, "Screwdriver")
    var b = InventoryItem.new(42, "Sword")

print("a is of type ", a.type)
a.wield()

print("b is of type ", b.type)
b.wield()

You’d probably want your items types as enums rather than numbers for clarity. You could use an array instead of discrete variables such as a and b. You class can have a bunch of functions which will only be executed if the item is of the right type. You can make it much more sophisticated (for example with unique items such as swords with varying degrees of damage etc) but that’s a reasonable starting point. As you make it more refined you could make a base class with characteristics and functions common to all items, then extend it for particular types of item.

Hope that helps :smiley:

Here’s an array version with an enum of item types :

enter code here
extends Node

enum ITEM_TYPE {sword, screwdriver, shirt}

class InventoryItem:
    var type
    var name

    func _init(t, n):
	    type  = t
	    name = n
	
    func wield():
	    if type != sword:
		    print("You can't wield the ", name)
	    elif type == sword:
		    print("You wield the ", name)
				
    func wear():
	    if type != shirt:
		    print("You can't wear that!")
	    elif type == shirt:
		    print("You wear the ", name)
	

func _ready():
    var items = [ InventoryItem.new(ITEM_TYPE.screwdriver, "Pink Screwdriver"),
      InventoryItem.new(ITEM_TYPE.screwdriver, "Black Screwdriver"),	
      InventoryItem.new(ITEM_TYPE.sword, "Sword"),
      InventoryItem.new(ITEM_TYPE.shirt, "Ragged shirt"),
      InventoryItem.new(ITEM_TYPE.shirt, "Shirt and tie"),
    ]

    # a quick test...
    for i in items:
	    i.wield()
	    i.wear()

EDIT: If you want to access a particular item you could give it an ID as you were then just search for it in the array. The ID would be another var in the class which you could assign programatically if required.

picnic | 2018-06-29 14:54

Sorry for the delay in the answer. Thank you for your suggestions!

The problem with this approach is the same as mine: you still have to create a big items array instancing everything. If you have to instance, like, 50 or 100 objects, it is long and tedious work.

Is there any way to say “take all the possible types” and instance them, instead of manually creating every class or instancing every object? Basically I would like a way to automatically generate the items array for any number of objects.

VictorSeven | 2018-07-04 12:10