本篇是游戏脚本的实现(3)的续篇。
 
首先来看看WinMain函数以及主窗口处理过程的实现:
//-----------------------------------------------------------------------------------
// Routine entry.
//-----------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
    const char* class_name = "mls edit class";
    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_MAIN), 0, NULL);
    ShowWindow(g_hwnd, cmd_show);
    UpdateWindow(g_hwnd);
    g_script_wnd      = GetDlgItem(g_hwnd, IDC_SCRIPT);
    g_script_addr_wnd = GetDlgItem(g_hwnd, IDC_SCRIPT_ADDR);
    g_action_wnd      = GetDlgItem(g_hwnd, IDC_ACTION);
    // force a load of action in default.mla
    load_actions_from_file("..\\Data\\Default.mla");
    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:
        wm_command_proc(word_param);        
        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.nMaxFileTitle = MAX_PATH;
        g_ofn.Flags         = OFN_HIDEREADONLY | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
        // clear script filenmae
        g_script_file[0] = 0;
        g_action_file[0] = 0;
        // clear all list boxes
        reset_listbox(g_script_wnd);
        reset_listbox(g_script_addr_wnd);
        reset_listbox(g_action_wnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg_id, word_param, long_param);
    }
    return 0;
}
 
使用load_actions_from_file函数来加载行为脚本:
//-----------------------------------------------------------------------------------
// Load action template file.
//-----------------------------------------------------------------------------------
BOOL load_actions_from_file(const char* filename)
{
    if(filename == NULL)    // ask for filename if none passed
    {
        // setup the open dialog information
        g_ofn.hwndOwner   = g_hwnd;
        g_ofn.lpstrFile   = g_action_file;
        g_ofn.lpstrTitle  = "Load Actions File";
        g_ofn.lpstrFilter = "MLS Action Files (*.mla)\0*.mla\0All Files (*.*)\0*.*\0\0";
        g_ofn.lpstrDefExt = "mla";
        // get action template file name
        //
        // Creates an Open dialog box that lets the user specify the drive, directory, 
        // and the name of a file to open. 
        if(! GetOpenFileName(&g_ofn))
            return FALSE;
        // ask if sure to make new script
        if(! new_script())
            return FALSE;
    }
    else
        strcpy(g_action_file, filename);
    // attempt to load actions
    if(! g_action_template.load_actions(g_action_file))
    {
        MessageBox(g_hwnd, g_action_file, "Unable to open file", MB_OK);
        return FALSE;
    }
    // clear the list box
    reset_listbox(g_action_wnd);
    // get a pointer to the parent action
    ACTION_PTR act_ptr = g_action_template.get_root_action();
    long num_actions = g_action_template.get_num_actions();
    // loop through all actions
    for(long i = 0; i < num_actions; i++)
    {
        char text[2048];
        // get expanded action text
        g_action_template.expand_default_action_text(text, act_ptr);
        // add action text to action list
        add_string_to_listbox(g_action_wnd, text);
        // go to next action
        act_ptr = act_ptr->next;
    }
    return TRUE;
}
所有的脚本编辑命令都在wm_command_proc函数中处理:
 
//-------------------------------------------------------------------------------------------------
// The WM_COMMAND message is sent when the user selects a command item from a menu, when a control 
// sends a notification message to its parent window, or when an accelerator keystroke is translated. 
//
// WM_COMMAND
//    WPARAM wParam
//    LPARAM lParam;
//
// wParam:
//      The high-order word specifies the notification code if the message is from a control. 
//      If the message is from an accelerator, this value is 1. If the message is from a menu, 
//      this value is zero. 
//
//      The low-order word specifies the identifier of the menu item, control, or accelerator. 
//
// lParam:
//      Handle to the control sending the message if the message is from a control. 
//      Otherwise, this parameter is NULL. 
//
// If an application processes this message, it should return zero. 
//-------------------------------------------------------------------------------------------------
void wm_command_proc(WPARAM word_param)
{
    switch(LOWORD(word_param))
    {
    case IDC_NEW:           // create a new script
        new_script();
        break;
    case IDC_LOAD:          // load a script file
        load_script();
        break;
    case IDC_SAVE:          // save a script file
        save_script();
        break;
    case IDC_LOADACTIONS:   // load an action file
        load_actions_from_file(NULL);
        break;
    case IDC_SCRIPT:        // edit a script
        if(HIWORD(word_param) != LBN_DBLCLK)
            break;
    case IDC_EDIT:
        edit_script();
        break;
    case IDC_ACTIONS:       // add an actiotion
        if(HIWORD(word_param) != LBN_DBLCLK)
            break;
    case IDC_ADD:
        add_script();
        break;
    case IDC_INSERT:        // insert a script
        insert_script();
        break;
    case IDC_DELETE:        // delete a script
        delete_script();
        break;
    case IDC_UP:            // move up script
        move_up_script();
        break;
    
    case IDC_DOWN:          // move down script
        move_down_script();
        break;
    }
}
new_script函数用来提示用户是否真要创建一个脚本,如果是将清除脚本列表框中的脚本:

//-----------------------------------------------------------------------------------
// New a script.
//-----------------------------------------------------------------------------------
BOOL new_script()
{
    const char* msg_info = "Are you sure? (Discards any unsaved script information)";
    if(MessageBox(g_hwnd, msg_info, "New Script", MB_YESNO) == IDYES)
    {
        delete g_root_script;
        g_root_script = NULL;
        g_num_script = 0;
        // clear out listbox
        reset_listbox(g_script_wnd);
        reset_listbox(g_script_addr_wnd);
        return TRUE;
    }
    return TRUE;
}
load_script用来加载脚本并将其添加到脚本列表框中:
 
//-----------------------------------------------------------------------------------
// Load script from file.
//-----------------------------------------------------------------------------------
BOOL load_script()
{
    // setup the open dialog infomation
    g_ofn.hwndOwner   = g_hwnd;
    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";
    // ask for filename
    if(! GetOpenFileName(&g_ofn))
        return FALSE;
    FILE* fp;
    // open the file for input
    if((fp = fopen(g_script_file, "rb")) == NULL)
    {
        MessageBox(g_hwnd, g_script_file, "Unable to open file.", MB_OK);
        return FALSE;
    }
    // delete the current script
    delete g_root_script;
    char text[2048];
    SCRIPT_PTR script_ptr = NULL;
    // get number of script
    fread(&g_num_script, 1, sizeof(long), fp);
    // loop through each script
    for(long i = 0; i < g_num_script; i++)
    {
        // allocate a script structure and link in
        SCRIPT_PTR script = new SCRIPT;
        script->next = NULL;
        if(script_ptr == NULL)
            g_root_script = script;
        else
            script_ptr->next = script;
        script_ptr = script;
        // get index of actions and number of entries
        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)
        {
            // allocate entry array
            script->entries = new ENTRY[script->num_entries];
            // load in each entry
            for(long j = 0; j < script->num_entries; j++)
            {
                // get entry type and data
                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)
                {
                    long length = script->entries[j].length;
                    if(length)
                    {
                        // allocate a buffer and get string
                        script->entries[j].text = new char[length];
                        fread(script->entries[j].text, 1, length, fp);    
                    }
                }                
            }
        }
    }
    fclose(fp);
    // clear the script and script address boxes
    reset_listbox(g_script_wnd);
    reset_listbox(g_script_addr_wnd);
    // add script to listbox
    script_ptr = g_root_script;
    while(script_ptr)
    {
        // add script text to listbox
        g_action_template.expand_action_text(text, script_ptr);
        add_string_to_listbox(g_script_wnd, text);
        // add script pointer to listbox
        sprintf(text, "%lu", script_ptr);
        add_string_to_listbox(g_script_addr_wnd, text);
        script_ptr = script_ptr->next;
    }
    return TRUE;
}
save_script将脚本存储到文件中:
 
//-----------------------------------------------------------------------------------
// Save script to file.
//-----------------------------------------------------------------------------------
BOOL save_script()
{
    // make sure there is some script actions
    if(g_num_script == 0)
    { 
       MessageBox(g_hwnd, "No script actions exist!", "Error", MB_OK);
       return FALSE;
    }
    // setup the open dialog information
    g_ofn.hwndOwner   = g_hwnd;
    g_ofn.lpstrFile   = g_script_file;
    g_ofn.lpstrTitle  = "Save Script File";
    g_ofn.lpstrFilter = "MLS Script Files (*.mls)\0*.mls\0All Files (*.*)\0*.*\0\0";
    g_ofn.lpstrDefExt = "mls";
    // ask for filename
    if(! GetSaveFileName(&g_ofn))
        return FALSE;
    FILE* fp;
    // open the file for output
    if((fp = fopen(g_script_file, "wb")) == NULL)
    {
        MessageBox(g_hwnd, g_script_file, "Unable to save file.", MB_OK);
        return FALSE;
    }
    // output number of scripts
    fwrite(&g_num_script, 1, sizeof(long), fp);
    // loop through each script
    for(long i = 0; i < g_num_script; i++)
    {
        char text[256];
        // get a pointer to the script structure (do not depend on list)
        get_listbox_text(g_script_addr_wnd, i, text);
        SCRIPT_PTR script_ptr = (SCRIPT_PTR) atol(text);
        // output index of action and number of entries
        fwrite(&script_ptr->action_index, 1, sizeof(long), fp);
        fwrite(&script_ptr->num_entries, 1, sizeof(long), fp);
        // output entry data (if any)
        for(long j = 0; j < script_ptr->num_entries; j++)
        {
            // write entry type and data
            fwrite(&script_ptr->entries[j].type, 1, sizeof(long), fp);
            fwrite(&script_ptr->entries[j].io_value, 1, sizeof(long), fp);
            // write text (if any)
            if(script_ptr->entries[j].type == ENTRY_TEXT && script_ptr->entries[j].text)
                fwrite(script_ptr->entries[j].text, 1, script_ptr->entries[j].length, fp);
        }
    }
    fclose(fp);
    return TRUE;
}
edit_script用于编辑当前选中的脚本行:
 
//-----------------------------------------------------------------------------------
// Edit script.
//-----------------------------------------------------------------------------------
void edit_script()
{
    int sel;
    // see if a script was selected
    if((sel = (int)get_listbox_selected(g_script_wnd)) == LB_ERR)
        return;
    char text[2048];
    // get pointer to script entry
    get_listbox_text(g_script_addr_wnd, sel, text);
    SCRIPT_PTR script = (SCRIPT_PTR) atol(text);
    // setup the script to edit
    g_modify_script = script;
    // call the modify script dialog
    g_show_cancel = FALSE;
    DialogBox(NULL, MAKEINTRESOURCE(IDD_MODIFY), g_hwnd, modify_dialog_proc);
    // update script listbox
    g_action_template.expand_action_text(text, script);
    delete_listbox_string(g_script_wnd, sel);
    insert_string_to_listbox(g_script_wnd, sel, text);
}