Controlling Players and Characters(15)

Controlling Players and Characters(15)

 

To see more details about game script,see 游戏脚本的实现。

 

Scripting and Characters


Scripting keeps popping its head up throughout these last few chapters, and true
to form, scripting plays a major role when dealing with characters. Scripts work
with conversations, spells, character movement, and much more.


What you need at this point is a clean-cut method of processing the game scripts.
The best way to do this is to create a class to entwine into your character and other
application processing.

 

The Script Class

it becomes even easier to execute scripts when you put the whole script processing in a class.

enum  ENTRY_TYPE { ENTRY_NONE = 0, ENTRY_TEXT, ENTRY_BOOL, ENTRY_INT, ENTRY_FLOAT, ENTRY_CHOICE };

typedef 
struct  sEntry
{
    
long     type;                // type of blank entry (ENTRY_TEXT, ENTRY_BOOL, )

    union
    {
        
long     io_value;        // used for saving/loading
         long     length;          // length of text (0 terminator)
         long     selection;       // selection in choice
        BOOL    bool_value;     
        
long     long_value;     
        
float    float_value;    
    };

    
char *   text;                // entry text buffer

    sEntry()
    {
        memset(
this , 0,  sizeof (* this ));
    }

    ~sEntry()
    {
        delete[] text;
    }
} *sEntryPtr;

////////////////////////////////////////////////////////////////////////////////////

typedef 
struct  sScriptInfo
{
    
long         action_index;    // [0, number of actions - 1]

    
long         num_entries;     // number of entries in this action
    sEntry*     entries;         // array of entries

    sScriptInfo*     prev;      
// previous in linked list
    sScriptInfo*     next;       // next in linked list

    sScriptInfo()
    {
        memset(
this , 0,  sizeof (* this ));
    }

    ~sScriptInfo()
    {
        delete[] entries;
        delete next;
    }
} *sScriptInfoPtr;

////////////////////////////////////////////////////////////////////////////////////

typedef 
class  cScript
{
private :
    
long             m_num_actions;
    sScriptInfo*    m_root_script_info;

private :
    
virtual   bool  prepare() {  return   true ; }
    
virtual   bool  release() {  return   true ; }

    
virtual  sScriptInfo* process(sScriptInfo* script_info)
    {
        
return  script_info->next;
    }

public :
    cScript()
    {
        m_num_actions = 0;
        m_root_script_info = NULL;
    }

    ~cScript()
    {
        free();
    }

    
void  free()
    {
        delete m_root_script_info;
        m_root_script_info = NULL;

        m_num_actions = 0;
    }

    sScriptInfo* get_root_script_info()
    {
        
return  m_root_script_info;
    }
    
    
bool  load( const   char * filename);
    
void  execute( const   char * filename);    
} *cScriptPtr;

Although deceptively small, the cScript class packs a punch. Loading a script is
accomplished via the Load function. Once it’s loaded, you can process the script
with a call to Execute. If you don’t want to hassle with loading a script
before processing, a call to the Execute function takes a script file
to load and execute in the same function call (plus it frees the script when execution is complete).

bool  cScript::load( const   char * filename)
{
    free();

    FILE* fp;

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

    fread(&m_num_actions, 1, 
sizeof (m_num_actions), fp);

    sScriptInfo* head = NULL;
    sScriptInfo* ptr = NULL;

    
// loop through each script action
     for ( long  i = 0; i < m_num_actions; i++)
    {
        
// allocate a script structure and link in

        sScriptInfo* info = 
new  sScriptInfo;
        info->next = NULL;

        
if (ptr == NULL)
            head = info;
        
else
            ptr->next = info;

        ptr = info;

        fread(&info->action_index, 1, 
sizeof (info->action_index), fp);
        fread(&info->num_entries, 1, 
sizeof (info->num_entries), fp);

        
// get entry data (if any)
         if (info->num_entries)
        {
            info->entries = 
new  sEntry[info->num_entries];

            
// load in each entry
             for ( long  j = 0; j < info->num_entries; j++)
            {
                fread(&info->entries[j].type, 1, 
sizeof (info->entries[j].type), fp);
                fread(&info->entries[j].io_value, 1, 
sizeof (info->entries[j].io_value), fp);

                
// get text (if any)
                 if (info->entries[j].type == ENTRY_TEXT && info->entries[j].length)
                {
                    info->entries[j].text = 
new   char [info->entries[j].length];
                    fread(info->entries[j].text, 1, info->entries[j].length, fp);
                }
            }
        }
    }

    fclose(fp);

    m_root_script_info = head;

    
return   true ;
}

void  cScript::execute( const   char * filename)
{
    
// load script if none already
     if (filename)
        load(filename);

    
// prepare script data for execution
     if (! prepare())
        
return ;

    
// start at beginning of script
    sScriptInfo* ptr = m_root_script_info;
    
if (ptr == NULL)
        
return ;
    
    
// loop until no more script actions
     while (ptr)
    {
        
// Call script function and break on NULL return value.
        // Any other return type is the pointer to the next function, which is typically ptr->next.
        ptr = process(ptr);
    }

    release();

    
// release script if execute loaded it
     if (filename)
        free();
}

TIP
Using the Load function to load a script is useful if the script is processed many times
because you don’t have to free it between uses. Loading a script within the Execute
function forces the script to be loaded and freed every time, wasting precious time.

The way the cScript class processes scripts is ingenious. You actually have to derive the cScript class to
parse each script action as it is processed. That’s the purpose of the Process function.
Once a script is loaded, the Process function is called for every script action to process.

Each script pointer is queried for the script action number, and you must decide
what to do with the action. Then you need to update the script pointers by returning
the pointer to the next script action in the linked list.


你可能感兴趣的:(Controlling Players and Characters(15))