本篇是游戏脚本的实现(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 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);
}
截图:
下载源码和工程