游戏脚本的实现(6)

游戏脚本的实现(6)

 

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

 

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


 

Mad Lib Scripts的执行

窗口设计:

游戏脚本的实现(6)_第1张图片

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   char string );

//////////////////////////////////// / 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   char string )
{
    
// 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 );
}

截图:

游戏脚本的实现(6)_第2张图片

游戏脚本的实现(6)_第3张图片

 

下载源码和工程

 

你可能感兴趣的:(游戏脚本的实现(6))