游戏中物件的定义与使用(8)

游戏中物件的定义与使用(8)

 

本篇是游戏中物件的定义与使用(7)的续篇。


Developing a Character ICS

Although developing a character’s inventory system might make you cringe at first,
let me reassure you that it’s not much different from developing a map inventory
control system. You have the ability to add and remove items, but you don’t have the
problem of dealing with the item coordinates on the map. Instead, a player’s ICS
keeps track of order, which means that players can rearrange the items as they see fit.


Of course, this ordering is just a matter of arranging the linked list, but what about
the items that can hold other items, such as backpacks? As long as you properly categorize
the items as containers in the MIL Editor, you don’t need to worry.


Speaking of categorizing, the real magic happens when you want to equip or use
an item. Because each item is categorized, the character ICS can quickly determine
what to do with the item in question. If the item is a weapon, the character ICS can
ask to equip the item. If it’s a healing item, the player can consume it. Beginning
to get the idea?

Finally, a character ICS should allow the player to examine objects, which is the reason
for the mesh and image parameters in the MIL Editor. Whenever the game’s
player examines the object, the specific mesh or image is loaded and displayed.
Now, turn your attention to putting together a character ICS and example by using
the ICS.

 

Defining the cCharICS Class

Interface:

//==================================================================================
// This structure contains char item information list.
//==================================================================================
typedef  struct  sCharItem
{
    
long     item_index;      // MIL item index
     long     quantity;        // quantity of item (ie coins)    

    sCharItem*   prev;
    sCharItem*   next;

    
long     index;            // map item index
     long     owner;

    sCharItem*   parent;     
// parent of a contained item
    
    sCharItem()
    {
        memset(
this , 0,  sizeof (* this ));
        owner = -1;
    }

    ~sCharItem()
    {
        delete next;   
    }
} *sCharItemPtr;

//==================================================================================
// This class encapsulate char inventory contrl system for character.
//==================================================================================
typedef  class  cCharIcs
{
private :
    
long          m_num_items;
    sCharItemPtr m_root_item;

private :
    
long     get_next_long(FILE* fp);
    
float    get_next_float(FILE* fp);

public :
    cCharIcs();
    ~cCharIcs();

    
bool  load( const   char * filename);
    
bool  save( const   char * filename);
    
void  free();

    
void  add( long  item_index,  long  quantity, sCharItemPtr owner_item);
    
void  remove(sCharItemPtr item);

    
long  get_num_items();
    sCharItemPtr get_root_item();
    sCharItemPtr get_item(
long  index);

    
void  sort();
    
bool  move_up(sCharItemPtr item);
    
bool  move_down(sCharItemPtr item);
} *cCharIcsPtr;
 

Much like the cMapICS class, the cCharICS class uses a special structure (sCharItem) that
tracks the MIL item numbers and quantity and maintains a linked list. Unlike the
sMapItem structure, however, sCharItem doesn’t care about the item’s coordinates.

Implement:

//----------------------------------------------------------------------------
// Get numeric value from file.
//----------------------------------------------------------------------------
static   void  get_numeric_value(FILE* fp,  char * buf,  int  size)
{
    
long  pos;
    
int  c;

    
// read until EOF or EOL or over buffer
     for (pos = 0; (c = fgetc(fp)) != EOF && c != 0x0a && pos != size-1; pos++)
    {
        
if ((c >= '0' && c <= '9') || c == '.' || c == '-')
            buf[pos] = c;
    }

    buf[pos] = 0;
}

//----------------------------------------------------------------------------
// Constructor, initialize member data.
//----------------------------------------------------------------------------
cCharIcs::cCharIcs()
{
    memset(
this , 0,  sizeof (* this ));
}

//----------------------------------------------------------------------------
// Destructor, free allocated resource. 
//----------------------------------------------------------------------------
cCharIcs::~cCharIcs()
{
  free();
}

//----------------------------------------------------------------------------
// free allocate resource.
//----------------------------------------------------------------------------
void  cCharIcs::free()
{
  m_num_items = 0;

  delete m_root_item;  
}

//----------------------------------------------------------------------------
// Load all char items from file.
//----------------------------------------------------------------------------
bool  cCharIcs::load( const   char * filename)
{
    free();     
// free a prior set

    FILE* fp;

    
if ((fp = fopen(filename, "rb")) == NULL)
        
return   false ;    

    sCharItemPtr item;
    sCharItemPtr item_ptr = NULL;

    
// loop forever reading in items
     while (1)
    {
        
long  item_index;

        
// get next item number (break if no more items, which is represented by a return value of -1).
         if ((item_index = get_next_long(fp)) == -1)
            
break ;

        
// create a new map item and link it in

        item = 
new  sCharItem;

        
if (item_ptr == NULL)
            m_root_item = item;
        
else
        {
            item->prev = item_ptr;
            item_ptr->next = item;
        }

        item_ptr = item;

        item->item_index  = item_index;
        item->quantity    = get_next_long(fp);      
        item->owner       = get_next_long(fp);
        item->index       = m_num_items++;
    }

    fclose(fp);

    
// match objects that belong to others
     for (item_ptr = m_root_item; item_ptr != NULL; item_ptr = item_ptr->next)
    {
        
// check if this item belongs to another
         if (item_ptr->owner == -1)
            
continue ;

        
// find matching parent item
         for (item = m_root_item; item != NULL; item = item->next)
        {
            
if (item_ptr->owner == item->index)
            {
                
// a match, point to parent and stop scanning for parents.
                item_ptr->parent = item;
                
break ;
            }
        }
    }

    
return   true ;
}

//----------------------------------------------------------------------------
// Save all char items to file.
//----------------------------------------------------------------------------
bool  cCharIcs::save( const   char * filename)
{
    FILE* fp;

    
if ((fp = fopen(filename, "wb")) == NULL)
        
return   false ;
     
    
long  index = 0;

    
for (sCharItemPtr item = m_root_item; item != NULL; item = item->next)
    {
        item->index = index++;

        
// match child items to parents
         if (item->parent)
            item->owner = item->parent->index;
        
else
            item->owner = -1;

        fprintf(fp, "%lu\r\n%lu\r\n%ld\r\n", item->item_index, item->quantity, item->owner);
    }
    
    fclose(fp);

    
return   true ;
}

//----------------------------------------------------------------------------
// Add char item into list.
//----------------------------------------------------------------------------
void  cCharIcs::add( long  item_index,  long  quantity, sCharItemPtr owner_item)
{
    sCharItemPtr item = 
new  sCharItem;

    
// fill the item structure
    item->item_index = item_index;
    item->quantity   = quantity;
    item->parent     = owner_item;

    
// insert into top of list

    item->next = m_root_item;

    
if (m_root_item)
        m_root_item->prev = item;

    m_root_item = item;
}

//----------------------------------------------------------------------------
// Remove char item from list.
//----------------------------------------------------------------------------
void  cCharIcs::remove(sCharItemPtr item)
{    
    sCharItemPtr next_item;

    
// remove child objects first
     for (sCharItemPtr item_ptr = m_root_item; item_ptr != NULL; item_ptr = next_item)
    {
        next_item = item_ptr->next;

        
if (item_ptr->parent == item)
            remove(item_ptr);
    }

    
// remove from linked list and reset root if it's the current head of list.
    
    
if (item->prev)
        item->prev->next = item->next;
    
else
        m_root_item = item->next;

    
if (item->next)
        item->next->prev = item->prev;

    item->prev = item->next = NULL;
    delete item;
}

//----------------------------------------------------------------------------
// Return number of map items.
//----------------------------------------------------------------------------
long  cCharIcs::get_num_items()
{
    
return  m_num_items;
}

//----------------------------------------------------------------------------
// Return root map item.
//----------------------------------------------------------------------------
sCharItemPtr cCharIcs::get_root_item()
{
    
return  m_root_item;
}

//----------------------------------------------------------------------------
// Return map item with specified index.
//----------------------------------------------------------------------------
sCharItemPtr cCharIcs::get_item( long  index)
{
    sCharItemPtr item;

    
// loop until reached item index
     for (item = m_root_item; item != NULL && index != 0; item = item->next)
        index--;

    
return  item;
}

//----------------------------------------------------------------------------
// Return long value from next line in file.
//----------------------------------------------------------------------------
long  cCharIcs::get_next_long(FILE *fp)
{
    
char  buf[1024];
    get_numeric_value(fp, buf, 
sizeof (buf));

    
if (strlen(buf) == 0)     // if there is no long value in file
         return  -1;

    
return  atol(buf);
}

//----------------------------------------------------------------------------
// Return float value from next line in file.
//----------------------------------------------------------------------------
float  cCharIcs::get_next_float(FILE *fp)
{
    
char  buf[1024];
    get_numeric_value(fp, buf, 
sizeof (buf));

    
return  ( float )atof(buf);
}

The cCharICS class, again, is much like its cMapICS counterpart, except for the addition
of three more public functions— sort, move_up, and move_down. You use these functions
to sort the character’s list of items. Their code is as follows:

//----------------------------------------------------------------------------
// Arrange char ics.
//----------------------------------------------------------------------------
void  cCharIcs::sort()
{
    
// start at top of linked list and float each item up that has a lesser item_index,
    // break if past bottom of list.
     for (sCharItemPtr item = m_root_item; item != NULL; item = item->next)
    {
        
// keep floating up while previous item has a lesser item_index value or
        // until top of list has been rearched.
         for (sCharItemPtr prev_item = item->prev; prev_item != NULL; prev_item = item->prev)
        {
            
// break if no more to float up
             if (prev_item->item_index <= item->item_index)
                
break ;

            
// swap element

            
if (prev_item->prev)
                prev_item->prev->next = item;

            
if ((prev_item->next = item->next) != NULL)
                item->next->prev = prev_item;

            
if ((item->prev = prev_item->prev) == NULL)
                m_root_item = item;
            
            prev_item->prev = item;
            item->next = prev_item;
        }
    }
}

//----------------------------------------------------------------------------
// Move one char item up.
//----------------------------------------------------------------------------
bool  cCharIcs::move_up(sCharItemPtr item)
{
    sCharItemPtr prev_item = item->prev;

    
if (prev_item == NULL)
        
return  FALSE;

    
// swap item and item before it

    
if (prev_item->prev)
        prev_item->prev->next = item;

    
if ((prev_item->next = item->next) != NULL)
        item->next->prev = prev_item;

    
if ((item->prev = prev_item->prev) == NULL)
        m_root_item = item;

    prev_item->prev = item;
    item->next = prev_item;

    
return  TRUE;
}

//----------------------------------------------------------------------------
// Move one char item down.
//----------------------------------------------------------------------------
bool  cCharIcs::move_down(sCharItemPtr item)
{
    sCharItemPtr next_item = item->next;

    
if (next_item == NULL)
        
return  FALSE;

    
// swap item and item after it

    
if ((item->next = next_item->next) != NULL)
        next_item->next->prev = item;

    
if ((next_item->prev = item->prev) != NULL)
        item->prev->next = next_item;
    
else
        m_root_item = next_item;

    next_item->next = item;
    item->prev = next_item;

    
return  TRUE;
}

Function 'sort' sorts the linked list of items based on each item’s MIL item number, from
lowest to highest. If, on the other hand, you want to specifically order the list yourself,
you can utilize the move_up and move_down functions, which take a pointer to a
sCharItem structure that is already contained in the list.

The move_up function moves the specified sItem structure up in the linked list, and
move_down moves the specified structure down in the linked list. Figure 15.10 illustrates
the concept of using the Arrange, move_up, and move_down functions on a sample
linked list of items.

游戏中物件的定义与使用(8)_第1张图片

The rest of the functions in cCharICS are identical in their functionality to the
cMapICS class, with the obvious exclusion of the item coordinates used when adding
an item to the list. Even the storage format for character items is identical to the
map item format, except for the coordinates.

 

Using the cCharICS Class

To demonstrate the use of the character ICS system, I created a demo application
named CharICS that maintains a list of items contained in the default.mil master
item list file. When you start the demo, you’ll see the
Inventory dialog box (shown in following snap). In the Inventory dialog box, the list
box contains an imaginary character’s inventory, which you can manipulate by
using the buttons on the dialog box.

游戏中物件的定义与使用(8)_第2张图片

To use the CharICS demo, follow these steps:


1. Click the Add Item button. The CharICS demo will add a random item from
the MIL to the inventory list.


2. Select an item from the list. Items are classified, so the Use and Equip buttons
are enabled or disabled depending on which item you select from the
list. Clicking Equip has no effect; it comes into play later when you deal with
characters. Clicking Use, however, removes an item if it is flagged as USEONCE.

3. Click the Drop button to drop an item (remove the item from the inventory
list) if it is flagged as CANDROP.


4. In order to arrange the items in the inventory, you can click an item and
then click either Sort Up or Sort Down. The selected item then moves up or
down in the list. To arrange the items by their item number in the list, click
Sort.


As a last, special treat, items that have matching meshes appear in the box on the
right in the demo. The 3-D object spins around slowly so that you have a full view
of all sides. Now, that’s cool!


你可能感兴趣的:(游戏中物件的定义与使用(8))