/* **********************************************************************************************************************
   Included Header Files
   **********************************************************************************************************************/

#include "VDNPCEditor.h"



/* **********************************************************************************************************************
   Constructor
   **********************************************************************************************************************/

VDNPCEditor::VDNPCEditor() {
    window = NULL;
    npcListBox = NULL;
    (void) strcpy_s(npcEditorPreviousNPCSelection, "");
    nameEditBox = NULL;
    loadSpriteButton = NULL;
    npcSpriteSheet = NULL;
    (void) strcpy_s(npcSpriteSheetLocation, "");
    npcSprite = NULL;
    (void) strcpy_s(npcSpriteImageLocation, "");
    (void) SecureZeroMemory(&npcSpriteDimensions, sizeof(npcSpriteDimensions));
    shiftNPCUp = NULL;
    shiftNPCDown = NULL;
    previousAnimation = NULL;
    nextAnimation = NULL;
    removeNPCButton = NULL;
    saveNPCButton = NULL;
    dialogueListBox = NULL;
    (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
    textEditBox = NULL;
    textIDEditBox = NULL;
    removeDialogueButton = NULL;
    saveDialogueButton = NULL;
    optionListBox = NULL;
    (void) strcpy_s(npcEditorPreviousOptionSelection, "");
    textEditBox2 = NULL;
    textIDEditBox2 = NULL;
    removeOptionButton = NULL;
    saveOptionButton = NULL;
    saveChangesButton = NULL;
};



/* **********************************************************************************************************************
   Destructor
   **********************************************************************************************************************/

VDNPCEditor::~VDNPCEditor() {
    FreeMemory();
};



void VDNPCEditor::FreeMemory() {
    if (npcSprite != NULL) {
        delete npcSprite;
        npcSprite = NULL;
    };
    if (npcSpriteSheet != NULL) {
        delete npcSpriteSheet;
        npcSpriteSheet = NULL;
    };
};



/* **********************************************************************************************************************
   CreateNPCEditor
   ---------------
   Creates a new NPC Editor window.
   #param  instance      The instance of the application.
   #param  parentWindow  Handle to the main window (to use for the starting x, y position)
   #param  className     The name of the window class.
   #return  Handle to the window.
   **********************************************************************************************************************/

HWND VDNPCEditor::CreateNPCEditor(HINSTANCE instance, HWND parentWindow, LPCSTR className) {
    RECT windowDimensions;
    (void) GetWindowRect(parentWindow, &windowDimensions);
    window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW,      // extended window styles
                            className,                             // class name
                            "NPC Editor",                          // window name
                            WS_CAPTION | WS_SYSMENU | WS_VISIBLE,  // window styles
                            windowDimensions.left + 100,           // x coordinate (0 = left)
                            windowDimensions.top + 100,            // y coordinate (0 = top)
                            440,                                   // width
                            530,                                   // height
                            NULL,                                  // parent/owner window
                            NULL,                                  // menu
                            instance,                              // instance
                            NULL);                                 // used for CREATESTRUCT structure
    if (window == NULL) {
        FreeMemory();
        VDError("Failed to create the window.", __FILE__, __LINE__);
    };

    // NPC's
    HWND npcListStatic = CreateWindow("Static", "NPC's:", WS_CHILD | WS_VISIBLE, 10, 10, 40, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_NPC_LIST, instance, NULL);
    if (npcListStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    npcListBox = CreateWindow("ListBox", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL, 10, 30, 100, 100, window, (HMENU) VD_NPC_EDITOR_NPC_LIST, instance, NULL);
    if (npcListBox == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    (void) strcpy_s(npcEditorPreviousNPCSelection, "");
    HWND npcNameStatic = CreateWindow("Static", "Name:", WS_CHILD | WS_VISIBLE, 120, 30, 50, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_NAME, instance, NULL);
    if (npcNameStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    nameEditBox = CreateWindow("Edit", "", WS_CHILD | WS_BORDER | WS_VISIBLE, 180, 30, 140, 18, window, (HMENU) VD_NPC_EDITOR_NAME, instance, NULL);
    if (nameEditBox == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    loadSpriteButton = CreateWindow("Button", "Load Sprite", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 120, 75, 90, 20, window, (HMENU) VD_NPC_EDITOR_LOAD_SPRITE, instance, NULL);
    if (loadSpriteButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    HWND spriteBorderStatic = CreateWindow("Static", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 225, 63, 48, 48, window, (HMENU) VD_NPC_EDITOR_STATIC_SPRITE_BORDER, instance, NULL);
    if (spriteBorderStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    FreeMemory();
    (void) strcpy_s(npcSpriteSheetLocation, "");
    (void) strcpy_s(npcSpriteImageLocation, "");
    npcSpriteDimensions.left = 233;
    npcSpriteDimensions.top = 71;
    npcSpriteDimensions.right = 265;
    npcSpriteDimensions.bottom = 103;
    shiftNPCUp = CreateWindow("Button", "/\\", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 273, 63, 20, 20, window, (HMENU) VD_NPC_EDITOR_SHIFT_NPC_UP, instance, NULL);
    shiftNPCDown = CreateWindow("Button", "\\/", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 273, 91, 20, 20, window, (HMENU) VD_NPC_EDITOR_SHIFT_NPC_DOWN, instance, NULL);
    previousAnimation = CreateWindow("Button", "<", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 225, 111, 20, 20, window, (HMENU) VD_NPC_EDITOR_PREVIOUS_ANIMATION, instance, NULL);
    nextAnimation = CreateWindow("Button", ">", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 253, 111, 20, 20, window, (HMENU) VD_NPC_EDITOR_NEXT_ANIMATION, instance, NULL);
    removeNPCButton = CreateWindow("Button", "Remove NPC", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 10, 132, 100, 20, window, (HMENU) VD_NPC_EDITOR_LOAD_SPRITE, instance, NULL);
    if (removeNPCButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    saveNPCButton = CreateWindow("Button", "Save NPC", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 120, 107, 80, 20, window, (HMENU) VD_NPC_EDITOR_LOAD_SPRITE, instance, NULL);
    if (saveNPCButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };

    // NPC dialogue
    HWND dialogueStatic = CreateWindow("Static", "Dialogues", WS_CHILD | WS_VISIBLE, 10, 160, 70, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_DIALOGUE, instance, NULL);
    if (dialogueStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    dialogueListBox = CreateWindow("ListBox", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL, 10, 180, 200, 110, window, (HMENU) VD_NPC_EDITOR_DIALOGUE, instance, NULL);
    if (dialogueListBox == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
    HWND textStatic = CreateWindow("Static", "Text:", WS_CHILD | WS_VISIBLE, 220, 160, 50, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_TEXT, instance, NULL);
    if (textStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    textEditBox = CreateWindow("Edit", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_MULTILINE, 220, 180, 200, 60, window, (HMENU) VD_NPC_EDITOR_TEXT, instance, NULL);
    if (textEditBox == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    HWND textIDStatic = CreateWindow("Static", "Text ID:", WS_CHILD | WS_VISIBLE, 220, 250, 50, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_TEXT_ID, instance, NULL);
    if (textIDStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    textIDEditBox = CreateWindow("Edit", "", WS_CHILD | WS_BORDER | WS_VISIBLE, 275, 250, 30, 18, window, (HMENU) VD_NPC_EDITOR_TEXT_ID, instance, NULL);
    if (textIDEditBox == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    removeDialogueButton = CreateWindow("Button", "Remove Dialogue", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 10, 283, 130, 20, window, (HMENU) VD_NPC_EDITOR_REMOVE_DIALOGUE, instance, NULL);
    if (removeDialogueButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    saveDialogueButton = CreateWindow("Button", "Save Text", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 320, 250, 80, 20, window, (HMENU) VD_NPC_EDITOR_SAVE_DIALOGUE, instance, NULL);
    if (saveDialogueButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };

    // options
    HWND optionsStatic = CreateWindow("Static", "User Response Options", WS_CHILD | WS_VISIBLE, 10, 310, 200, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_OPTIONS, instance, NULL);
    if (optionsStatic == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    optionListBox = CreateWindow("ListBox", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL, 10, 330, 200, 110, window, (HMENU) VD_NPC_EDITOR_OPTIONS, instance, NULL);
    if (optionListBox == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    (void) strcpy_s(npcEditorPreviousOptionSelection, "");
    HWND textStatic2 = CreateWindow("Static", "Text:", WS_CHILD | WS_VISIBLE, 220, 310, 70, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_TEXT2, instance, NULL);
    if (textStatic2 == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    textEditBox2 = CreateWindow("Edit", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_MULTILINE, 220, 330, 200, 60, window, (HMENU) VD_NPC_EDITOR_TEXT2, instance, NULL);
    if (textEditBox2 == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    HWND textIDStatic2 = CreateWindow("Static", "Next ID:", WS_CHILD | WS_VISIBLE, 220, 400, 55, 18, window, (HMENU) VD_NPC_EDITOR_STATIC_TEXT_ID2, instance, NULL);
    if (textIDStatic2 == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    textIDEditBox2 = CreateWindow("Edit", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 278, 400, 30, 18, window, (HMENU) VD_NPC_EDITOR_TEXT_ID2, instance, NULL);
    if (textIDEditBox2 == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    removeOptionButton = CreateWindow("Button", "Remove Response", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 10, 432, 140, 20, window, (HMENU) VD_NPC_EDITOR_REMOVE_OPTION, instance, NULL);
    if (removeOptionButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };
    saveOptionButton = CreateWindow("Button", "Save Text", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 320, 400, 80, 20, window, (HMENU) VD_NPC_EDITOR_SAVE_OPTION, instance, NULL);
    if (saveOptionButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };

    // save NPC's
    saveChangesButton = CreateWindow("Button", "SAVE CHANGES", WS_CHILD | BS_PUSHBUTTON | WS_BORDER | WS_VISIBLE, 115, 460, 200, 40, window, (HMENU) VD_NPC_EDITOR_SAVE_CHANGES, instance, NULL);
    if (saveChangesButton == NULL) {
        FreeMemory();
        VDError("Failed to create an interface resource.", __FILE__, __LINE__);
    };

    return window;
};



/* **********************************************************************************************************************
   ProcessNPCEditor
   ----------------
   Processes messages sent by and received from the NPC Editor.
   **********************************************************************************************************************/

void VDNPCEditor::ProcessNPCEditor(LPDIRECT3DDEVICE9 direct3DDevice, vector<VDNPC>* npcs) {
    // Save Changes button
    //////////////////////

    if (SendMessage(saveChangesButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        // save the NPCs
        WriteNPCs(npcs);

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // Load Sprite button
    /////////////////////

    if (SendMessage(loadSpriteButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        char text[FILE_NAME_MAX_SIZE];

        // open a sprite image
        VDOpenFile(window, "Sprites (*.png,*.bmp)\0*.png;*.bmp\0All Files\0*.*\0", text);

        // make sure the user selected a file
        if (strcmp(text, "") != 0) {
            // set the sprite file location
            (void) strcpy_s(npcSpriteSheetLocation, text);

            // add the sprite to the editor so that it may be drawn
            RECT drawingArea;
            (void) SetRect(&drawingArea, 0, 0, 32, 32);
            FreeMemory();
            npcSpriteSheet = new VDSprite(direct3DDevice, text, drawingArea, D3DXVECTOR3((float) npcSpriteDimensions.left, (float) npcSpriteDimensions.top, 0.0f), 0xFF000000, FALSE, FALSE);
        };

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // Up button
    ////////////

    if (SendMessage(shiftNPCUp, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        if (npcSpriteSheet != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSpriteSheet->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            if (drawingArea.top == 0) {
                LONG bottom = npcSpriteSheet->GetSpriteHeight();
                npcSpriteSheet->SetSourceRectangle(drawingArea.left, bottom - 32, drawingArea.right, bottom);
            } else {
                npcSpriteSheet->SetSourceRectangle(drawingArea.left, drawingArea.top - 32, drawingArea.right, drawingArea.bottom - 32);
            };
        };
        if (npcSprite != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSprite->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            if (drawingArea.top == 0) {
                LONG bottom = npcSprite->GetSpriteHeight();
                npcSprite->SetSourceRectangle(drawingArea.left, bottom - 32, drawingArea.right, bottom);
            } else {
                npcSprite->SetSourceRectangle(drawingArea.left, drawingArea.top - 32, drawingArea.right, drawingArea.bottom - 32);
            };
        };

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // Down button
    //////////////

    if (SendMessage(shiftNPCDown, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        if (npcSpriteSheet != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSpriteSheet->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            LONG bottom = npcSpriteSheet->GetSpriteHeight();
            if (drawingArea.bottom == bottom) {
                npcSpriteSheet->SetSourceRectangle(drawingArea.left, 0, drawingArea.right, 32);
            } else {
                npcSpriteSheet->SetSourceRectangle(drawingArea.left, drawingArea.top + 32, drawingArea.right, drawingArea.bottom + 32);
            };
        };
        if (npcSprite != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSprite->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            LONG bottom = npcSprite->GetSpriteHeight();
            if (drawingArea.bottom == bottom) {
                npcSprite->SetSourceRectangle(drawingArea.left, 0, drawingArea.right, 32);
            } else {
                npcSprite->SetSourceRectangle(drawingArea.left, drawingArea.top + 32, drawingArea.right, drawingArea.bottom + 32);
            };
        };

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // Left button
    //////////////

    if (SendMessage(previousAnimation, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        if (npcSpriteSheet != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSpriteSheet->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            if (drawingArea.left == 0) {
                LONG right = npcSpriteSheet->GetSpriteWidth();
                npcSpriteSheet->SetSourceRectangle(right - 32, drawingArea.top, right, drawingArea.bottom);
            } else {
                npcSpriteSheet->SetSourceRectangle(drawingArea.left - 32, drawingArea.top, drawingArea.right - 32, drawingArea.bottom);
            };
        };
        if (npcSprite != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSprite->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            if (drawingArea.left == 0) {
                LONG right = npcSprite->GetSpriteWidth();
                npcSprite->SetSourceRectangle(right - 32, drawingArea.top, right, drawingArea.bottom);
            } else {
                npcSprite->SetSourceRectangle(drawingArea.left - 32, drawingArea.top, drawingArea.right - 32, drawingArea.bottom);
            };
        };

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // Right button
    ///////////////

    if (SendMessage(nextAnimation, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        if (npcSpriteSheet != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSpriteSheet->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            LONG right = npcSpriteSheet->GetSpriteWidth();
            if (drawingArea.right == right) {
                npcSpriteSheet->SetSourceRectangle(0, drawingArea.top, 32, drawingArea.bottom);
            } else {
                npcSpriteSheet->SetSourceRectangle(drawingArea.left + 32, drawingArea.top, drawingArea.right + 32, drawingArea.bottom);
            };
        };
        if (npcSprite != NULL) {
            // get the sprite's source rectangle
            RECT drawingArea;
            (void) npcSprite->GetSourceRectangle(&drawingArea);

            // see if we're wrapping around
            LONG right = npcSprite->GetSpriteWidth();
            if (drawingArea.right == right) {
                npcSprite->SetSourceRectangle(0, drawingArea.top, 32, drawingArea.bottom);
            } else {
                npcSprite->SetSourceRectangle(drawingArea.left + 32, drawingArea.top, drawingArea.right + 32, drawingArea.bottom);
            };
        };

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // Save NPC button
    //////////////////

    if (SendMessage(saveNPCButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        char npcText[FILE_NAME_MAX_SIZE];

        // check for a name, ID, and sprite
        (void) GetWindowText(nameEditBox, npcText, sizeof(npcText));
        if (strcmp(npcText, "") != 0 && npcSpriteSheet != NULL) {
            char newImageFile[FILE_NAME_MAX_SIZE], extension[10];

            // remove the path name, save the extension
            int n = 0, m = 0;
            while (npcSpriteSheetLocation[n] != '.') {
                n++;
            };
            while (npcSpriteSheetLocation[n] != '\0') {
                extension[m] = npcSpriteSheetLocation[n];
                n++;
                m++;
            };
            extension[m] = '\0';

            // use the NPC name for the new texture
            (void) strcpy_s(newImageFile, "Images/");
            (void) strcat_s(newImageFile, npcText);
            (void) strcat_s(newImageFile, extension);

            // add the NPC
            if (AddNPC(npcs, npcText, newImageFile) == TRUE) {
                // update the list box
                (void) SendMessage(npcListBox, LB_ADDSTRING, 0, (LPARAM) npcText);
            };

            // create the texture
            CreateNPCTexture(direct3DDevice, newImageFile);

            // reset the window
            (void) SendMessage(npcListBox, LB_SETCURSEL, (WPARAM) - 1, 0);
            (void) SetWindowText(nameEditBox, "");
            FreeMemory();
            (void) SendMessage(dialogueListBox, LB_RESETCONTENT, 0, 0);
            (void) SetWindowText(textEditBox, "");
            (void) SetWindowText(textIDEditBox, "");
            (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
            (void) SetWindowText(textEditBox2, "");
            (void) SetWindowText(textIDEditBox2, "");
            (void) strcpy_s(npcEditorPreviousNPCSelection, "");
            (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
            (void) strcpy_s(npcEditorPreviousOptionSelection, "");
        };

        // make sure we're not still focused on the button
        (void) SetFocus(npcListBox);
    };

    // Remove NPC button
    ////////////////////

    if (SendMessage(removeNPCButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
        char npcText[FILE_NAME_MAX_SIZE];

        // get the index of the selected NPC text
        LRESULT index = SendMessage(npcListBox, LB_GETCURSEL, 0, 0);
        if (index != LB_ERR && index >= 0) {
            // get the name of the selected NPC
            (void) SendMessage(npcListBox, LB_GETTEXT, (WPARAM) index, (LPARAM) &npcText);

            // remove the NPC from the world
            RemoveNPC(npcs, npcText);

            // TODO: remove the created texture

            // update the list box
            (void) SendMessage(npcListBox, LB_DELETESTRING, (WPARAM) index, 0);

            // reset the window
            (void) SendMessage(npcListBox, LB_SETCURSEL, (WPARAM) - 1, 0);
            (void) SetWindowText(nameEditBox, "");
            FreeMemory();
            (void) SendMessage(dialogueListBox, LB_RESETCONTENT, 0, 0);
            (void) SetWindowText(textEditBox, "");
            (void) SetWindowText(textIDEditBox, "");
            (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
            (void) SetWindowText(textEditBox2, "");
            (void) SetWindowText(textIDEditBox2, "");
            (void) strcpy_s(npcEditorPreviousNPCSelection, "");
            (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
            (void) strcpy_s(npcEditorPreviousOptionSelection, "");
        };

        // make sure we're not still focused on the button
        (void) SetFocus(window);
    };

    // user has an NPC selected
    ///////////////////////////

    int currentNPCIndex = (int) SendMessage(npcListBox, LB_GETCURSEL, 0, 0);
    if (currentNPCIndex != LB_ERR) {
        char currentNPCSelection[FILE_NAME_MAX_SIZE];

        // get the name of the current selected NPC
        (void) strcpy_s(currentNPCSelection, "");
        (void) SendMessage(npcListBox, LB_GETTEXT, (WPARAM) currentNPCIndex, (LPARAM) &currentNPCSelection);

        // user selects a different NPC
        ///////////////////////////////

        if (strcmp(npcEditorPreviousNPCSelection, currentNPCSelection) != 0) {
            RECT drawingArea;

            // copy the current selection to previous
            (void) strcpy_s(npcEditorPreviousNPCSelection, currentNPCSelection);

            // make sure the editor knows the file location
            (void) strcpy_s(npcSpriteImageLocation, GetNPC(currentNPCSelection, npcs).GetImageFileLocation());

            // update the window
            (void) SetWindowText(nameEditBox, currentNPCSelection);
            (void) SetRect(&drawingArea, 0, 0, 32, 32);
            FreeMemory();
            npcSprite = new VDSprite(direct3DDevice, npcSpriteImageLocation, drawingArea, D3DXVECTOR3((float) npcSpriteDimensions.left, (float) npcSpriteDimensions.top, 0.0f), 0xFF000000, FALSE, FALSE);

            // reset the window
            (void) SendMessage(dialogueListBox, LB_RESETCONTENT, 0, 0);
            (void) SetWindowText(textEditBox, "");
            (void) SetWindowText(textIDEditBox, "");
            (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
            (void) SetWindowText(textEditBox2, "");
            (void) SetWindowText(textIDEditBox2, "");
            (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
            (void) strcpy_s(npcEditorPreviousOptionSelection, "");

            // populate the dialogue list
            if (SendMessage(dialogueListBox, LB_GETCOUNT, 0, 0) == 0) {
                // get the current NPC
                VDNPC currentNPC = GetNPC(currentNPCSelection, npcs);

                // see how many dialogues the NPC has
                size_t numberOfDialogues = currentNPC.GetDialoguesSize();

                // add the dialogues to the list
                while (numberOfDialogues > 0) {
                    char dialogue[FILE_NAME_MAX_SIZE];

                    // set it up so it's "ID: Text"
                    (void) _itoa_s(currentNPC.GetDialogueID(numberOfDialogues - 1), dialogue, FILE_NAME_MAX_SIZE, 10);
                    (void) strcat_s(dialogue, ": ");
                    (void) strcat_s(dialogue, currentNPC.GetDialogueText(numberOfDialogues - 1));
                    (void) SendMessage(dialogueListBox, LB_ADDSTRING, 0, (LPARAM) dialogue);
                    numberOfDialogues--;
                };
            };
        };

        // Save Text (dialogue) button
        //////////////////////////////

        if (SendMessage(saveDialogueButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
            char text[FILE_NAME_MAX_SIZE], textID[FILE_NAME_MAX_SIZE];

            // check for text and ID
            (void) GetWindowText(textEditBox, text, sizeof(text));
            (void) GetWindowText(textIDEditBox, textID, sizeof(textID));
            if (strcmp(text, "") != 0 && strcmp(textID, "") != 0) {
                // add the dialogue
                AddNPCDialogue(npcs, currentNPCSelection, atoi(textID), text);

                // reset the window
                (void) SendMessage(dialogueListBox, LB_RESETCONTENT, 0, 0);
                (void) SetWindowText(textEditBox, "");
                (void) SetWindowText(textIDEditBox, "");
                (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
                (void) SetWindowText(textEditBox2, "");
                (void) SetWindowText(textIDEditBox2, "");
                (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
                (void) strcpy_s(npcEditorPreviousOptionSelection, "");

                // populate the dialogue list
                if (SendMessage(dialogueListBox, LB_GETCOUNT, 0, 0) == 0) {
                    // get the current NPC
                    VDNPC currentNPC = GetNPC(currentNPCSelection, npcs);

                    // see how many dialogues the NPC has
                    size_t numberOfDialogues = currentNPC.GetDialoguesSize();

                    // add the dialogues to the list
                    while (numberOfDialogues > 0) {
                        char dialogue[FILE_NAME_MAX_SIZE];

                        // set it up so it's "ID: Text"
                        (void) _itoa_s(currentNPC.GetDialogueID(numberOfDialogues - 1), dialogue, FILE_NAME_MAX_SIZE, 10);
                        (void) strcat_s(dialogue, ": ");
                        (void) strcat_s(dialogue, currentNPC.GetDialogueText(numberOfDialogues - 1));
                        (void) SendMessage(dialogueListBox, LB_ADDSTRING, 0, (LPARAM) dialogue);
                        numberOfDialogues--;
                    };
                };
            };

            // make sure we're not still focused on the button
            (void) SetFocus(window);
        };

        // Remove Dialogue button
        /////////////////////////

        if (SendMessage(removeDialogueButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
            char text[FILE_NAME_MAX_SIZE];

            // get the index of the selected Dialogue text
            LRESULT index = SendMessage(dialogueListBox, LB_GETCURSEL, 0, 0);
            if (index != LB_ERR && index >= 0) {
                char id[10];

                // get the name of the selected dialogue
                (void) SendMessage(dialogueListBox, LB_GETTEXT, (WPARAM) index, (LPARAM) &text);

                // get the dialogue ID, remove the dialogue from the NPC
                int n = 0;
                while (text[n] != ':') {
                    id[n] = text[n];
                    n++;
                };
                id[n] = '\0';
                RemoveNPCDialogue(npcs, currentNPCSelection, atoi(id));

                // update the list box
                (void) SendMessage(dialogueListBox, LB_DELETESTRING, (WPARAM) index, 0);
                (void) SendMessage(dialogueListBox, LB_SETCURSEL, (WPARAM) - 1, 0);

                // reset the window
                (void) SetWindowText(textEditBox, "");
                (void) SetWindowText(textIDEditBox, "");
                (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
                (void) SetWindowText(textEditBox2, "");
                (void) SetWindowText(textIDEditBox2, "");
                (void) strcpy_s(npcEditorPreviousDialogueSelection, "");
                (void) strcpy_s(npcEditorPreviousOptionSelection, "");
            };

            // make sure we're not still focused on the button
            (void) SetFocus(window);
        };

        // user has a dialogue selected
        ///////////////////////////////

        int currentDialogueIndex = (int) SendMessage(dialogueListBox, LB_GETCURSEL, 0, 0);
        if (currentDialogueIndex != LB_ERR) {
            char id[10], currentDialogueSelection[FILE_NAME_MAX_SIZE], dialogueName[FILE_NAME_MAX_SIZE];

            // get the string of the current selected dialogue
            (void) strcpy_s(currentDialogueSelection, "");
            (void) SendMessage(dialogueListBox, LB_GETTEXT, (WPARAM) currentDialogueIndex, (LPARAM) &currentDialogueSelection);

            // get the ID and name of the dialogue
            int n = 0, m = 0;
            while (currentDialogueSelection[n] != ':') {
                id[n] = currentDialogueSelection[n];
                n++;
            };
            id[n] = '\0';
            n++;
            while (currentDialogueSelection[n] != ' ') {
                n++;
            };
            n++;
            while (currentDialogueSelection[n] != '\0') {
                dialogueName[m] = currentDialogueSelection[n];
                n++;
                m++;
            };
            dialogueName[m] = '\0';
            int currentDialogueID = atoi(id);

            // user selects a different dialogue
            ////////////////////////////////////

            if (strcmp(npcEditorPreviousDialogueSelection, currentDialogueSelection) != 0) {
                // copy the current selection to previous
                (void) strcpy_s(npcEditorPreviousDialogueSelection, currentDialogueSelection);

                // update the window
                (void) SetWindowText(textEditBox, dialogueName);
                (void) SetWindowText(textIDEditBox, id);
                (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
                (void) SetWindowText(textEditBox2, "");
                (void) SetWindowText(textIDEditBox2, "");
                (void) strcpy_s(npcEditorPreviousOptionSelection, "");

                // populate the options list
                if (SendMessage(optionListBox, LB_GETCOUNT, 0, 0) == 0) {
                    // get the current NPC
                    VDNPC currentNPC = GetNPC(currentNPCSelection, npcs);

                    // see how many options the dialogue of the NPC has
                    size_t numberOfOptions = currentNPC.GetOptionsSize(currentDialogueID);

                    // add the options to the list
                    while (numberOfOptions > 0) {
                        char option[FILE_NAME_MAX_SIZE];

                        // set it up so it's "ID: Text"
                        (void) _itoa_s(currentNPC.GetOptionNextID(currentDialogueID, numberOfOptions - 1), option, FILE_NAME_MAX_SIZE, 10);
                        (void) strcat_s(option, "[next]: ");
                        (void) strcat_s(option, currentNPC.GetOption(currentDialogueID, numberOfOptions - 1));
                        (void) SendMessage(optionListBox, LB_ADDSTRING, 0, (LPARAM) option);
                        numberOfOptions--;
                    };
                };
            };

            // Save Text (option) button
            ////////////////////////////

            if (SendMessage(saveOptionButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
                char text[FILE_NAME_MAX_SIZE], nextID[FILE_NAME_MAX_SIZE];

                // check for text and ID
                (void) GetWindowText(textEditBox2, text, sizeof(text));
                (void) GetWindowText(textIDEditBox2, nextID, sizeof(nextID));
                if (strcmp(text, "") != 0 && strcmp(nextID, "") != 0) {
                    // add the option
                    (void) AddNPCDialogueOption(npcs, currentNPCSelection, currentDialogueID, text, atoi(nextID));

                    // reset the window
                    (void) SendMessage(optionListBox, LB_RESETCONTENT, 0, 0);
                    (void) SetWindowText(textEditBox2, "");
                    (void) SetWindowText(textIDEditBox2, "");
                    (void) strcpy_s(npcEditorPreviousOptionSelection, "");

                    // populate the options list
                    if (SendMessage(optionListBox, LB_GETCOUNT, 0, 0) == 0) {
                        // get the current NPC
                        VDNPC currentNPC = GetNPC(currentNPCSelection, npcs);

                        // see how many options the dialogue of the NPC has
                        size_t numberOfOptions = currentNPC.GetOptionsSize(currentDialogueID);

                        // add the options to the list
                        while (numberOfOptions > 0) {
                            char option[FILE_NAME_MAX_SIZE];

                            // set it up so it's "ID: Text"
                            (void) _itoa_s(currentNPC.GetOptionNextID(currentDialogueID, numberOfOptions - 1), option, FILE_NAME_MAX_SIZE, 10);
                            (void) strcat_s(option, "[next]: ");
                            (void) strcat_s(option, currentNPC.GetOption(currentDialogueID, numberOfOptions - 1));
                            (void) SendMessage(optionListBox, LB_ADDSTRING, 0, (LPARAM) option);
                            numberOfOptions--;
                        };
                    };
                };

                // make sure we're not still focused on the button
                (void) SetFocus(window);
            };

            // Remove Response button
            /////////////////////////

            if (SendMessage(removeOptionButton, BM_GETSTATE, 0, 0) == BST_FOCUS) {
                char text[FILE_NAME_MAX_SIZE];

                // get the index of the selected Option text
                LRESULT index = SendMessage(optionListBox, LB_GETCURSEL, 0, 0);
                if (index != LB_ERR && index >= 0) {
                    char optionName[FILE_NAME_MAX_SIZE];

                    // get the string of the selected option
                    (void) SendMessage(optionListBox, LB_GETTEXT, (WPARAM) index, (LPARAM) &text);

                    // get the option name, remove the option from the dialogue
                    int n = 0, m = 0;
                    while (text[n] != ' ') {
                        n++;
                    };
                    n++;
                    while (text[n] != '\0') {
                        optionName[m] = text[n];
                        n++;
                        m++;
                    };
                    optionName[m] = '\0';
                    RemoveNPCDialogueOption(npcs, currentNPCSelection, currentDialogueID, optionName);

                    // update the list box
                    (void) SendMessage(optionListBox, LB_DELETESTRING, (WPARAM) index, 0);
                    (void) SendMessage(optionListBox, LB_SETCURSEL, (WPARAM) - 1, 0);

                    // reset the window
                    (void) SetWindowText(textEditBox2, "");
                    (void) SetWindowText(textIDEditBox2, "");
                    (void) strcpy_s(npcEditorPreviousOptionSelection, "");
                };

                // make sure we're not still focused on the button
                (void) SetFocus(window);
            };

            // user has an option selected
            //////////////////////////////

            int currentOptionIndex = (int) SendMessage(optionListBox, LB_GETCURSEL, 0, 0);
            if (currentOptionIndex != LB_ERR) {
                char currentOptionSelection[FILE_NAME_MAX_SIZE];

                // get the name of the current selected option
                (void) strcpy_s(currentOptionSelection, "");
                (void) SendMessage(optionListBox, LB_GETTEXT, (WPARAM) currentOptionIndex, (LPARAM) &currentOptionSelection);

                // user selects a different option
                //////////////////////////////////

                if (strcmp(npcEditorPreviousOptionSelection, currentOptionSelection) != 0) {
                    char id[10], optionName[FILE_NAME_MAX_SIZE];

                    // copy the current selection to previous
                    (void) strcpy_s(npcEditorPreviousOptionSelection, currentOptionSelection);

                    // get the option ID and name
                    int n = 0, m = 0;
                    while (currentOptionSelection[n] != '[') {
                        id[n] = currentOptionSelection[n];
                        n++;
                    };
                    id[n] = '\0';
                    while (currentOptionSelection[n] != ' ') {
                        n++;
                    };
                    n++;
                    while (currentOptionSelection[n] != '\0') {
                        optionName[m] = currentOptionSelection[n];
                        n++;
                        m++;
                    };
                    optionName[m] = '\0';

                    // update the window
                    (void) SetWindowText(textEditBox2, optionName);
                    (void) SetWindowText(textIDEditBox2, id);
                };
            };
        };
    };
};






/* **********************************************************************************************************************
   AddNPC
   ------
   Adds an NPC to the list.
   #param  npcs               Pointer to the list of NPCs.
   #param  name               Name of the NPC.
   #param  imageFileLocation  The file location of the sprite.
   #return  TRUE if a new NPC was created, FALSE if the image was changed
   **********************************************************************************************************************/

BOOL VDNPCEditor::AddNPC(vector<VDNPC>* npcs, LPCSTR name, LPCSTR imageFileLocation) {
    // see if we're trying to add an NPC that already exists
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(name, npcs->at(n).GetName()) == 0) {
            // only change the image, not the name
            npcs->at(n).SetImageFileLocation(imageFileLocation);
            return FALSE;
        };
    };

    // add the NPC
    VDNPC newNPC = VDNPC(name);
    newNPC.SetImageFileLocation(imageFileLocation);
    npcs->push_back(newNPC);

    return TRUE;
};



/* **********************************************************************************************************************
   RemoveNPC
   ---------
   Removes an NPC from the list given their name.
   #param  npcs  Pointer to the list of NPCs.
   #param  name  Name of the NPC.
   **********************************************************************************************************************/

void VDNPCEditor::RemoveNPC(vector<VDNPC>* npcs, LPCSTR name) {
    // TODO swap erase method
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(name, npcs->at(n).GetName()) == 0) {
            npcs->erase(npcs->begin() + n);
        };
    };
};



/* **********************************************************************************************************************
   AddNPCDialogue
   --------------
   Tells the NPC to add a new dialogue.
   #param  npcs  Pointer to the list of NPCs.
   #param  name  Name of the NPC.
   #param  id    The ID of the dialogue.
   #param  text  The text of the dialogue.
   **********************************************************************************************************************/

void VDNPCEditor::AddNPCDialogue(vector<VDNPC>* npcs, LPCSTR name, int id, LPCSTR text) {
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(name, npcs->at(n).GetName()) == 0) {
            npcs->at(n).AddDialogue(id, text);
        };
    };
};



/* **********************************************************************************************************************
   RemoveNPCDialogue
   -----------------
   Tells the NPC to remove a dialogue.
   #param  npcs  Pointer to the list of NPCs.
   #param  name  Name of the NPC.
   #param  id    The ID of the dialogue.
   #param  text  The text of the dialogue.
   **********************************************************************************************************************/

void VDNPCEditor::RemoveNPCDialogue(vector<VDNPC>* npcs, LPCSTR name, int id) {
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(name, npcs->at(n).GetName()) == 0) {
            npcs->at(n).RemoveDialogue(id);
        };
    };
};



/* **********************************************************************************************************************
   AddNPCDialogueOption
   --------------------
   Tells the NPC to tell the dialogue to add a new option.
   #param  npcs        Pointer to the list of NPCs.
   #param  npcName     Name of the NPC.
   #param  dialogueID  The ID of the dialogue.
   #param  text        The string of the option text.
   #param  nextID      ID of the next dialogue.
   **********************************************************************************************************************/

void VDNPCEditor::AddNPCDialogueOption(vector<VDNPC>* npcs, LPCSTR npcName, int dialogueID, LPCSTR text, int nextID) {
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(npcName, npcs->at(n).GetName()) == 0) {
            npcs->at(n).AddOption(dialogueID, text, nextID);
        };
    };
};



/* **********************************************************************************************************************
   RemoveNPCDialogueOption
   -----------------------
   Tells the NPC to tell the dialogue to remove an option.
   #param  npcs        Pointer to the list of NPCs.
   #param  npcName     Name of the NPC.
   #param  dialogueID  The ID of the dialogue.
   #param  optionName  The string of the option text.
   #param  nextID      ID of the next dialogue.
   **********************************************************************************************************************/

void VDNPCEditor::RemoveNPCDialogueOption(vector<VDNPC>* npcs, LPCSTR npcName, int dialogueID, LPCSTR optionName) {
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(npcName, npcs->at(n).GetName()) == 0) {
            npcs->at(n).RemoveOption(dialogueID, optionName);
        };
    };
};



/* **********************************************************************************************************************
   VDOpenFile
   ----------
   Creates an open file box and sets the file name parameter to the chosen file.
   #param  window    Handle to the window opening the box.
   #param  filter    The type of filter to distinguish which types of files to open.
   #param  fileName  (out) The chosen file location.
   **********************************************************************************************************************/

void VDNPCEditor::VDOpenFile(HWND window, LPSTR filter, LPSTR fileName) {
    char file[FILE_NAME_MAX_SIZE];
    OPENFILENAME openFileBox;

    // zero out the open file box just to be on the safe side
    SecureZeroMemory(&openFileBox, sizeof(OPENFILENAME));

    // set up the open file structure
                openFileBox.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
            openFileBox.hwndOwner = window;
            openFileBox.lpstrFile = file;
         openFileBox.lpstrFile[0] = '\0';
       openFileBox.lpstrFileTitle = NULL;
          openFileBox.lpstrFilter = filter;
      openFileBox.lpstrInitialDir = NULL;
          openFileBox.lStructSize = sizeof(OPENFILENAME);
         openFileBox.nFilterIndex = 1;
             openFileBox.nMaxFile = sizeof(file);
        openFileBox.nMaxFileTitle = NULL;

    // open a file
    BOOL returnValue = GetOpenFileName(&openFileBox);
    if (returnValue != 0) {
        (void) strcpy_s(fileName, sizeof(file), file);
    } else {
        (void) strcpy_s(fileName, sizeof(file), "");
    };
};



/* **********************************************************************************************************************
   WriteNPCs
   ---------
   #param  npcs  Pointer to the list of NPCs.
   Saves the NPC's to hard disk.
   **********************************************************************************************************************/

void VDNPCEditor::WriteNPCs(vector<VDNPC>* npcs) {
    HANDLE file = NULL;
    BOOL returnValue = FALSE;
    DWORD numberOfBytesWritten = 0;
    size_t numberOfNPCs = 0;

    // open the file
    file = CreateFile("Data/NPCs.vd", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (file == INVALID_HANDLE_VALUE) {
        FreeMemory();
        VDError("Failed to create the NPCs.vd file.", __FILE__, __LINE__);
    };

    // make sure we let it know how many NPC elements we're adding
    numberOfNPCs = npcs->size();
    returnValue = WriteFile(file, &numberOfNPCs, sizeof(numberOfNPCs), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        FreeMemory();
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write all the NPCs
    for (unsigned int n = 0; n < npcs->size(); n++) {
        npcs->at(n).WriteNPC(file);
        if (returnValue == FALSE) {
            FreeMemory();
            VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
        };
    };

    // close the file
    returnValue = CloseHandle(file);
    if (returnValue == FALSE) {
        FreeMemory();
        VDError("Failed to close the NPCs.vd file.", __FILE__, __LINE__);
    };
};



/* **********************************************************************************************************************
   CreateNPCTexture
   ----------------
   Loads a NPC texture represented on an NPC sheet if it exists, creates it otherwise.
   #param  direct3DDevice  The Direct3D COM object.
   #param  npcFileName     The path and file name of the NPC texture.
   **********************************************************************************************************************/

void VDNPCEditor::CreateNPCTexture(LPDIRECT3DDEVICE9 direct3DDevice, LPCSTR npcFileName) {
    RECT sourceRect;
    LPDIRECT3DTEXTURE9 tempTexture = NULL;

    // get the extension
    char extension[10];
    int n = 0, m = 0;
    while (npcSpriteSheetLocation[n] != '.') {
        n++;
    };
    while (npcSpriteSheetLocation[n] != '\0') {
        extension[m] = npcSpriteSheetLocation[n];
        n++;
        m++;
    };
    extension[m] = '\0';

    UINT sourceSpriteWidth = npcSpriteSheet->GetSpriteWidth();
    // create a new texture using the width of the old texture
    HRESULT returnValue = D3DXCreateTexture(direct3DDevice, sourceSpriteWidth, 32, D3DX_DEFAULT, 0, D3DFMT_X8R8G8B8, D3DPOOL_SCRATCH, &tempTexture);
    D3DLOCKED_RECT sourceSprite, destSprite;
    returnValue = npcSpriteSheet->GetTexture()->LockRect(0, &sourceSprite, NULL, D3DLOCK_READONLY);
    if (FAILED(returnValue)) {
        FreeMemory();
        VDDirectXError("Failed to lock the texture.", returnValue, __FILE__, __LINE__);
    }
    returnValue = tempTexture->LockRect(0, &destSprite, NULL, D3DLOCK_DISCARD);
    if (FAILED(returnValue)) {
        npcSpriteSheet->GetTexture()->UnlockRect(0);
        FreeMemory();
        VDDirectXError("Failed to lock the texture.", returnValue, __FILE__, __LINE__);
    };

    UINT bytesPerPixel = 4;
    npcSpriteSheet->GetSourceRectangle(&sourceRect);
    const BYTE* sourceLoc = (const BYTE*) sourceSprite.pBits + sourceRect.top * sourceSpriteWidth * bytesPerPixel;
    for (int n = 0; n < 32; n++) {
        BYTE* destAddress = (BYTE *) destSprite.pBits + n * destSprite.Pitch;
        memcpy(destAddress, sourceLoc, sourceSpriteWidth * bytesPerPixel);
        sourceLoc += sourceSprite.Pitch;
    };

    npcSpriteSheet->GetTexture()->UnlockRect(0);
    tempTexture->UnlockRect(0);

    // save the texture to hard disk - requires a directory Images to be in the app directory
    if (strcmp(extension, ".png") == 0) {
        returnValue = D3DXSaveTextureToFile(npcFileName, D3DXIFF_PNG, tempTexture, NULL);
        if (FAILED(returnValue)) {
            FreeMemory();
            VDDirectXError("Failed to save texture.", returnValue, __FILE__, __LINE__);
        };
    } else if (strcmp(extension, ".bmp") == 0) {
        returnValue = D3DXSaveTextureToFile(npcFileName, D3DXIFF_BMP, tempTexture, NULL);
        if (FAILED(returnValue)) {
            FreeMemory();
            VDDirectXError("Failed to save texture.", returnValue, __FILE__, __LINE__);
        };
    };

    if (tempTexture != NULL) {
        tempTexture->Release();
        tempTexture = NULL;
    };

    FreeMemory();

    (void) strcpy_s(npcSpriteSheetLocation, "");
};



/* **********************************************************************************************************************
   Accessors and Modifiers
   **********************************************************************************************************************/

VDNPC VDNPCEditor::GetNPC(LPCSTR aName, vector<VDNPC>* npcs) {
    for (unsigned int n = 0; n < npcs->size(); n++) {
        if (strcmp(aName, npcs->at(n).GetName()) == 0) {
            return npcs->at(n);
        };
    };

    return NULL;
};

HWND VDNPCEditor::GetWindow() {
    return window;
};

HWND VDNPCEditor::GetNPCListBox() {
    return npcListBox;
};

HWND VDNPCEditor::GetNameEditBox() {
    return nameEditBox;
};

HWND VDNPCEditor::GetLoadSpriteButton() {
    return loadSpriteButton;
};

VDSprite* VDNPCEditor::GetNPCSpriteSheet() {
    return npcSpriteSheet;
};

LPCSTR VDNPCEditor::GetNPCSpriteSheetLocation() {
    return npcSpriteSheetLocation;
};

LPRECT VDNPCEditor::GetNPCSpriteDimensions(LPRECT outRect) {
    outRect->left = npcSpriteDimensions.left;
    outRect->top = npcSpriteDimensions.top;
    outRect->right = npcSpriteDimensions.right;
    outRect->bottom = npcSpriteDimensions.bottom;

    return &npcSpriteDimensions;
};

VDSprite* VDNPCEditor::GetNPCSprite() {
    return npcSprite;
};

HWND VDNPCEditor::GetShiftNPCUp() {
    return shiftNPCUp;
};

HWND VDNPCEditor::GetShiftNPCDown() {
    return shiftNPCDown;
};

HWND VDNPCEditor::GetPreviousAnimation() {
    return previousAnimation;
};

HWND VDNPCEditor::GetNextAnimation() {
    return nextAnimation;
};

HWND VDNPCEditor::GetRemoveNPCButton() {
    return removeNPCButton;
};

HWND VDNPCEditor::GetSaveNPCButton() {
    return saveNPCButton;
};

HWND VDNPCEditor::GetDialogueListBox() {
    return dialogueListBox;
};

HWND VDNPCEditor::GetTextEditBox() {
    return textEditBox;
};

HWND VDNPCEditor::GetTextIDEditBox() {
    return textIDEditBox;
};

HWND VDNPCEditor::GetRemoveDialogueButton() {
    return removeDialogueButton;
};

HWND VDNPCEditor::GetSaveDialogueButton() {
    return saveDialogueButton;
};

HWND VDNPCEditor::GetOptionsListBox() {
    return optionListBox;
};

HWND VDNPCEditor::GetTextEditBox2() {
    return textEditBox2;
};

HWND VDNPCEditor::GetTextIDEditBox2() {
    return textIDEditBox2;
};

HWND VDNPCEditor::GetRemoveOptionButton() {
    return removeOptionButton;
};

HWND VDNPCEditor::GetSaveOptionButton() {
    return saveOptionButton;
};

HWND VDNPCEditor::GetSaveChangesButton() {
    return saveChangesButton;
};