RPG Inventory Control System
by Ed Johnson · in Torque Game Engine · 04/07/2005 (7:44 pm) · 14 replies
In a RPG programming book they had a section on making a map and character inventory tracker. All the inventory resources are really old and dont seem to be relevent anymore. I looked at Spock's C++ inventory to see how i'd implement a c++ inventory to torque's inner workings (script) its a bit complicated though. Since I bet some people out there are still tryin to get a good inventory system up, ill post this and see where it goes. it needs alot of work to get squeezed into torque, but i bet it'd be appreciated in the community.
If you'd like to see a working program using this, email me, it wouldnt be a problem.
Theres 2 parts, the MapICS handles object instances scattered around a map (with an X,Y,Z).. Each map would have a MapICS file list of items... (torque already has items that can be placed around, this is needed more:)
The CharICS handles personal inventory, with owners, containers, and sorting(using linked list)...
They both use special structures that track the MIL item numbers and maintains a linked list; and they both rely on a Master Item List or database of all game items, with each item having a unique identifier (number) that both the ICS's refer to. The MIL has single, unique instances while the ICS have many instances of any object.
Items belong to a map, a character, or a different item (a backpack). This means they need ownership, and an owner can have multiple instances of an object (i.e. coins). An owner's collection of items is an inventory list, and any number of instances of objects can be in it.
If you'd like to see a working program using this, email me, it wouldnt be a problem.
Theres 2 parts, the MapICS handles object instances scattered around a map (with an X,Y,Z).. Each map would have a MapICS file list of items... (torque already has items that can be placed around, this is needed more:)
The CharICS handles personal inventory, with owners, containers, and sorting(using linked list)...
They both use special structures that track the MIL item numbers and maintains a linked list; and they both rely on a Master Item List or database of all game items, with each item having a unique identifier (number) that both the ICS's refer to. The MIL has single, unique instances while the ICS have many instances of any object.
Items belong to a map, a character, or a different item (a backpack). This means they need ownership, and an owner can have multiple instances of an object (i.e. coins). An owner's collection of items is an inventory list, and any number of instances of objects can be in it.
#2
04/07/2005 (7:48 pm)
MapICS.cc - continued \\ map inventory control system, not really needed, torque does this well.BOOL cMapICS::Add(long ItemNum, long Quantity, \
float XPos, float YPos, float ZPos, \
sMapItem *OwnerItem)
{
sMapItem *Item;
// Create a new item structure
Item = new sMapItem();
// Insert into top of list
Item->Next = m_ItemParent;
if(m_ItemParent != NULL)
m_ItemParent->Prev = Item;
m_ItemParent = Item;
// Fill the item structure
Item->ItemNum = ItemNum;
Item->Quantity = Quantity;
Item->XPos = XPos;
Item->YPos = YPos;
Item->ZPos = ZPos;
Item->Parent = OwnerItem;
return TRUE;
}
BOOL cMapICS::Remove(sMapItem *Item)
{
sMapItem *ItemPtr, *NextItem;
// Remove child objects first
if((ItemPtr = m_ItemParent) != NULL) {
while(ItemPtr != NULL) {
NextItem = ItemPtr->Next;
if(ItemPtr->Parent == Item)
Remove(ItemPtr);
ItemPtr = NextItem;
}
}
// Remove from linked list and reset root
// if it's the current head of list.
if(Item->Prev != NULL)
Item->Prev->Next = Item->Next;
else
m_ItemParent = Item->Next;
if(Item->Next != NULL)
Item->Next->Prev = Item->Prev;
// Clear link list
Item->Prev = Item->Next = NULL;
// Free memory
delete Item;
return TRUE;
}
long cMapICS::GetNumItems()
{
return m_NumItems;
}
sMapItem *cMapICS::GetParentItem()
{
return m_ItemParent;
}
sMapItem *cMapICS::GetItem(long Num)
{
sMapItem *Item;
Item = m_ItemParent;
while(Num--) {
if(Item == NULL)
return NULL;
Item = Item->Next;
}
return Item;
}
long cMapICS::GetNextLong(FILE *fp)
{
char Buf[1024];
long Pos = 0;
int c;
// Read until EOF or EOL
while(1) {
if((c = fgetc(fp)) == EOF)
break;
if(c == 0x0a)
break;
if((c >= '0' && c <= '9') || c == '.' || c == '-')
Buf[Pos++] = c;
}
if(!Pos)
return -1;
Buf[Pos] = 0;
return atol(Buf);
}
float cMapICS::GetNextFloat(FILE *fp)
{
char Buf[1024];
long Pos = 0;
int c;
// Read until EOF or EOL
while(1) {
if((c = fgetc(fp)) == EOF)
break;
if(c == 0x0a)
break;
if((c >= '0' && c <= '9') || c == '.' || c == '-')
Buf[Pos++] = c;
}
Buf[Pos] = 0;
return (float)atof(Buf);
}
#3
04/07/2005 (7:49 pm)
MapICS.h \\ map inventory control system, not really needed, torque does this well.#ifndef _MAPICS_H_
#define _MAPICS_H_
typedef struct sMapItem
{
long ItemNum; // Master-Item-List item number
long Quantity; // Quantity of item (ie coins)
float XPos, YPos, ZPos; // Map coordinates
sMapItem *Prev, *Next; // linked list pointers
long Index; // This items index #
long Owner; // Owner index #
sMapItem *Parent; // Parent of a contained item
sMapItem()
{
Prev = Next = Parent = NULL;
Index = 0; Owner = -1;
}
~sMapItem() { delete Next; }
} sMapItem;
class cMapICS
{
private:
long m_NumItems; // # items in map
sMapItem *m_ItemParent; // Linked list parent map item
// Functions to read in next long or float # in file
long GetNextLong(FILE *fp);
float GetNextFloat(FILE *fp);
public:
cMapICS(); // Constructor
~cMapICS(); // Destructor
// Load, save, and free a list of map items
BOOL Load(char *Filename);
BOOL Save(char *Filename);
BOOL Free();
// Add and remove an item on map
BOOL Add(long ItemNum, long Quantity, \
float XPos, float YPos, float ZPos, \
sMapItem *OwnerItem = NULL);
BOOL Remove(sMapItem *Item);
// Retrieve # items or parent linked list object
long GetNumItems();
sMapItem *GetParentItem();
sMapItem *GetItem(long Num);
};
#endif
#4
04/07/2005 (7:50 pm)
CharICS.cc character inventory control system, currently loads from files, could be setup for external or local database..#include <windows.h>
#include <stdio.h>
#include "CharICS.h"
cCharICS::cCharICS()
{
m_NumItems = 0;
m_ItemParent = NULL;
}
cCharICS::~cCharICS()
{
Free();
}
BOOL cCharICS::Load(char *Filename)
{
FILE *fp;
long LongNum;
sCharItem *Item, *ItemPtr = NULL;
Free(); // Free a prior set
// Open the file
if((fp=fopen(Filename, "rb"))==NULL)
return FALSE;
// Loop forever reading in items
while(1) {
// Get next item number (break if no more items,
// which is represented by a return value of -1).
if((LongNum = GetNextLong(fp)) == -1)
break;
// Create a new item pointer and link it in
Item = new sCharItem();
if(ItemPtr == NULL)
m_ItemParent = Item;
else {
Item->Prev = ItemPtr;
ItemPtr->Next = Item;
}
ItemPtr = Item;
// Store MIL item number
Item->ItemNum = LongNum;
// Get quantity
Item->Quantity = GetNextLong(fp);
// Get owner #
Item->Owner = GetNextLong(fp);
// Save index # and increase count
Item->Index = m_NumItems++;
}
// Close the file
fclose(fp);
// Match objects that belong to others
ItemPtr = m_ItemParent;
while(ItemPtr != NULL) {
// Check if this item belongs to another
if(ItemPtr->Owner != -1) {
// Find matching parent item
Item = m_ItemParent;
while(Item != NULL) {
if(ItemPtr->Owner == Item->Index) {
// A match, point to parent
ItemPtr->Parent = Item;
break; // Stop scanning for parents
}
Item = Item->Next;
}
}
// Go to next item
ItemPtr = ItemPtr->Next;
}
return TRUE;
}
BOOL cCharICS::Save(char *Filename)
{
FILE *fp;
sCharItem *Item;
long Index = 0;
// Open the file
if((fp=fopen(Filename, "wb"))==NULL)
return FALSE;
// Assign index numbers to items
if((Item = m_ItemParent) == NULL) {
fclose(fp);
return TRUE; // no items to save
}
while(Item != NULL) {
Item->Index = Index++;
Item = Item->Next;
}
// Match child items to parents
Item = m_ItemParent;
while(Item != NULL) {
if(Item->Parent != NULL)
Item->Owner = Item->Parent->Index;
else
Item->Owner = -1;
Item = Item->Next;
}
// Save 'em out
Item = m_ItemParent;
while(Item != NULL) {
// Item number
fprintf(fp, "%lu\r\n", Item->ItemNum);
// Quantity
fprintf(fp, "%lu\r\n", Item->Quantity);
// Owner #
fprintf(fp, "%ld\r\n", Item->Owner);
// Next item
Item = Item->Next;
}
fclose(fp); // Close the file
return TRUE; // Return success!
}
#5
04/07/2005 (7:52 pm)
CharICS.cc - continued // character inventory control systemBOOL cCharICS::Free()
{
m_NumItems = 0;
delete m_ItemParent;
m_ItemParent = NULL;
return TRUE;
}
BOOL cCharICS::Add(long ItemNum, long Quantity, \
sCharItem *OwnerItem)
{
sCharItem *Item;
// Create a new item structure
Item = new sCharItem();
// Insert into top of list
Item->Next = m_ItemParent;
if(m_ItemParent != NULL)
m_ItemParent->Prev = Item;
m_ItemParent = Item;
// Fill the item structure
Item->ItemNum = ItemNum;
Item->Quantity = Quantity;
Item->Parent = OwnerItem;
return TRUE;
}
BOOL cCharICS::Remove(sCharItem *Item)
{
sCharItem *ItemPtr, *NextItem;
// Remove child objects first
if((ItemPtr = m_ItemParent) != NULL) {
while(ItemPtr != NULL) {
NextItem = ItemPtr->Next;
if(ItemPtr->Parent == Item)
Remove(ItemPtr);
ItemPtr = NextItem;
}
}
// Remove from linked list and reset root
// if it's the current head of list.
if(Item->Prev != NULL)
Item->Prev->Next = Item->Next;
else
m_ItemParent = Item->Next;
if(Item->Next != NULL)
Item->Next->Prev = Item->Prev;
// Clear link list
Item->Prev = Item->Next = NULL;
// Free memory
delete Item;
return TRUE;
}
long cCharICS::GetNumItems()
{
return m_NumItems;
}
sCharItem *cCharICS::GetParentItem()
{
return m_ItemParent;
}
sCharItem *cCharICS::GetItem(long Num)
{
sCharItem *Item;
Item = m_ItemParent;
while(Num--) {
if(Item == NULL)
return NULL;
Item = Item->Next;
}
return Item;
}
BOOL cCharICS::Arrange()
{
sCharItem *Item, *PrevItem;
// Start at top of linked list and float
// each item up that has a lesser ItemNum.
// Break if past bottom of list
Item = m_ItemParent;
while(Item != NULL) {
// Check previous item to float up
if(Item->Prev != NULL) {
// Keep floating up while prev item has
// a lesser ItemNum value or until top
// of list has been reached.
while(Item->Prev != NULL) {
PrevItem = Item->Prev; // Get prev item pointer
// Break if no more to float up
if(Item->ItemNum >= PrevItem->ItemNum)
break;
// Swap 'em
if((PrevItem = Item->Prev) != NULL) {
if(PrevItem->Prev != NULL)
PrevItem->Prev->Next = Item;
if((PrevItem->Next = Item->Next) != NULL)
Item->Next->Prev = PrevItem;
if((Item->Prev = PrevItem->Prev) == NULL)
m_ItemParent = Item;
PrevItem->Prev = Item;
Item->Next = PrevItem;
}
}
}
// Go to next object
Item = Item->Next;
}
return TRUE;
}
BOOL cCharICS::MoveUp(sCharItem *Item)
{
sCharItem *PrevItem;
// Swap item and item before it
if((PrevItem = Item->Prev) != NULL) {
if(PrevItem->Prev != NULL)
PrevItem->Prev->Next = Item;
if((PrevItem->Next = Item->Next) != NULL)
Item->Next->Prev = PrevItem;
if((Item->Prev = PrevItem->Prev) == NULL)
m_ItemParent = Item;
PrevItem->Prev = Item;
Item->Next = PrevItem;
}
return TRUE; // Return success
}
BOOL cCharICS::MoveDown(sCharItem *Item)
{
sCharItem *NextItem;
// Swap item and item after it
if((NextItem = Item->Next) != NULL) {
if((Item->Next = NextItem->Next) != NULL)
NextItem->Next->Prev = Item;
if((NextItem->Prev = Item->Prev) != NULL)
Item->Prev->Next = NextItem;
else
m_ItemParent = NextItem;
NextItem->Next = Item;
Item->Prev = NextItem;
}
return TRUE; // Return success
}
#6
04/07/2005 (7:52 pm)
CharICS.cc - continued // character inventory control systemlong cCharICS::GetNextLong(FILE *fp)
{
char Buf[1024];
long Pos = 0;
int c;
// Read until EOF or EOL
while(1) {
if((c = fgetc(fp)) == EOF)
break;
if(c == 0x0a)
break;
if((c >= '0' && c <= '9') || c == '.' || c == '-')
Buf[Pos++] = c;
}
if(!Pos)
return -1;
Buf[Pos] = 0;
return atol(Buf);
}
float cCharICS::GetNextFloat(FILE *fp)
{
char Buf[1024];
long Pos = 0;
int c;
// Read until EOF or EOL
while(1) {
if((c = fgetc(fp)) == EOF)
break;
if(c == 0x0a)
break;
if((c >= '0' && c <= '9') || c == '.' || c == '-')
Buf[Pos++] = c;
}
Buf[Pos] = 0;
return (float)atof(Buf);
}
#7
04/07/2005 (7:54 pm)
CharICS.h character inventory control system header.. struct for actual character inventory items#ifndef _CHARICS_H_
#define _CHARICS_H_
typedef struct sCharItem
{
long ItemNum; // Master-Item-List item number
long Quantity; // Quantity of item (ie coins,arrows,bullets)
sCharItem *Prev, *Next; // linked list pointers
long Index; // This items index #
long Owner; // Owner index # (player, npc, map)
sCharItem *Parent; // Parent of a contained item (treasure chest)
sCharItem()
{
Prev = Next = Parent = NULL;
Index = 0; Owner = -1;
}
~sCharItem() { delete Next; }
} sCharItem;
class cCharICS
{
private:
long m_NumItems; // # items in inventory
sCharItem *m_ItemParent; // Linked list parent item
// Functions to read in next long or float # in file
long GetNextLong(FILE *fp);
float GetNextFloat(FILE *fp);
public:
cCharICS(); // Constructor
~cCharICS(); // Destructor
// Load, save, and free a list of items
BOOL Load(char *Filename);
BOOL Save(char *Filename);
BOOL Free();
// Add and remove an item
BOOL Add(long ItemNum, long Quantity,
sCharItem *OwnerItem = NULL);
BOOL Remove(sCharItem *Item);
// Retrieve # items or parent linked list object
long GetNumItems();
sCharItem *GetParentItem();
sCharItem *GetItem(long Num);
// Re-ordering functions
BOOL Arrange();
BOOL MoveUp(sCharItem *Item);
BOOL MoveDown(sCharItem *Item);
};
#endif
#8
04/08/2005 (3:40 am)
Maybe post this as a resource rather than in the forums?
#9
04/08/2005 (1:18 pm)
Well its not complete, i'm hoping a few people will get this working with torque, then we could write up a resource and tutorial.
#10
04/08/2005 (1:20 pm)
C++ is definitely NOT the language for such a critter.
#11
04/08/2005 (1:35 pm)
Resource will be great...
#12
04/08/2005 (2:19 pm)
Why not? I'd think any large part of the game is better in C++ than script.
#13
04/08/2005 (4:53 pm)
Spocks is similar, using linked lists, and is in c++
#14
Best way to do inventory IMHO, is to manage it server side via script, and add a part to the inventory script, to notify the client via CommandToClient, having it store inventory in a global array, and tie the array to a GUI.
Easy, quick and can be implemented in < 30 mins except for the GUI which could take a little time to develop.
I have a whole series of tutorials coming up and one section deals with a complete inventory system that requires no tinkering under the hood.
Now if they will just get the things approved so I can finish the tuts :)
04/11/2005 (8:04 am)
This wouldn't work in a multiplayer environment, your client would be completely unaware of his inventory.Best way to do inventory IMHO, is to manage it server side via script, and add a part to the inventory script, to notify the client via CommandToClient, having it store inventory in a global array, and tie the array to a GUI.
Easy, quick and can be implemented in < 30 mins except for the GUI which could take a little time to develop.
I have a whole series of tutorials coming up and one section deals with a complete inventory system that requires no tinkering under the hood.
Now if they will just get the things approved so I can finish the tuts :)
Torque Owner Ed Johnson
#include <windows.h> #include <stdio.h> #include "MapICS.h" cMapICS::cMapICS() { m_NumItems = 0; m_ItemParent = NULL; } cMapICS::~cMapICS() { Free(); } BOOL cMapICS::Load(char *Filename) // loads inv list { FILE *fp; long LongNum; sMapItem *Item, *ItemPtr = NULL; Free(); // Free a prior set // Open the file if((fp=fopen(Filename, "rb"))==NULL) return FALSE; // Loop forever reading in items while(1) { // Get next item number (break if no more items, // which is represented by a return value of -1). if((LongNum = GetNextLong(fp)) == -1) break; // Create a new map pointer and link it in Item = new sMapItem(); if(ItemPtr == NULL) m_ItemParent = Item; else { Item->Prev = ItemPtr; ItemPtr->Next = Item; } ItemPtr = Item; // Store MIL item number Item->ItemNum = LongNum; // Get quantity Item->Quantity = GetNextLong(fp); // Get coordinates Item->XPos = GetNextFloat(fp); Item->YPos = GetNextFloat(fp); Item->ZPos = GetNextFloat(fp); // Get owner # Item->Owner = GetNextLong(fp); // Save index # and increase count Item->Index = m_NumItems++; } // Close the file fclose(fp); // Match objects that belong to others ItemPtr = m_ItemParent; while(ItemPtr != NULL) { // Check if this item belongs to another if(ItemPtr->Owner != -1) { // Find matching parent item Item = m_ItemParent; while(Item != NULL) { if(ItemPtr->Owner == Item->Index) { // A match, point to parent ItemPtr->Parent = Item; break; // Stop scanning for parents } Item = Item->Next; } } // Go to next item ItemPtr = ItemPtr->Next; } return TRUE; } BOOL cMapICS::Save(char *Filename) { FILE *fp; sMapItem *Item; long Index = 0; // Open the file if((fp=fopen(Filename, "wb"))==NULL) return FALSE; // Assign index numbers to items if((Item = m_ItemParent) == NULL) { fclose(fp); return TRUE; // no items to save } while(Item != NULL) { Item->Index = Index++; Item = Item->Next; } // Match child items to parents Item = m_ItemParent; while(Item != NULL) { if(Item->Parent != NULL) Item->Owner = Item->Parent->Index; else Item->Owner = -1; Item = Item->Next; } // Save 'em out Item = m_ItemParent; while(Item != NULL) { // Item number fprintf(fp, "%lu\r\n", Item->ItemNum); // Quantity fprintf(fp, "%lu\r\n", Item->Quantity); // Coordinates fprintf(fp, "%lf\r\n%lf\r\n%lf\r\n", Item->XPos, Item->YPos, Item->ZPos); // Owner # fprintf(fp, "%ld\r\n", Item->Owner); // Next item Item = Item->Next; } fclose(fp); // Close the file return TRUE; // Return success! } BOOL cMapICS::Free() { m_NumItems = 0; delete m_ItemParent; m_ItemParent = NULL; return TRUE; }