Inventory Type Array theory

Blitz3D Forums/Blitz3D Programming/Inventory Type Array theory

In an RPG game...

I think I need a little help getting my head around the dimensions and structure for an array of types.

I wish to use an Array because I need to represent an inventory listing (of an amount of inventory 'Spaces') capable of holding an indefinite number of items. These inventories need to be linked to each Character (again, possibly an indefinite number of characters will need inventories), and also, some items (i.e. bags) will have smaller inventories of their own.

Hope this is understandable, it does gert confusing, I know.

I've not used Arrays mixed with Types before, so I'm not 100% on the syntax and stuff, hopefully by implementing Arrays it will make things much easier, more efficient and cleaner-looking. From my ideas so far (without arrays), here's some thoughts:


Type Character
Field Char_name$
Field Char_Inventory_Ref
EndType

Type Inventory
Field Inven_Reference
Field Number_of_Slots
Field Has_Inven
End Type

Type InvenSlots
Field Inven_Ref
Field Slot_Reference
Field Item_Reference
End Type



Note that I need to cross reference the "Inventory" field along with Slot_Reference numbers in the last Type where the actual contents of slots arew to be found. I think that eventually, because there will be approximately 10 000 different items, this will get quite slowed down, although the inventories will only need to be iterated through when the inventory screen is opened or an object is picked up/dropped/equipped/unequipped etc.

Please can someone help me to make this more efficient?

I'm not sure I understand exactly what you want but, personally, I'd simply maintain my own linked list of inventory items whithin each 'character' type:

Type characterT
  Field name$
  Field inventory.itemT
End Type

Type itemT
  Field name$
  Field contents.itemT
End Type


Here, the 'inventory' field is a linked list of all the items the character is carrying. Similarly, the 'contents' field of itemT is a list of all items contained within it - this is used for container items like bags, etc.

You will need to write functions to add/remove items from these lists but that's fairly straight forward.

I do it without arrays/linked lists... I just have a type for the items, and in this type I store which player the item belongs to in a field. When I want to show all the items of one player, I just loop trough all the items, and show the ones where that player is specified in that field.
Something like this :
for i=each item
	if i\pl=pl then
		; do stuff
	end if
next


Off course, this isn't the most performant way of doing it, so if you have 100's of items/players to loop trough (like in a mmorpg), another approach is better. But in my game there are only a limited amount of characters, which are all controlled by the player, and the items are only displayed in the menu and I only loop trough them when setting up that specific menu (not while displaying it, I just store the values in other types which are used by my menu system), so this is no problem. Hope this makes some sense :)
So I guess that's the easiest way of doing it, but not the most performant.

The consideration here is that different items may have inventories, and different characters may have different inventories and the inventories may change sizes depending on strength etc.

@Big10p

This is where I get confused, it's the use of the syntax and the "." in the middle of the FieldName that you use here:


 Field inventory.itemT

...

 Field contents.itemT


What exactly does this do and how do I use the functionality to best advantage?

Thanks!

Dots (.) are used to define the type that field will be an instance of. So in that case, "inventory" will be an instance of type "itemT".

I suggested the linked list method because I figured it would make use of container objects easier. You can put containers inside containers etc. Also, all items held in a container automatically get carried across when you move the container from one list to another. e.g. "The wizard gives the bag to the Elf".

That sounds jsut like what I need, Big10p

Il have to have a play around with it and might be posting back here, but thanks!

Malice, here's a small example of the linked list system I'm talking about. Implementing it wasn't actually as straight forward as I thought it would be. :) I decided to use doubly linked items (that point forward and backwards in the list) mainly to make removing items from lists easier.

Anyway, as it is, this system is pretty flexible and offers the following features:
- Each character has their own inventory which has no limit to the amount of items they can carry. In reality, you'll probably want to limit this by giving each character a maximum weight they can carry, or something.
- Containers can be put in containers with no limit on the level of nesting. I didn't want to complicate the code by identifying specific items as containers so you can actually put any item in any other item! You'll want to change this. :)
- Transfering a container item will automatically take all it's contents with it. This should work even if the container itself contains any number of further containers and items.


	Graphics 640,480,0,2

	Type characterT
		Field name$							; Descriptive name of character.
		Field inventory.itemT		; Points to first item in character's inventory list.
	End Type
	
	Type itemT
		Field name$							; Descriptive name of item.
		Field container.itemT		; Points to the container this item is held within.
		Field owner.characterT	; Points to the character who currently owns this item.
		Field contents.itemT		; Points to first item contained within this item.
		Field before_item.itemT	; Points to item before this one in linked list.
		Field after_item.itemT	; Points to item after this one in linked list.
	End Type
	
	; Create some characters.
	wizard.characterT = New characterT
	wizard\name$ = "Murthlok the Wizard"
	elf.characterT = New characterT
	elf\name$ = "Giffin the Elf"
	
	; Create some items.
	Const NUM_ITEMS%			= 7
	Const ITEM_FOOD%			= 0
	Const ITEM_STAFF%			= 1
	Const ITEM_BAG%				= 2
	Const ITEM_MAP%				= 3
	Const ITEM_SWORD%			= 4
	Const ITEM_BOX%				= 5
	Const ITEM_TALISMAN%	= 6
	Dim game_item.itemT(NUM_ITEMS-1)
	Restore item_data
	For n = 0 To NUM_ITEMS-1
		game_item(n) = New itemT
		Read game_item(n)\name$
	Next
	
	; Give everything to the wizard.
	add_item_to_inventory(game_item(ITEM_BAG), wizard)
	add_item_to_inventory(game_item(ITEM_STAFF), wizard)
	add_item_to_inventory(game_item(ITEM_FOOD), wizard)
	add_item_to_inventory(game_item(ITEM_MAP), wizard)
	add_item_to_inventory(game_item(ITEM_SWORD), wizard)
	add_item_to_inventory(game_item(ITEM_BOX), wizard)
	add_item_to_inventory(game_item(ITEM_TALISMAN), wizard)
	print_inventories()
	Color 255,0,0
	Print "Press a key to put the TALISMAN into the BOX."
	Print
	Color 255,255,255
	WaitKey()

	put_item_in_container(game_item(ITEM_TALISMAN), game_item(ITEM_BOX))
	print_inventories()
	Color 255,0,0
	Print "Press a key to put the BOX, FOOD and MAP into the BAG."
	Print
	Color 255,255,255
	WaitKey()

	put_item_in_container(game_item(ITEM_BOX), game_item(ITEM_BAG))
	put_item_in_container(game_item(ITEM_FOOD), game_item(ITEM_BAG))
	put_item_in_container(game_item(ITEM_MAP), game_item(ITEM_BAG))
	print_inventories()
	Color 255,0,0
	Print "Press a key to give the BAG and SWORD to the Elf"
	Print
	Color 255,255,255
	WaitKey()

	add_item_to_inventory(game_item(ITEM_BAG), elf)
	add_item_to_inventory(game_item(ITEM_SWORD), elf)
	print_inventories()
	Color 255,0,0
	Print "The Elf bogs off on an amazing adventure..."
	Color 255,255,255
	WaitKey()
	End


;
; Adds an item to a character's inventory linked list.
;
Function add_item_to_inventory(item.itemT, character.characterT)	

	release_item(item)

	If character\inventory <> Null Then character\inventory\before_item = item
	
	item\after_item = character\inventory
	character\inventory = item
	
	set_item_owner(item, character)
	
End Function


;
; Adds an item to a container item's contents linked list.
;
Function put_item_in_container(item.itemT, container.itemT)	

	release_item(item)

	If container\contents <> Null Then container\contents\before_item = item
	
	item\after_item = container\contents
	container\contents = item
	item\container = container
	
	set_item_owner(item, container\owner)
	
End Function


;
; Cleanly removes a given item from any linked list it may be part of.
;
Function release_item(item.itemT)

	If item\container <> Null ; Item is inside a container...

		If item\container\contents = item	; Item is first in containers contents list...
			item\container\contents = item\after_item
		EndIf

	ElseIf item\owner <> Null ; Item is in owner's direct inventory...
	
		If item\owner\inventory = item ; Item is first in owner's inventory list...
			item\owner\inventory = item\after_item
		EndIf
		
	Else
	
		Return
	
	EndIf

	If item\after_item <> Null Then item\after_item\before_item = item\before_item
	If item\before_item <> Null Then item\before_item\after_item = item\after_item

	item\container = Null
	item\before_item = Null
	item\after_item = Null

	set_item_owner(item, Null)

End Function


;
; Sets the owner of a given item.
; Note: this function is recursive so any items contained in the
; given item are also set to have the same owner.
;
Function set_item_owner(item.itemT, owner.characterT)

	While item <> Null

		item\owner = owner
		
		If item\contents <> Null Then set_item_owner(item\contents, owner)
	
		item = item\after_item

	Wend
	
End Function


;
; Prints all characters' inventories.
;
Function print_inventories()

	For character.characterT = Each characterT

		Print character\name$ + " is carrying:"
		print_items(character\inventory)
		Print

	Next

End Function


;
; Prints a given item's name
; Note: this function is recursive so any items contained in the
; given item are also printed.
;
Function print_items(item.itemT, tabs% = 2)

	If item = Null
		Print String$(" ",tabs) + "Nothing!"
		Return
	EndIf
	
	While item <> Null

		If item\contents <> Null
			Print String$(" ",tabs) + item\name$ + ", inside is:"
			print_items(item\contents, tabs + 2)
		Else
			Print String$(" ",tabs) + item\name$
		EndIf
	
		item = item\after_item

	Wend
		
End Function

	
.item_data
Data "Some food"
Data "An old staff"
Data "A leather bag"
Data "A map"
Data "A short sword"
Data "A small box"
Data "A ruby talisman"


Thanks Big10p! This is a great help! It should be easy to implement.

I had never had knowledge of accessing another Type's field as linked to another Type directly as dfescribed in this line for example:
Field inventory.itemT		


Thanks again!

You're welcome.

If you're not familiar with linked lists, you may find the code a bit confusing to begin with. I find it's helpful to actually draw the links between objects out on paper to get a better understanding of what points to what, etc. This is what I did when working out the system, before actually coding it. :)