天行健 君子当自强而不息

游戏脚本的实现(2)

 

本篇是游戏脚本的实现(1)的续篇。

 

脚本条目的创建

因为ENREY_RULE结构仅包含了行为和条目的规则,所以需要另外的结构数组去存储每个条目的数据。这些新的结构包括了在条目中所使用的文本、布尔值、多重选项,我们使用结构体ENTRY来表示。

//============================================================================
// structure that store all entries fact information.
//============================================================================
typedef struct ENTRY
{
    
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;     // BOOL value
        long    long_value;     // long balue
        float   float_value;    // float value
    };

    
char*   text;       // entry text buffer

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

    ~ENTRY()
    {
        delete[] text;
    }
} *ENTRY_PTR;

在处理脚本条目的过程中,当一个脚本出现了许多条目时,最麻烦的问题也就接踵而来。脚本中的每个行为都要求一个相匹配的ENTRY_RULE结构,其依次包含了一定数量的ENTRY结构。为了更好地处理一个脚本的结构,还需要其他的结构来记录属于脚本行为的每个条目,我们将它命名为SCRPT。

//============================================================================
// structure that store all 
//============================================================================
typedef struct SCRIPT
{
    
long        action_index;   // [0, number of actions - 1]

    
long        num_entries;    // number of entries in this action
    ENTRY_PTR   entries;        // array of entries

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

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

    ~SCRIPT()
    {
        delete[] entries;
        delete next;
    }
} *SCRIPT_PTR;

prev和next维护了整个脚本的连接列表,为了构造SCRIPT结构的连接列表,从代表脚本的第一个行为的根结构开始,然后使用next和prev将SCRIPT结构连接起来,如下图所示:

 

ACTION_TEMPLATE类的整合

理解了行为模板所使用的结构以及所容纳的脚本后,现在开始将它们整合到一起创建一个加载并处理脚本的类。

//============================================================================
// This class encapsulate script save and load.
//============================================================================
typedef class ACTION_TEMPLATE
{
private:
    
long        m_num_actions;  // number of actions in template
    ACTION_PTR  m_root_action;  // list of template actions  
    
public:
    ACTION_TEMPLATE();
    ~ACTION_TEMPLATE();

    BOOL load_actions(
const char* filename);
    
void free();

    
long get_num_actions();
    ACTION_PTR get_root_action();
    ACTION_PTR get_action(
long act_index);

    SCRIPT_PTR create_script(
long act_index);

    
long get_num_entries_rule(long act_index);
    ENTRY_RULE_PTR get_entry_rule(
long act_index, long entry_rule_index);

    
void expand_default_action_text(char* buffer, ACTION_PTR action);
    BOOL expand_action_text(
char* buffer, SCRIPT_PTR script);
} *ACTION_TEMPLATE_PTR;

实现:

//------------------------------------------------------------------------
// Get quoted line from file.
//------------------------------------------------------------------------
static BOOL _get_quoted_line(FILE* fp, char* data, long max_data_size)
{
    
int c;
    
long pos = 0;

    
// read until a quote is reached (or EOF)
    while(1)
    {
        
if((c = fgetc(fp)) == EOF)
            
return FALSE;

        
if(c == '"')
        {
            
// read until next quot (or EOF)
            while(1)
            {
                
if((c = fgetc(fp)) == EOF)
                    
return FALSE;

                
// return text when 2nd quote found
                if(c == '"')
                {
                    data[pos] = 0;
                    
break;
                }

                
// add acceptable text to line
                if(c != 0x0a && c != 0x0d)  // if character is not linefeed, not carriage.
                {
                    
if(pos < max_data_size-1)
                        data[pos++] = c;
                }
            }

            
break;
        }
    }

    
return TRUE;
}

//------------------------------------------------------------------------
// Get word from file.
//------------------------------------------------------------------------
static BOOL _get_word(FILE* fp, char* data, long max_data_size)
{
    
int c;
    
long pos = 0;

    
// reset word to empty
    data[0] = 0;

    
// read until an acceptable character found
    while(1)
    {
        
if((c = fgetc(fp)) == EOF)
        {
            data[0] = 0;
            
return FALSE;
        }

        
// check for start of word
        if(c != 32 && c != 0x0a && c != 0x0d)   // if character is not blank, not linefeed, not carriage.
        {
            data[pos++] = c;
           
            
// loop until end of word (or EOF)
            while((c = fgetc(fp)) != EOF)
            {
                
// break on acceptable word seperators
                if(c == 32 || c == 0x0a || c == 0x0d)
                    
break;

                
// add if enough room left
                if(pos < max_data_size-1)
                    data[pos++] = c;
            }

            
// add end of line to text
            data[pos] = 0;

            
break;
        }
    }

    
return TRUE;
}

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

//------------------------------------------------------------------------
// Destructor, release allocated memory.
//------------------------------------------------------------------------
ACTION_TEMPLATE::~ACTION_TEMPLATE()
{
    free();
}

//------------------------------------------------------------------------
// Release allocated memory.
//------------------------------------------------------------------------
void ACTION_TEMPLATE::free()
{
    delete m_root_action;
    m_num_actions = 0;
}

//------------------------------------------------------------------------
// Load an action template.
//------------------------------------------------------------------------
BOOL ACTION_TEMPLATE::load_actions(const char* filename)
{
    
const int size = 256;

    
// free previous action structures
    free();

    FILE* fp;

    
// open the action file
    if((fp = fopen(filename, "rb")) == NULL)
        
return FALSE;

    ACTION_PTR act_ptr = NULL;
    
    
// keep looping until end of file found
    while(1)
    {
        
char text[size];

        
// get next quoted action
        if(! _get_quoted_line(fp, text, size))
            
break;

        
// quit if no action text
        if(text[0] == 0)
            
break;

        
// allocate on action structure and append it to list

        ACTION_PTR act = 
new ACTION;
        act->next = NULL;

        
if(act_ptr == NULL)
            m_root_action = act;
        
else
            act_ptr->next = act;

        act_ptr = act;

        
// copy action text
        strcpy(act->text, text);

        
// store action index
        act->index = m_num_actions;

        
// increase the number of actions loaded
        m_num_actions++;

        size_t text_len = strlen(text);

        
// count the number of entries in the action
        for(size_t i = 0; i < text_len; i++)
        {
            
if(text[i] == '~')
                act->num_entries_rule++;
        }

        
// allocated and read in entries (if any)
        if(act->num_entries_rule != 0)
        {
            act->entries_rule = 
new ENTRY_RULE[act->num_entries_rule];

            
for(short entry_index = 0; entry_index < act->num_entries_rule; entry_index++)
            {
                ENTRY_RULE_PTR entry_rule = &act->entries_rule[entry_index];

                
// get type of entry
                _get_word(fp, text, size);
               
                
if(!stricmp(text, "TEXT"))   // TEXT type, nothing data.
                {
                    entry_rule->type = ENTRY_TEXT;
                }
                
else if(!stricmp(text, "INT"))     // LONG type, get min and max values
                {
                    entry_rule->type = ENTRY_INT;
                    
                    
// get min value
                    _get_word(fp, text, size);
                    entry_rule->long_min = atol(text);

                    
// get max value
                    _get_word(fp, text, size);
                    entry_rule->long_max = atol(text);
                }
                
else if(!stricmp(text, "FLOAT"))    // FLOAT type, get min and max values
                {
                    entry_rule->type = ENTRY_FLOAT;

                    
// get min value
                    _get_word(fp, text, size);
                    entry_rule->float_min = (
float) atof(text);

                    
// get max value
                    _get_word(fp, text, size);
                    entry_rule->float_max = (
float) atof(text);
                }
                
else if(!stricmp(text, "BOOL"))     // BOOL type, no options.
                {
                    entry_rule->type = ENTRY_BOOL;
                }
                
else if(!stricmp(text, "CHOICE"))   // CHOICE type, get number of entries and entry's texts.
                {
                    entry_rule->type = ENTRY_CHOICE;

                    
// get the number of choices
                    _get_word(fp, text, size);

                    entry_rule->num_choices = atol(text);
                    entry_rule->choices = 
new char_ptr[entry_rule->num_choices];

                    
// get each entry text
                    for(long choice_index = 0; choice_index < entry_rule->num_choices; choice_index++)
                    {
                        _get_quoted_line(fp, text, size);
                        entry_rule->choices[choice_index] = strdup(text);
                    }
                }
            }
        }
    }

    fclose(fp);

    
return TRUE;
}

//------------------------------------------------------------------------
// Return number of actions in template.
//------------------------------------------------------------------------
long ACTION_TEMPLATE::get_num_actions()
{
    
return m_num_actions;
}

//------------------------------------------------------------------------
// Return root ACTION structure.
//------------------------------------------------------------------------
ACTION_PTR ACTION_TEMPLATE::get_root_action()
{
    
return m_root_action;
}

//------------------------------------------------------------------------
// Return specified ACTION structure.
//------------------------------------------------------------------------
ACTION_PTR ACTION_TEMPLATE::get_action(long act_index)
{
    
// return error if higher than number of actions
    if(act_index >= m_num_actions)
        
return NULL;

    ACTION_PTR act_ptr = m_root_action;
    
    
// scan list
    while(act_ptr)
    {
        
if(act_ptr->index == act_index)
            
return act_ptr;

        act_ptr = act_ptr->next;
    }

    
return NULL;
}

//------------------------------------------------------------------------
// Create script from specified action index.
//------------------------------------------------------------------------
SCRIPT_PTR ACTION_TEMPLATE::create_script(long act_index)
{
    
// make sure it is a valid action
    if(act_index >= m_num_actions)
        
return NULL;

    ACTION_PTR act_ptr;

    
// get pointer to action
    if((act_ptr = get_action(act_index)) == NULL)
        
return NULL;

    
// create new SCRIPT structure
    SCRIPT_PTR script = new SCRIPT;

    script->action_index = act_index;
    script->num_entries  = act_ptr->num_entries_rule;
    script->entries      = 
new ENTRY[script->num_entries];

    
// set up each entry
    for(long i = 0; i < script->num_entries; i++)
    {
        script->entries[i].type = act_ptr->entries_rule[i].type;

        
// set up entry data based on type
        switch(script->entries[i].type)
        {
        
case ENTRY_TEXT:
            script->entries[i].text = NULL;
            
break;

        
case ENTRY_INT:
            script->entries[i].long_value = act_ptr->entries_rule[i].long_min;
            
break;

        
case ENTRY_FLOAT:
            script->entries[i].float_value = act_ptr->entries_rule[i].float_min;
            
break;    

        
case ENTRY_BOOL:
            script->entries[i].bool_value = TRUE;
            
break;  

        
case ENTRY_CHOICE:
            script->entries[i].selection = 0;
            
break;
        }
    }

    
return script;
}

//------------------------------------------------------------------------
// Return number of entries rule in the specified action.
//------------------------------------------------------------------------
long ACTION_TEMPLATE::get_num_entries_rule(long act_index)
{
    
// get pointer to specified action
    ACTION_PTR act_ptr = get_action(act_index);

    
// return 0 if on error
    if(act_ptr == NULL)
        
return 0;

    
return act_ptr->num_entries_rule;
}

//------------------------------------------------------------------------
// Return specified entry rule in specified action.
//------------------------------------------------------------------------
ENTRY_RULE_PTR ACTION_TEMPLATE::get_entry_rule(long act_index, long entry_rule_index)
{
    ACTION_PTR act_ptr = get_action(act_index);

    
if(act_ptr == NULL || entry_rule_index >= act_ptr->num_entries_rule)
        
return NULL;

    
return &(act_ptr->entries_rule[entry_rule_index]);
}

//------------------------------------------------------------------------
// Expand action text using min/first/TRUE choice values.
//------------------------------------------------------------------------
void ACTION_TEMPLATE::expand_default_action_text(char* buffer, ACTION_PTR action)
{
    
// copy action text into buffer if no entries rule
    if(action->num_entries_rule == 0)
    {
        strcpy(buffer, action->text);
        
return;
    }

    
// expand entry types into action text
    size_t buf_pos = 0;
    
long rule_index = 0;

    size_t text_len = strlen(action->text);
    
const size_t mem_size = 256;

    
for(size_t i = 0; i < text_len; i++)
    {
        
char memory[mem_size];

        
// expand the entry into text based on value, text, etc.
        if(action->text[i] == '~')
        {
            
if(action->entries_rule[rule_index].type == ENTRY_TEXT)
            {
                memcpy(&buffer[buf_pos], "(*TEXT*)", 8);
                buf_pos += 8;
            }
            
else if(action->entries_rule[rule_index].type == ENTRY_INT)
            {
                sprintf(memory, "(*%lu*)", action->entries_rule[rule_index].long_min);
                memcpy(&buffer[buf_pos], memory, strlen(memory));
                buf_pos += strlen(memory);
            }
            
else if(action->entries_rule[rule_index].type == ENTRY_FLOAT)
            {
                sprintf(memory, "(*%lf*)", action->entries_rule[rule_index].float_min);
                memcpy(&buffer[buf_pos], memory, strlen(memory));
                buf_pos += strlen(memory);
            }
            
else if(action->entries_rule[rule_index].type == ENTRY_BOOL)
            {
                memcpy(&buffer[buf_pos], "(*TRUE*)", 8);
                buf_pos += 8;
            }
            
else if(action->entries_rule[rule_index].type == ENTRY_CHOICE)
            {
                memcpy(&buffer[buf_pos], "(*", 2);
                buf_pos += 2;

                
char* choice = action->entries_rule[rule_index].choices[0];
                size_t choice_len = strlen(choice);

                memcpy(&buffer[buf_pos], choice, choice_len);
                buf_pos += choice_len;

                memcpy(&buffer[buf_pos], "*)", 2);
                buf_pos += 2;
            }

            rule_index++;
        }
        
else
            buffer[buf_pos++] = action->text[i];
    }


    buffer[buf_pos] = 0;
}

//------------------------------------------------------------------------
// Expand action text using selections.
//------------------------------------------------------------------------
BOOL ACTION_TEMPLATE::expand_action_text(char* buffer, SCRIPT_PTR script)
{
    
// get a pointer to the specified action
    ACTION_PTR act_ptr = get_action(script->action_index);

    
if(act_ptr == NULL)
        
return FALSE;

    
// copy action text into buffer if no entries
    if(act_ptr->num_entries_rule == 0)
    {
        strcpy(buffer, act_ptr->text);
        
return TRUE;
    }

    
// expand entry types into action text

    size_t buf_pos = 0;
    size_t entry_index = 0;
    
    
char memory[256];
    size_t memory_length;    

    size_t act_text_length = strlen(act_ptr->text);

    
for(size_t i = 0; i < act_text_length; i++)
    {
        
// expand the entry into text based on values, text, etc.
        if(act_ptr->text[i] == '~')
        {
            
if(act_ptr->entries_rule[entry_index].type == ENTRY_TEXT && 
               script->entries[entry_index].type == ENTRY_TEXT)
            {                
                memcpy(&buffer[buf_pos], "(*", 2);
                buf_pos += 2;

                
if(script->entries[entry_index].text)
                {
                    
for(long j = 0; j < 32; j++)    // copy at most 32 charaters
                    {
                        
if(script->entries[entry_index].text[j] == 0)
                            
break;

                        buffer[buf_pos++] = script->entries[entry_index].text[j];
                    }
                }

                memcpy(&buffer[buf_pos], "*)", 2);
                buf_pos += 2;                
            }
            
else if(act_ptr->entries_rule[entry_index].type == ENTRY_INT)
            {
                sprintf(memory, "(*%lu*)", script->entries[entry_index].long_value);
                
                memory_length = strlen(memory);

                memcpy(&buffer[buf_pos], memory, memory_length);
                buf_pos += memory_length;
            }
            
else if(act_ptr->entries_rule[entry_index].type == ENTRY_FLOAT)
            {
                sprintf(memory, "(*%lf*)", script->entries[entry_index].float_value);
                
                memory_length = strlen(memory);

                memcpy(&buffer[buf_pos], memory, memory_length);
                buf_pos += memory_length;
            }
            
else if(act_ptr->entries_rule[entry_index].type == ENTRY_BOOL)
            {
                
if(script->entries[entry_index].bool_value)
                    memcpy(&buffer[buf_pos], "(*TRUE *)", 9);
                
else
                    memcpy(&buffer[buf_pos], "(*FALSE*)", 9);

                buf_pos += 9;
            }
            
else if(act_ptr->entries_rule[entry_index].type == ENTRY_CHOICE)
            {
                memcpy(&buffer[buf_pos], "(*", 2);
                buf_pos += 2;

                
long sel = script->entries[entry_index].selection;
                
char* choice = act_ptr->entries_rule[entry_index].choices[sel];
                size_t choice_len = strlen(choice);

                memcpy(&buffer[buf_pos], choice, choice_len);
                buf_pos += choice_len;

                memcpy(&buffer[buf_pos], "*)", 2);
                buf_pos += 2;
            }

            entry_index++;
        }
        
else
            buffer[buf_pos++] = act_ptr->text[i];
    }

    buffer[buf_pos] = 0;

    
return TRUE;
}

posted on 2007-11-03 17:05 lovedday 阅读(552) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论