天行健 君子当自强而不息

游戏脚本的实现(6)

 

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

 

事实上,创建可接受脚本的游戏引擎对于大多数的游戏而言,将产生出一个非常开放的源代码以及高效率的项目。


 

Mad Lib Scripts的执行

窗口设计:

resouce.h:

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by MlsDemo.rc
//
#define IDD_DEMO                        101
#define IDC_LOAD                        1000
#define IDC_EXECUTE                     1001
#define IDC_SCRIPT                      1002
#define IDC_TITLE                       1003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1004
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

MlsDemo.rc:

 
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_DEMO DIALOGEX 0, 0, 356, 198
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MLS Demo"
CLASS "MLSDEMO"
FONT 12, "Segoe UI", 400, 0, 0x0
BEGIN
    PUSHBUTTON      "Load Script",IDC_LOAD,254,173,43,14
    PUSHBUTTON      "Execute",IDC_EXECUTE,306,173,43,14
    LISTBOX         IDC_SCRIPT,7,5,342,162,NOT LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
    CONTROL         "",IDC_TITLE,"Static",SS_LEFTNOWORDWRAP,7,175,232,14
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO 
BEGIN
    IDD_DEMO, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 349
        TOPMARGIN, 7
        BOTTOMMARGIN, 191
    END
END
#endif    // APSTUDIO_INVOKED


#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

#endif    // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

实现:

 
/*************************************************************************
PURPOSE:
    Mad Lib Scripting demo.
*************************************************************************/


#include <windows.h>
#include <stdio.h>
#include <memory.h>
#include "resource.h"

#pragma warning(disable : 4996)

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

//============================================================================
// 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;

//============================================================================
// 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;

///////////////////////////////////// function declaration /////////////////////////////////////

SCRIPT_PTR script_if_flag_then(SCRIPT_PTR script);
SCRIPT_PTR script_else(SCRIPT_PTR script);
SCRIPT_PTR script_endif(SCRIPT_PTR script);
SCRIPT_PTR script_set_flag(SCRIPT_PTR script);
SCRIPT_PTR script_print(SCRIPT_PTR script);
SCRIPT_PTR script_move(SCRIPT_PTR script);
SCRIPT_PTR script_gain_loss(SCRIPT_PTR script);
SCRIPT_PTR script_battle(SCRIPT_PTR script);
SCRIPT_PTR script_end(SCRIPT_PTR script);

LRESULT CALLBACK window_proc(HWND hwnd, UINT msg_id, WPARAM word_param, LPARAM long_param);
SCRIPT_PTR load_script_from_file(
const char* filename);
BOOL run_script();

void reset_listbox(HWND listbox);
LRESULT add_string_to_listbox(HWND listbox, 
const charstring);

///////////////////////////////////// global variables /////////////////////////////////////

HWND g_hwnd;

SCRIPT_PTR      g_root_script;

BOOL            g_flags[256];

OPENFILENAME    g_ofn;
char            g_script_file[MAX_PATH];

typedef SCRIPT_PTR (*SCRIPT_FUNC)(SCRIPT_PTR script);

SCRIPT_FUNC g_script_list[] =
{
     script_if_flag_then,
     script_else,
     script_endif,
     script_set_flag,
     script_print,
     script_move,
     script_gain_loss,
     script_battle,
     script_end,
};

//-----------------------------------------------------------------------------------
// Routine entry.
//-----------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
    
const char* class_name = "MLSDEMO";

    WNDCLASS win_class;

    
// create window class and register it    
    win_class.style         = CS_HREDRAW | CS_VREDRAW;
    win_class.lpfnWndProc   = window_proc;
    win_class.cbClsExtra    = 0;
    win_class.cbWndExtra    = DLGWINDOWEXTRA;
    win_class.hInstance     = inst;
    win_class.hIcon         = LoadIcon(inst, IDI_APPLICATION);
    win_class.hCursor       = LoadCursor(NULL, IDC_ARROW);
    win_class.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    win_class.lpszMenuName  = NULL;
    win_class.lpszClassName = class_name;    

    
if(! RegisterClass(&win_class))
        
return FALSE;
   
    
// The CreateDialog macro creates a modeless dialog box from a dialog box template resource. 
    // The CreateDialog macro uses the CreateDialogParam function.
    //
    // HWND CreateDialog(          
    //    HINSTANCE hInstance,
    //    LPCTSTR lpTemplate,
    //    HWND hWndParent,
    //    DLGPROC lpDialogFunc);
    //
    // hInstance:
    //        [in] Handle to the module whose executable file contains the dialog box template. 
    //
    // lpTemplate
    //        [in] Specifies the dialog box template. This parameter is either the pointer to a null-terminated 
    //        character string that specifies the name of the dialog box template or an integer value that 
    //        specifies the resource identifier of the dialog box template. If the parameter specifies a resource 
    //        identifier, its high-order word must be zero and its low-order word must contain the identifier. 
    //        You can use the MAKEINTRESOURCE macro to create this value. 
    //
    // hWndParent:
    //        [in] Handle to the window that owns the dialog box. 
    //
    // lpDialogFunc:
    //        [in] Pointer to the dialog box procedure. For more information about the dialog box procedure, 
    //        see DialogProc. 
    //
    // Return Value:
    //    If the function succeeds, the return value is the handle to the dialog box.
    //    If the function fails, the return value is NULL. To get extended error information, call GetLastError.

    // Create the dialog box window and show it

    g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_DEMO), 0, NULL);

    ShowWindow(g_hwnd, cmd_show);
    UpdateWindow(g_hwnd);

    MSG msg;

    
// message loop
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    delete g_root_script;

    UnregisterClass(class_name, inst);

    
return 0;
}

//------------------------------------------------------------------------------------------------
// Main window procedure.
//------------------------------------------------------------------------------------------------
LRESULT CALLBACK window_proc(HWND hwnd, UINT msg_id, WPARAM word_param, LPARAM long_param)
{
    
switch(msg_id)
    {
    
case WM_COMMAND:
        
switch(LOWORD(word_param))
        {
        
case IDC_LOAD:  // load a script file
            if(! GetOpenFileName(&g_ofn))
                
break;

            
// delete the current script
            delete g_root_script;

            
if((g_root_script = load_script_from_file(g_script_file)) == NULL)
                MessageBox(hwnd, g_script_file, "Unalbe to open file.", MB_OK);

            
// display script filename
            SetWindowText(GetDlgItem(g_hwnd, IDC_TITLE), g_script_file);

            
break;

        
case IDC_EXECUTE:   // run a script file
            run_script();
            
break;
        }

        
break;

    
case WM_CREATE:
        
// initialize the save/load dialog box information
        ZeroMemory(&g_ofn, sizeof(OPENFILENAME));

        g_ofn.lStructSize   = 
sizeof(OPENFILENAME);
        g_ofn.nMaxFile      = MAX_PATH;        
        g_ofn.Flags         = OFN_HIDEREADONLY;
        g_ofn.lpstrFile     = g_script_file;
        g_ofn.lpstrTitle    = "Load Script File";
        g_ofn.lpstrFilter   = "MLS Script Files (*.mls)\0*.mls\0All Files (*.*)\0*.*\0\0";
        g_ofn.lpstrDefExt   = "mls";
        g_ofn.nMaxFileTitle = MAX_PATH;

        
// set default open path
        strcpy(g_script_file, "..\\data\\test.mls");

        reset_listbox(GetDlgItem(g_hwnd, IDC_SCRIPT));

        
break;

    
case WM_DESTROY:
        PostQuitMessage(0);
        
break;

    
default:
        
return DefWindowProc(hwnd, msg_id, word_param, long_param);
    }

    
return 0;
}

//----------------------------------------------------------------------------------------
// Load scripts from file and return root script.
//----------------------------------------------------------------------------------------
SCRIPT_PTR load_script_from_file(const char* filename)
{
    FILE* fp;

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

    
long num_script;
    fread(&num_script, 1, 
sizeof(long), fp);

    SCRIPT_PTR root_script;
    SCRIPT_PTR script_ptr = NULL;

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

        SCRIPT_PTR script = 
new SCRIPT;
        script->next = NULL;

        
if(script_ptr == NULL)
            root_script = script;
        
else
            script_ptr->next = script;

        script_ptr = script;

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

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

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

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

    fclose(fp);

    
return root_script;
}

//----------------------------------------------------------------------------------------
// Execute all scripts.
//----------------------------------------------------------------------------------------
BOOL run_script()
{
    
// clear flags
    for(short i = 0; i < 256; i++)
        g_flags[i] = FALSE;

    SCRIPT_PTR script_ptr;

    
// start at beginning of script
    if((script_ptr = g_root_script) == NULL)
        
return FALSE;

    reset_listbox(GetDlgItem(g_hwnd, IDC_SCRIPT));

    
// loop until no more scripts
    while(script_ptr)
    {
        
// Call script function and break on NULL return value,
        // any other return type is the pointer to the next function.
        script_ptr = g_script_list[script_ptr->action_index](script_ptr);
    }

    
return TRUE;
}

//----------------------------------------------------------------------------------------
// Handle 'if then' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_if_flag_then(SCRIPT_PTR script)
{
    BOOL skipping;

    
long flag_index = script->entries[0].long_value % 256;

    
// see if a flag matches second entry
    if(g_flags[flag_index] == script->entries[1].bool_value)
        skipping = FALSE;
    
else
        skipping = TRUE;

    script = script->next;

    
while(script)
    {
        
// if 'else', flip skip mode
        if(script->action_index == 1)
            skipping = !skipping;

        
// break on 'end if'
        if(script->action_index == 2)
            
return script->next;

        
// Process script function in conditional block, 
        // making sure to skip actions when condition not met.
        if(skipping)
            script = script->next;
        
else
        {
            
if((script = g_script_list[script->action_index](script)) == NULL)
                
break;
        }
    }

    
return NULL;
}

//----------------------------------------------------------------------------------------
// Handle 'else' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_else(SCRIPT_PTR script)
{
    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle 'endif' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_endif(SCRIPT_PTR script)
{
    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle 'set flag' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_set_flag(SCRIPT_PTR script)
{
    g_flags[script->entries[0].long_value % 256] = script->entries[1].bool_value;

    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle 'print' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_print(SCRIPT_PTR script)
{
    HWND listbox = GetDlgItem(g_hwnd, IDC_SCRIPT);

    add_string_to_listbox(listbox, "Print string:");
    add_string_to_listbox(listbox, script->entries[0].text);
    add_string_to_listbox(listbox, "");

    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle 'Move character' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_move(SCRIPT_PTR script)
{
    
char text[256];

    HWND listbox = GetDlgItem(g_hwnd, IDC_SCRIPT);

    add_string_to_listbox(listbox, "Moving character to:");

    sprintf(text, "%lf, %lf, %lf", 
            script->entries[0].float_value, 
            script->entries[1].float_value, 
            script->entries[2].float_value);

    add_string_to_listbox(listbox, text);
    add_string_to_listbox(listbox, "");

    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle 'gain loss' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_gain_loss(SCRIPT_PTR script)
{
    
char options[7][64] = 
    {
        { "Main character" }, 
        { "Caster"         },
        { "Target"         },
        { "Gains"          },
        { "Looses"         },
        { "Hit"            },
        { "Magic"          }
    };

    
char text[1024];

    sprintf(text, "%s %s %lu %s points",
            options[script->entries[0].selection],
            options[script->entries[1].selection + 3],
            script->entries[2].long_value,
            options[script->entries[3].selection + 5]);

    HWND listbox = GetDlgItem(g_hwnd, IDC_SCRIPT);

    add_string_to_listbox(listbox, text);
    add_string_to_listbox(listbox, "");

    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle 'engaging in battle' script statement.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_battle(SCRIPT_PTR script)
{
    
char text[256];

    sprintf(text, "Engaging in battle #%lu", script->entries[0].long_value);

    HWND listbox = GetDlgItem(g_hwnd, IDC_SCRIPT);

    add_string_to_listbox(listbox, text);
    add_string_to_listbox(listbox, "");

    
return script->next;
}

//----------------------------------------------------------------------------------------
// Handle for script end.
//----------------------------------------------------------------------------------------
SCRIPT_PTR script_end(SCRIPT_PTR script)
{  
    HWND listbox = GetDlgItem(g_hwnd, IDC_SCRIPT);

    add_string_to_listbox(listbox, "End of Script");
    add_string_to_listbox(listbox, "");

    
return NULL;
}

//-----------------------------------------------------------------------------------
// Remove all items from list box.
//-----------------------------------------------------------------------------------
void reset_listbox(HWND listbox)
{
    
// An application sends an LB_RESETCONTENT message to remove all items from a list box. 
    //
    // To send this message, call the SendMessage function as follows. 
    //
    // lResult = SendMessage(       // returns LRESULT in lResult     
    //      (HWND) hWndControl,     // handle to destination control     
    //      (UINT) LB_RESETCONTENT, // message ID     
    //      (WPARAM) wParam,        // = (WPARAM) () wParam;    
    //      (LPARAM) lParam         // = (LPARAM) () lParam; );   
    //
    // wParam:
    //      Not used; must be zero. 
    //
    // lParam:
    //      Not used; must be zero. 
    //
    // This message does not return a value. 

    SendMessage(listbox, LB_RESETCONTENT, 0, 0);
}

//-----------------------------------------------------------------------------------
// Add string to listbox.
//-----------------------------------------------------------------------------------
LRESULT add_string_to_listbox(HWND listbox, const charstring)
{
    
// An application sends an LB_ADDSTRING message to add a string to a list box. If the list box does not have 
    // the LBS_SORT style, the string is added to the end of the list. Otherwise, the string is inserted into 
    // the list and the list is sorted. 
    //
    // To send this message, call the SendMessage function as follows. 
    //
    // lResult = SendMessage(       // returns LRESULT in lResult     
    //      (HWND) hWndControl,     // handle to destination control     
    //      (UINT) LB_ADDSTRING,    // message ID     
    //      (WPARAM) wParam,        // = (WPARAM) () wParam;    
    //      (LPARAM) lParam         // = (LPARAM) () lParam; 
    //  );       
    //
    // wParam:
    //      This parameter is not used. 
    //
    // lParam:
    //      Pointer to the null-terminated string that is to be added. 
    //
    //      If you create the list box with an owner-drawn style but without the LBS_HASSTRINGS style, 
    //      this parameter is stored as item data instead of the string to which it would otherwise point. 
    //      You can send the LB_GETITEMDATA and LB_SETITEMDATA messages to retrieve or modify the item data.
    //
    // Return Value:    
    //      The return value is the zero-based index of the string in the list box. If an error occurs, 
    //      the return value is LB_ERR. If there is insufficient space to store the new string, the return value 
    //      is LB_ERRSPACE. 

    
return SendMessage(listbox, LB_ADDSTRING, 0, (LPARAM)string);
}

截图:

 

下载源码和工程

 

posted on 2007-11-04 01:29 lovedday 阅读(626) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论