Skip to main content
GameDev.net gamedev.net
🔒 Locked

2D Map Editor help

Started by Dransic Apr 4, 2005 at 6:41 PM 2 replies 1.6k views
Original Post
Dransic
Dransic
Hey, I've been messing with the gametutorials 2d map ediotr series for a while and I am trying to add the ability to scroll the map over to view larger maps. But every time I set the map width and height to higher then 20 it gives me an access violation and crashes. But when the map is set to below 20x20 and you try to scroll it leaves streaks on the bg and scrolls beyond what it should be able to. heres all the code: CBitmap.cpp

#include "main.h"
#include "Bitmap.h"

//the constructor for the CBuffer class
CBuffer::CBuffer():hWnd(0),hCompBitmap(0),hOldBitmap(0),hdcFront(0),hdcBack(0),oldPen(0),oldBrush(0)
{
	memset(&scrnRect, 0, sizeof(RECT));
}

//deconstructor for the CBuffer class
CBuffer::~CBuffer()
{
	// We need to make sure if we used a pen or brush the original
	// pen and brushes are restored so there isn't a memory leak.
	if(oldPen)
	{
		HPEN setPen = (HPEN)SelectObject(hdcBack, oldPen);
		DeleteObject(setPen);
	}

	if(oldBrush)
	{
		HBRUSH setBrush = (HBRUSH)SelectObject(hdcBack, oldBrush);
		DeleteObject(setBrush);
	}

	// Free our back buffer information
	FreeBuffers();
}

//creates a double buffer. Pass in a buffer struct.
void CBuffer::CreateDoubleBuffering(HWND hWndNew)
{
	// Assign the window handle to our structure
	hWnd = hWndNew;									
	
	// Store the client rectangle in our structure
	GetClientRect(hWnd, &(scrnRect));

	// Get a handle to the device context and store it as the front buffer
	hdcFront = GetDC(hWnd);

	// Create a compatible device context for the backbuffer
	hdcBack = CreateCompatibleDC(hdcFront);

	// Create another hdc to store the bitmap in before the backbuffer
	hdcBitmap = CreateCompatibleDC(hdcFront);

	// Create a dummy bitmap handle to store in the front buffer
	hCompBitmap = CreateCompatibleBitmap(hdcFront, scrnRect.right,
scrnRect.bottom);

	// Select the dummy bitmap handle into the back buffer to make it like the front hdc
	hOldBitmap = (HBITMAP)SelectObject(hdcBack, hCompBitmap);	
}

//loads a bitmap and returns a handle to it
HBITMAP CBuffer::LoadABitmap(LPSTR szFileName)
{
	// Load the bitmap and return the handle to the bitmap we just loaded
	return (HBITMAP)LoadImage(NULL, szFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}

//puts a bitmap in the back buffer
void CBuffer::DisplayBitmap(HBITMAP hBitmap, int x, int y)
{
	// Select the bitmap handle into the extra hdc that holds the bitmap
	HBITMAP hOldBitmap = (HBITMAP) SelectObject(hdcBitmap, hBitmap);

	// Blit the bitmap hdc into the backbuffer
	BitBlt(hdcBack, x, y, scrnRect.right, 
		   scrnRect.bottom,  hdcBitmap, 0, 0, SRCCOPY);

	// Select the old handle to the bitmap back where it was to clean up
	SelectObject(hdcBitmap, hOldBitmap);
}

//takes a portion of a bitmap and puts it in the back buffer
void CBuffer::DisplayBitmap(HBITMAP hBitmap, int x, int y, RECT rPortion)
{
    // Assign the width and height to shorten variables. 
	int width = rPortion.right - rPortion.left, height = rPortion.bottom - rPortion.top;

	// Select the bitmap handle into the extra hdc that holds the bitmap
	HBITMAP hOldBitmap = (HBITMAP) SelectObject(hdcBitmap, hBitmap);

	// Blit the bitmap hdc into the backbuffer using the transparent color 0 (black)
	BOOL result = BitBlt(hdcBack, x, y, width, height, 
				   hdcBitmap, rPortion.left, rPortion.top, SRCCOPY);

	// Select the old handle to the bitmap back where it was to clean up
	SelectObject(hdcBitmap, hOldBitmap);
}

//displays a bitmap with transparentcy to the screen
void CBuffer::TransparentBlit(HDC hdc, int destX, int destY, int destWidth, int destHeight, 
						      HDC hdc2, int srcX, int srcY, UINT transparency)
{
	unsigned char* pImageBits = NULL;
	unsigned char* pBackBits = NULL;
	BITMAPINFO bmBitmapInfo = {0};
	HBITMAP hBitmap, hBitmap2, hOldBitmap, hOldBitmap2;
	HDC compHDC;
	HDC compHDC2;
	
	// Fill in our BitmapInfo structure (we want a 24 bit image)
	bmBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmBitmapInfo.bmiHeader.biCompression = BI_RGB;
	bmBitmapInfo.bmiHeader.biHeight = destHeight;
	bmBitmapInfo.bmiHeader.biWidth = destWidth;	
	bmBitmapInfo.bmiHeader.biBitCount = 24;
	bmBitmapInfo.bmiHeader.biClrUsed = 0;
	bmBitmapInfo.bmiHeader.biPlanes = 1;

	// Create 2 DIB Sections.  One for the Front Buffer and one for the BackBuffer
	hBitmap  = CreateDIBSection(hdc,  &bmBitmapInfo, DIB_RGB_COLORS,(void **)&pImageBits, 0,0);	
	hBitmap2  = CreateDIBSection(hdc,  &bmBitmapInfo, DIB_RGB_COLORS,(void **)&pBackBits, 0,0);

	// Create a compatible DC for the front buffer and Select our Dib Section into it
	compHDC = CreateCompatibleDC(hdc);
	hOldBitmap = (HBITMAP)SelectObject(compHDC, hBitmap);
	
	// Create a compatible DC for the back buffer and Select our Dib Section into it
	compHDC2 = CreateCompatibleDC(hdc2);
	hOldBitmap2 = (HBITMAP)SelectObject(compHDC2, hBitmap2);

	// Blit the front buffer to our compatible DC that will hold the destination bits
	BitBlt(compHDC, 0, 0, destWidth, destHeight, hdc, destX, destY, SRCCOPY);

	// Blit the back buffer to our compatible DC that will hold the source bits
	BitBlt(compHDC2, 0, 0, destWidth, destHeight, hdc2, srcX, srcY, SRCCOPY);

	// Loop through the 24 bit image (Times 3 for R G and B)
	for(int i = 0; i < destHeight * destWidth * 3; i += 3)
	{
		// Check if the current pixel being examined isn't the transparent color
		// Remember, the image bits are stored (Blue, Green, Red), not (Red, Green, Blue)
		// We use the system macros to abstract the R G B data
		if((pBackBits   != GetBValue(transparency)) || 
		   (pBackBits[i+1] != GetGValue(transparency)) || 
		   (pBackBits[i+2] != GetRValue(transparency)))
		{
			// Assign the desired pixel to the foreground
			pImageBits     = pBackBits;
			pImageBits[i + 1] = pBackBits[i + 1];
			pImageBits[i + 2] = pBackBits[i + 2];
		}
	}  

	// Blit the transparent image to the front buffer (Voila!)
	BitBlt(hdc, destX, destY, destWidth, destHeight, compHDC, 0, 0, SRCCOPY);

	// Cleanup the monochrome bitmaps
	SelectObject(compHDC, hOldBitmap);
	SelectObject(compHDC2, hOldBitmap2);	

	// Free GDI Resources
	DeleteObject(hBitmap);
	DeleteObject(hBitmap2);
	DeleteDC(compHDC);
	DeleteDC(compHDC2);
}

//This takes a bitmap and puts it in the backbuffer
//BUT, not displaying any color with rgb values of (0, 0, 0).
void CBuffer::DisplayTransparentBitmap(HBITMAP hBitmap, int x, int y, RECT rPortion)
{
	// Assign the width and height to shorten variables. 
	int width = rPortion.right - rPortion.left, height = rPortion.bottom - rPortion.top;

	// Select the bitmap handle into the extra hdc that holds the bitmap
	HBITMAP hOldBitmap = (HBITMAP) SelectObject(hdcBitmap, hBitmap);

	// Blit the bitmap hdc into the backbuffer using the transparent color 0 (black)
	TransparentBlit(hdcBack, x, y, width, height, 
				    hdcBitmap, rPortion.left, rPortion.top, TRANSPARENT_COLOR);

	// Select the old handle to the bitmap back where it was to clean up
	SelectObject(hdcBitmap, hOldBitmap);
}

//draws and stretches a bitmap to the backbuffer
void CBuffer::DrawBackgroundImage(HBITMAP hBitmap)
{
	// This will hold our information on the bitmap
	BITMAP bitmapInfo;

	// Select the bitmap handle into the extra hdc that holds the bitmap
	HBITMAP hOldBitmap = (HBITMAP) SelectObject(hdcBitmap, hBitmap);

	// Get the width and height of the background bitmap
	GetObject(hBitmap, sizeof(BITMAP), &bitmapInfo);

	// Blit the bitmap hdc into the backbuffer using the transparent color 0 (black)
	BOOL result = StretchBlt(hdcBack, 0, 0, scrnRect.right, scrnRect.bottom, 
				   hdcBitmap, 0, 0, bitmapInfo.bmWidth, bitmapInfo.bmHeight, SRCCOPY);

	// Select the old handle to the bitmap back where it was to clean up
	SelectObject(hdcBitmap, hOldBitmap);
}

//clears the screen
void CBuffer::ClearScreen(int color)
{
	// Fill the backbuffer with white to erase the previous position of the bitmap
	FillRect(hdcBack, &scrnRect, (HBRUSH)GetStockObject(color));
}

//sets a pen
void CBuffer::SetPen(int type, int thickness, COLORREF color)
{
	// First create a brush that is hollow for shapes
	LOGBRUSH logBrush = {BS_HOLLOW, DIB_RGB_COLORS, NULL};

	// Set our new brush and our desired pen type
	HBRUSH hBrush = CreateBrushIndirect(&logBrush);
	HPEN hPen = CreatePen(type, thickness, color);

	// Make sure we only store the old original pen if we haven't yet
	if(!oldPen)
	{
		// Select both into our HDC
		oldPen = (HPEN)SelectObject(hdcBack,hPen);
		oldBrush = (HBRUSH)SelectObject(hdcBack, hBrush);
	}
	else
	{
		HPEN lastPen = (HPEN)SelectObject(hdcBack,hPen);
		DeleteObject(lastPen);
	}
}

//swaps the back buffer with the front
void CBuffer::SwapBackBuffer(BOOL bClearBackBuffer)
{
	// Blit the whole backbuffer to the screen (The front buffer)
	BitBlt(hdcFront, scrnRect.left, scrnRect.top, 
		   scrnRect.right, scrnRect.bottom, hdcBack, 0, 0, SRCCOPY);

	// If we want to clear the screen afterwards, set the background to white
	if(bClearBackBuffer) 
		ClearScreen(BLACK_BRUSH);
}

//resets the back and front buffer to the client window
void CBuffer::ResizeBuffers()
{
	// Get the new client rectangle of the window
	GetClientRect(hWnd, &(scrnRect));
			
	// Select the old bitmap back into the back buffer
	SelectObject(hdcBack, hOldBitmap);

	// Delete the old compatible bitmap
	DeleteObject(hCompBitmap);

	// Create a new compatible bitmap with the new size of the window
	hCompBitmap = CreateCompatibleBitmap(hdcFront, scrnRect.right, scrnRect.bottom);

	// Select the new compatible bitmap into the backbuffer and store the old one again
	hOldBitmap = (HBITMAP) SelectObject(hdcBack, hCompBitmap);	
}

//frees the buffers for the window
void CBuffer::FreeBuffers()
{
	// Set the back buffer to it's original bitmap (This is necessary for clean-up)
	SelectObject(hdcBack, hOldBitmap);

	// Free the hbitmaps
	if(hOldBitmap)	DeleteObject(hOldBitmap);
	if(hCompBitmap)	DeleteObject(hCompBitmap);

	// Free all the HDC's
	if(hdcBack)		ReleaseDC(hWnd, hdcBack);
	if(hdcBitmap)	ReleaseDC(hWnd, hdcBitmap);
	if(hdcFront)	ReleaseDC(hWnd, hdcFront);
}

CBitmap.h

#ifndef _BITMAP_H
#define _BITMAP_H

#include "main.h"

#define TRANSPARENT_COLOR RGB(255, 255, 255) // the transparent color

//backbuffer class
class CBuffer					
{
public:

	CBuffer();
	~CBuffer();

	// Create the double buffering for all of our bitmaps
	void CreateDoubleBuffering(HWND hWnd);

	// This loads a bitmap (ie. "Bitmap.bmp") and returns a handle to that bitmap
	HBITMAP LoadABitmap(LPSTR szFileName);

	// This displays the bitmap to the screen at a X and a Y location
	void DisplayBitmap(HBITMAP hBitmap, int x, int y);

	// This display a portion of a bitmap with a transparency to (0, 0, 0) (pure black)
	void DisplayTransparentBitmap(HBITMAP hBitmap, int x, int y);

	// Displays a portion of a bitmap to the backbuffer (Used from one bitmap animation)
	void DisplayBitmap(HBITMAP hBitmap, int x, int y, RECT rPortion);

	// This does the dirty pixel work to make the bitmap a transparent image
	void TransparentBlit(HDC hdc, int destX, int destY, int destWidth, int destHeight, 
				         HDC hdc2, int srcX, int srcY, UINT transparency);

	// This display a portion of a bitmap with a transparency to (0, 0, 0) (pure black)
	void DisplayTransparentBitmap(HBITMAP hBitmap, int x, int y, RECT rPortion);

	// This stretches the image to the window rectangle to fill up the background
	void DrawBackgroundImage(HBITMAP hBitmap);

	// Clears the backbuffer to what ever color is passed in
	void ClearScreen(int color);

	void SetPen(int type, int thickness, COLORREF color);

	// Swaps the back buffer to the screen
	void SwapBackBuffer(BOOL bClearBackBuffer);

	// This resizes the buffers to the current window size
	void ResizeBuffers();

	// This frees the back buffer information
	void FreeBuffers();

	// This returns the back buffer hdc
	HDC GetHDC()	{ return hdcBack; };

private:

	HWND hWnd;					// This holds the current window's handle
	RECT scrnRect;				// This holds the client rectangle of the window
	HANDLE hCompBitmap;			// This holds the compatible bitmap for the backbuffer
	HANDLE hOldBitmap;			// This is used for storage to free when the program quits
	HDC hdcFront;				// This is the front buffer (The part we see)
	HDC hdcBack;				// This is the back buffer (the part we draw to, then flip)
	HDC hdcBitmap;				// This is a temp buffer to swap the bitmap back and forth from
	HPEN oldPen;				// This holds the original pen stored by windows
	HBRUSH oldBrush;			// This holds the original brush stored by windows
};

#endif

Init.cpp

#include "main.h"

HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle, HINSTANCE hInstance)
{
	HWND hWnd;
	WNDCLASS wndclass;
	int positionFlag = CW_USEDEFAULT;
	
	memset(&wndclass, 0, sizeof(WNDCLASS));				// Init the size of the class
	wndclass.style = CS_HREDRAW | CS_VREDRAW;			// Regular drawing capabilities
	wndclass.lpfnWndProc = WndProc;						// Pass our function pointer as the window procedure
	wndclass.hInstance = hInstance;						// Assign our hInstance
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);	// General icon
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);		// An arrow for the cursor
	wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);	// A white window
	wndclass.lpszClassName = "MEclass";			// Assign the class name

	wndclass.lpszMenuName = "EditorMenu";

	RegisterClass(&wndclass);							// Register the class
	
	if(!dwStyle)										// Assign styles to the window depending on the choice
		dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	
	g_hInstance = hInstance;							// Assign our global hInstance to the window's hInstance

	RECT rWindow;
	rWindow.left	= 0;								// Set Left Value To 0
	rWindow.right	= width;							// Set Right Value To Requested Width
	rWindow.top	    = 0;								// Set Top Value To 0
	rWindow.bottom	= height;							// Set Bottom Value To Requested Height

	AdjustWindowRect( &rWindow, dwStyle, false);		// Adjust Window To True Requested Size

														// Create the window
	hWnd = CreateWindow("MEclass", strWindowName, dwStyle, positionFlag , positionFlag,
						rWindow.right  - rWindow.left, rWindow.bottom - rWindow.top, 
						NULL, NULL, hInstance, NULL);

	ShowWindow(hWnd, SW_SHOWNORMAL);					// Show the window
	UpdateWindow(hWnd);									// Draw the window

	SetFocus(hWnd);										// Sets Keymap Focus To The Window	

	return hWnd;
}

HWND CreateToolWindow(HWND hWndParent, LPSTR strWindowName, int width, int height, DWORD dwStyle, HINSTANCE hInstance)
{
	WNDCLASSEX wndclassEx;
	
	memset(&wndclassEx, 0, sizeof(WNDCLASS));				// Init the size of the class
	wndclassEx.cbSize = sizeof(wndclassEx);
	wndclassEx.style = CS_HREDRAW | CS_VREDRAW;				// Regular drawing capabilities
	wndclassEx.lpfnWndProc = WndProcPopup;					// Pass our function pointer as the window procedure
	wndclassEx.hInstance = hInstance;						// Assign our hInstance
	wndclassEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);		// General icon
	wndclassEx.hCursor = LoadCursor(NULL, IDC_ARROW);		// An arrow for the cursor
	wndclassEx.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);	// A white window
	wndclassEx.lpszClassName = "ToolWindowClass";			// Assign the class name
	wndclassEx.hIconSm = 0;

	RegisterClassEx(&wndclassEx);

	RECT rWindow;
	rWindow.left	= 0;								// Set Left Value To 0
	rWindow.right	= width;							// Set Right Value To Requested Width
	rWindow.top	    = 0;								// Set Top Value To 0
	rWindow.bottom	= height;							// Set Bottom Value To Requested Height

	AdjustWindowRect( &rWindow, dwStyle, false);		// Adjust Window To True Requested Size

	RECT rParentWindow = {0};							

	GetWindowRect(hWndParent, &rParentWindow);			// Get the window position of the main window
														// Set the tool bar on the right of the main window
	HWND hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, "ToolWindowClass", strWindowName, dwStyle, 
							   rParentWindow.right, rParentWindow.top, rWindow.right  - rWindow.left, 
							   rWindow.bottom - rWindow.top, hWndParent, NULL, hInstance, NULL);

	ShowWindow(hWnd, SW_SHOWNORMAL);					// Show the window
	UpdateWindow(hWnd);									// Draw the window

	return hWnd;
}

void DeInit()
{
    for(int i = 0; i < (int)g_vTiles.size(); i++)
		DeleteObject(g_vTiles.GetBitmap());
		
	UnregisterClass("ToolWindowClass", g_hInstance);
	UnregisterClass("MEclass", g_hInstance);
	PostQuitMessage(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{	
	// Create the main window and offset the height by the menu pixel height
	HWND hWnd = CreateMyWindow("Map-Editor nojutsu", 
			    			   SCREEN_WIDTH, SCREEN_HEIGHT + MENU_OFFSET,
                                                     WS_HSCROLL | WS_VSCROLL, hInstance);

	// If we never got a valid window handle, quit the program
	if(hWnd == NULL) return TRUE;

	// Create our tool bar window offset by the scroll bar pixel width and to compensate
	// for the small title bar we offset the height with the menu offset
	g_hWndTool = CreateToolWindow(hWnd, "", TILE_WIDTH + SCROLL_BAR_OFFSET, 32*15 + MENU_OFFSET, 
								  WS_POPUP| WS_SYSMENU | WS_CAPTION , hInstance);

	// Initialize our important things after the window is created
	Init(hWnd);													

	// Run our message loop and after it's done, return the result
	return (int)MainLoop();						
}

main.cpp

#include "main.h"								// Include our main header file

HWND  g_hWnd;                                   // This is a global handle for the window
HWND  g_NewDlg;									// This is a global handle for the toolbar window
HWND  g_hWndTool;
					
HINSTANCE g_hInstance;							// This holds the global hInstance for UnregisterClass() in DeInit()

CBuffer g_Buffer;								// Create a buffer for the main window
CBuffer g_ToolBuffer;							// Create a buffer for the tool bar window

vector<CTile> g_vTiles;							// Create a list for the map tiles to be loaded
							

// This stores our scroll bar information.  For more info on scroll bars 
// check out our scroll bar tutorial.
SCROLLINFO g_ScrollInfo = {0};
int g_scrollBarPosY = 0;						// Set the current scroll bar position to 0

SCROLLINFO g_mapscrollx = {0};
int g_mapsx = 0;

SCROLLINFO g_mapscrolly = {0};
int g_mapsy = 0;

//current map
CMap *g_pCurrentMap = NULL;						

// This is our map class which holds all the information for the current map.  When we load
// a map, all that information is stored here.  This is the most important class in our editor.
CMap g_Map;

//curser and curser tile info
CTile *g_pCursorTile = NULL;
POINT g_cursorPos = {0};
bool g_bLButtonDown = false;


// Create our global variables for our open file dialog box
OPENFILENAME g_OpenInfo = {0};
char g_szFileName[MAX_PATH] = {0};


//curser tile drawing function
void DrawCursorTile()
{
	// Create a rect to specify what part of our bitmaps we want to draw
	RECT rPortion = {0, 0, TILE_WIDTH, TILE_HEIGHT};

	// Here we update our cursor position and convert it to client 
	// coordinates (our window coordinates), not the screen coordinates.
	GetCursorPos(&g_cursorPos);
	ScreenToClient(g_hWnd, &g_cursorPos);

	// Since exits dont have a bitmap associated with them, we need
	// to tell this function it's okay if there isn't a valid cursor tile
	// only if the current type selected is exits.
	if(g_pCurrentMap->GetCurrentType() != EXIT_TYPE)
		if(!g_pCursorTile) return;
	
	// If the current type is a normal map tile, draw it with no transparency.
	// We line up the cursor bitmap so that it's dead center on the cursor.
	if(g_pCurrentMap->GetCurrentType() == TILE_TYPE)
		g_Buffer.DisplayBitmap(g_pCursorTile->GetBitmap(), 
							   g_cursorPos.x - TILE_WIDTH/2, g_cursorPos.y - TILE_HEIGHT/2, rPortion); 
	// Otherwise draw a transparent bitmap for the rest
	else
		g_Buffer.DisplayTransparentBitmap(g_pCursorTile->GetBitmap(), 
										  g_cursorPos.x - TILE_WIDTH/2, g_cursorPos.y - TILE_HEIGHT/2, rPortion); 

	// If the left button is still held down
	if(g_bLButtonDown)
	{
		// Grab the index on our 2D map depending on our cursor position
		int mapIndexX = g_cursorPos.x / TILE_WIDTH;
		int mapIndexY = g_cursorPos.y / TILE_HEIGHT;

		// Insert the tile type into our map according to it's index
		g_pCurrentMap->InsertTile(g_pCursorTile, mapIndexX, mapIndexY); 
	}
}

void RenderScene()
{
	// Make sure we have a current map pointer before we go shootin' blanks
	if(!g_pCurrentMap) return;

	// Here we set the portion of our bitmaps that we want to draw.
	// Since there is no animation in our tile editor we need only display 1 tile.
	RECT rPortion = {0, 0, TILE_WIDTH, TILE_HEIGHT};

	// Next we draw the current map (Default is just all green grass tiles)
	g_pCurrentMap->Draw();

	// Grab the size of the current tool bar type list (Not our map's placed tile lists)
	int size = g_pCurrentMap->GetCurrentTypeSize();

	// Go through our tiles in the tool bar and draw them, but subtract the scroll bar position
	// from the size.  This makes it so we only draw the tiles that can be seen in the tool bar.
	for(int i = 0; i < size - g_scrollBarPosY; i++)
	{
		// Depending on "i" and the scroll bar position, get the current tile's bitmap to draw
		HBITMAP bitmap = g_pCurrentMap->GetCurrentTypeTile(i + g_scrollBarPosY)->GetBitmap();
		
		// If the tool bar is showing map tiles then draw the tiles with no transparency
		if(g_pCurrentMap->GetCurrentType() == TILE_TYPE)
			g_ToolBuffer.DisplayBitmap(bitmap, 0, TILE_WIDTH * i, rPortion);  
		else
			// Draw the current tiles with a transparency (white) (i.e. items, monsters, etc...)
			g_ToolBuffer.DisplayTransparentBitmap(bitmap, 0, TILE_WIDTH * i, rPortion);  
	}
	
	DrawCursorTile();

	g_Buffer.SwapBackBuffer(FALSE);
	g_ToolBuffer.SwapBackBuffer(TRUE);
}


WPARAM MainLoop()
{
	MSG msg;
	HACCEL hAccelTable = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));

	while(1)											// Do our infinite loop
	{													// Check if there was a message
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
        { 
			if(msg.message == WM_QUIT)					// If the message wasn't to quit
				break;
			
			// Check if there was keyboard command - if not, process messages like normal
			if(!TranslateAccelerator(g_hWnd, hAccelTable, &msg))
			{
				TranslateMessage(&msg);					// Find out what the message does
				DispatchMessage(&msg);					// Execute the message
			}

			RenderScene();								// Since no animation, only render when the user does something
        } 
	}
	DeInit();											// Clean up and free all allocated memory

	return(msg.wParam);									// Return from the program
}


void CheckTileType(int menuID)
{
	// Grab a handle to our menu
	HMENU hMenu = GetMenu(g_hWnd);

	// Uncheck all of the menu items for the Tiles menu
	CheckMenuItem(hMenu, ID_TILES_TILES, MF_UNCHECKED);

	CheckMenuItem(hMenu, ID_TILES_EXITS, MF_UNCHECKED);

	// Put a check by the desired menu item
	CheckMenuItem(hMenu, menuID, MF_CHECKED);
}

void HandleMenu(int menuID)
{      		
	if(menuID == ID_FILE_QUIT)					// If the user chose File->Quit
	{
		PostQuitMessage(0);						// Quit the program
	}
	else if(menuID == ID_FILE_OPENUP)			// If the user chose File->Open
	{	
		// Have windows bring up the Open File dialog box and Load the file chosen
		if(GetOpenFileName(&g_OpenInfo))
			g_Map.Load(g_OpenInfo.lpstrFile);
	}
	else if(menuID == ID_FILE_SAVEIT)			// If the user chose File->Save
	{
		// If we have a n valid name for our map already, do a normal save
		if(strlen(g_Map.GetMapName()) > 3)
			g_Map.Save(g_Map.GetMapName());
		else
		{
			// If haven't given our map a name yet, do a "SaveAs" and bring up the save dlg box
			if(GetSaveFileName(&g_OpenInfo))
				g_Map.Save(g_OpenInfo.lpstrFile);
		}
	}
	else if(menuID == ID_FILE_SAVEAS)			// If the user chose File->SaveAs
	{
		// Bring up the save dlg box and allow the user to type in a new map name and save it
		if(GetSaveFileName(&g_OpenInfo))
			g_Map.Save(g_OpenInfo.lpstrFile);
	}
	else if(menuID == ID_FILE_RESET)			// If the user chose File->Reset
		g_Map.SetDefault();						// Destroy all the tiles on the map and start over
	else if(menuID == ID_TILES_TILES)			// If the user chose Tiles->Tiles
		g_Map.SetCurrentType(TILE_TYPE);		// Set the tool bar tile type to tiles
	else if(menuID == ID_HELP_ABOUT)			// If the user chose Help->About
		// Instead of creating a help dialog box, we can just use a simple system MessageBox() call
		MessageBox(g_hWnd, "www.GameTutorials.com Map Editor\n\n"
						   "\t- Choose the desired tile type from the Tiles menu,\n"
						   "\t  then click on a tile in the tile window. You can\n"
						   "\t  then draw the tiles on the map with the left mouse button.\n\n"
						   										   "\t- Hit Esc to quit the program.", "About", MB_OK | MB_ICONINFORMATION);

	// Over assuming that a tile type was changed, reset the scroll bar size.
	// The -1 is because the tool bar window's title size offsets it a bit (being smaller).
	g_ScrollInfo.nMax = g_Map.GetCurrentTypeSize() - MAP_HEIGHT - 1;
	
	// If there isn't enough (or any) tiles to fill up the tool bar window, disable the scroll bar
	if(g_ScrollInfo.nMax < 0)
	{
		g_ScrollInfo.nMax = 0;
		EnableScrollBar(g_hWndTool, SB_VERT, ESB_DISABLE_BOTH);
	}
	else
		EnableScrollBar(g_hWndTool, SB_VERT, ESB_ENABLE_BOTH);	// Enable the scroll bar if it's needed

	// Reset the scroll bar info
	g_scrollBarPosY = 0;
	g_ScrollInfo.nPage = 0;

	// Update the current scroll bar information with no need to redraw (FALSE)
	SetScrollInfo(g_hWndTool, SB_VERT, &g_ScrollInfo, FALSE);
	ShowScrollBar(g_hWndTool, SB_VERT, TRUE);
	
	// If we chose a tile type, put a check box next to it
	switch(menuID)
	{
		case ID_TILES_TILES: 
		    g_pCursorTile = NULL;
			CheckTileType(menuID);
			break;
	}
}


void HandleScrollBar(WPARAM wParam)
{
	switch(LOWORD(wParam)) 
	{
		case SB_LINEUP: case SB_PAGEUP:					// If we choose the up button or click above the middle button
			g_scrollBarPosY = g_scrollBarPosY - 1;		// Decrease the scroll bar position
				break;

		case SB_LINEDOWN: case SB_PAGEDOWN:				// If we choose the down button or click below the middle button
			g_scrollBarPosY = g_scrollBarPosY + 1;		// Increase the scroll bar position
				break;

		case SB_THUMBPOSITION: case SB_THUMBTRACK:		// If we dragged the middle button (thumb)
			g_scrollBarPosY = HIWORD(wParam);			// Update it's new position
				break;

		default:
			break;

	}
	
	SCROLLINFO scrollInfo;								// Create a new temporary scroll bar info variable

	scrollInfo.cbSize = sizeof(SCROLLINFO);				// Initialize it's size
	scrollInfo.fMask = SIF_POS | SIF_RANGE;				// Set the flags so that we get the position and range updates
	scrollInfo.nPage = 1;								// Set the page size to 1 (a page increases by 1 tile)
			
	GetScrollInfo(g_hWndTool, SB_VERT, &scrollInfo);	// Retrieve the current scroll info

	if(g_scrollBarPosY != scrollInfo.nPos)				// If the scroll bar position has changed
	{
		if(g_scrollBarPosY > scrollInfo.nMax)			// If we have gone past the first page of the scroll bar
			g_scrollBarPosY = scrollInfo.nMax;			// Set the current position
		else if(g_scrollBarPosY < scrollInfo.nMin)		// If go below the minimum value of the scroll bar set it back
			g_scrollBarPosY = scrollInfo.nMin;

		scrollInfo.nPos = g_scrollBarPosY;				// Set the current scroll position and then update the scroll bar
														
		SetScrollInfo(g_hWndTool, SB_VERT, &scrollInfo, TRUE);

		InvalidateRect(g_hWndTool, NULL, FALSE);		// Update the tool bar as we scroll with the thumb button
	}
}

LRESULT CALLBACK WndProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	// Check which message was passed in
    switch (iMsg)											
    {
        case WM_HSCROLL:
                    switch(LOWORD(wParam))
                    {
                        case SB_LINELEFT:
                            if(g_mapsx<0)
                            g_mapsx = 0;
                            else
                            g_mapsx = g_mapsx + 1;
                            break;
                        case SB_LINERIGHT:
                            if(g_mapsx>WIDTH)
                            g_mapsx = 0;
                            else
                            g_mapsx = g_mapsx - 1;
                            break;
                        case SB_THUMBPOSITION:
                            if(g_mapsx>WIDTH || g_mapsx<0)
                            g_mapsx = 0;
                            else
                            g_mapsx = HIWORD(wParam);
                            break;
                        default:
                            break;
                    }    
            break;
            
        case WM_VSCROLL:
            switch(LOWORD(wParam))
                    {
                        case SB_LINEUP:
                            if(g_mapsy>HEIGHT)
                            g_mapsy = 0;
                            else
                            g_mapsy + 1;
                            break;
                        case SB_LINEDOWN:
                            if(g_mapsy<0)
                            g_mapsy=0;
                            else
                            g_mapsy = g_mapsy - 1;
                            break;
                        case SB_THUMBPOSITION:
                            if(g_mapsy<0 || g_mapsy>HEIGHT)
                            g_mapsy = 0;
                            else
                            g_mapsy = HIWORD(wParam);
                            break;
                        default:
                            break;
                        }    
            break;
        
        g_mapscrollx.cbSize = sizeof(SCROLLINFO);
        g_mapscrolly.cbSize = sizeof(SCROLLINFO);
        g_mapscrollx.fMask = SIF_POS | SIF_RANGE;
        g_mapscrolly.fMask = SIF_POS | SIF_RANGE; 
        GetScrollInfo(g_hWnd, SB_HORZ, &g_mapscrollx);
        GetScrollInfo(g_hWnd, SB_VERT, &g_mapscrolly);
        
        if(g_mapsx != g_mapscrollx.nPos && g_mapsy != g_mapscrolly.nPos)
			{
				if(g_mapsx > g_mapscrollx.nMax)
					g_mapsx = g_mapscrollx.nMax;

				else if(g_mapsx < g_mapscrollx.nMin)
					g_mapsx = g_mapscrollx.nMin;

				g_mapscrollx.nPos = g_mapsx;

				SetScrollInfo(g_hWnd, SB_HORZ, &g_mapscrollx, true);
				
				if(g_mapsy > g_mapscrolly.nMax)
					g_mapsy = g_mapscrolly.nMax;

				else if(g_mapsy < g_mapscrolly.nMin)
					g_mapsy = g_mapscrolly.nMin;

				g_mapscrolly.nPos = g_mapsy;

				SetScrollInfo(g_hWnd, SB_VERT, &g_mapscrolly, true);

				InvalidateRect(g_hWnd,NULL,TRUE);
			}
        
        return 0;
        
		case WM_COMMAND:							// Check if we have a command from the user						
			HandleMenu(LOWORD(wParam));				// Pass in the command ID to our menu function
			break;

		case WM_KEYDOWN:
			switch (wParam) 
			{
				case VK_ESCAPE:
					
					// Tell the window we want to QUIT
					PostQuitMessage(0);
					break;
			}
			break;
		
			
        case WM_LBUTTONDOWN:						// If we hit the left mouse button
			g_bLButtonDown = true;					// Set our global boolean to true
			break;

		case WM_LBUTTONUP:							// If we let off of the left button
			g_bLButtonDown = false;					// Set our global boolean value to false
			break;

		case WM_RBUTTONDOWN:						// If we click the right button
			if(g_pCursorTile == NULL)				// If we don't have a current cursor tile
			{										
				// Here we delete the tile at our current mouse position from the map
				g_pCurrentMap->DeleteTile(g_cursorPos.x/TILE_WIDTH, g_cursorPos.y/TILE_HEIGHT);
			}
			else									
				g_pCursorTile = NULL;				// Otherwise remove the cursor tile
			break;

		case WM_PAINT:								// If we need to paint the screen
			RenderScene();							// Update the screen and draw the map
			break;

		// Since we created a child window for our tool bar, we want both windows
		// to be constantly active.  By return TRUE in DefWindowProc() we can have
		// the window not go inactive when we click on another window.
		case WM_NCACTIVATE:
			return DefWindowProc(hWnd, iMsg, TRUE, lParam);

		// By return 1 when this message is called we eliminate have our window background
		// redrawn.  This makes it so we don't see flicker when our tool bar window is
		// dragged over our main window.  Cool little trip huh :)
		case WM_ERASEBKGND:
			return 1;

		// This message is sent when the window is destroyed
		case WM_DESTROY:									
			
			// Tell the window we need to close
			PostQuitMessage(0);							
			break;				
	}													

	// Process/handle the default messages
	return DefWindowProc (hWnd, iMsg, wParam, lParam);		
															
}

LRESULT CALLBACK WndProcPopup(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	// Check which message was passed in
    switch (iMsg)											
    {
		// Draw the scene when necessary
		case WM_PAINT:
			RenderScene();
			break;
		
		case WM_LBUTTONDOWN:								// If we click the left button in the tool bar
			g_cursorPos.x = LOWORD(lParam);					// Grab the x and y position of the cursor
			g_cursorPos.y = HIWORD(lParam);
			
			// Here we grab a pointer to the tile type that we clicked on (used for drawing on the cursor)
			g_pCursorTile = g_pCurrentMap->GetCurrentTypeTile(g_cursorPos.y/TILE_HEIGHT + g_scrollBarPosY);
			break;
		
		// Check if we need to handle a scroll bar message
		case WM_VSCROLL:
			HandleScrollBar(wParam);
			break;

		// By sending TRUE in DefWindowProc in this message we always maintain an active window
		case WM_NCACTIVATE:
			return DefWindowProc(hWnd, iMsg, TRUE, lParam); 
		// This message is sent when the window is destroyed
		case WM_DESTROY:									
			PostQuitMessage(0);							
			break;				
	}												

	// Process/handle the default messages
	return DefWindowProc (hWnd, iMsg, wParam, lParam);		
															
}

void Init(HWND hWnd)
{
	// Set our global window handle to our main window
	g_hWnd = hWnd;

	// Create our double buffering for each window
	g_Buffer.CreateDoubleBuffering(hWnd);
	g_ToolBuffer.CreateDoubleBuffering(g_hWndTool);

	// Set the map to default
    g_Map.SetDefault();

	// Load the tiles into the global lists and then get the scroll bar max scroll position
	g_ScrollInfo.nMax = LoadTiles();
	
	
	// By default set the current tile type to map tiles
	g_Map.SetCurrentType(TILE_TYPE);

	// Set our current map to ....  our current map :)
	g_pCurrentMap = &g_Map;

	// Initialize the scroll bar information
	g_ScrollInfo.cbSize = sizeof(SCROLLINFO);
	g_ScrollInfo.nMin = 0; 
	g_ScrollInfo.nPage = 1; 
	g_ScrollInfo.fMask = SIF_PAGE | SIF_RANGE;
	SetScrollInfo(g_hWndTool, SB_VERT, &g_ScrollInfo, FALSE);
	
	g_mapscrollx.cbSize = sizeof(SCROLLINFO);
	g_mapscrollx.nMin = 0;
    g_mapscrollx.nMax = WIDTH;
    g_mapscrollx.nPage = 1;
    g_mapscrollx.fMask = SIF_PAGE | SIF_RANGE;
    SetScrollInfo(g_hWnd,SB_HORZ,&g_mapscrollx,false);
    
    g_mapscrolly.cbSize = sizeof(SCROLLINFO);
    g_mapscrolly.nMin = 0;
    g_mapscrolly.nMax = HEIGHT;
    g_mapscrolly.nPage = 1;
    g_mapscrolly.fMask = SIF_PAGE | SIF_RANGE;
    SetScrollInfo(g_hWnd, SB_VERT, &g_mapscrolly, false);
	
	// Here we initialize our open file information
	g_OpenInfo.lStructSize = sizeof(OPENFILENAME);				// Set the size of the structure
	g_OpenInfo.nMaxFile = MAX_PATH;								// Set the max characters for a file name
	g_OpenInfo.lpstrFile = g_szFileName;						// Give a string to store the file name
	g_OpenInfo.lpstrFilter = "Map Files (*.map)\0*.map";		// Only accept .map files to load
	g_OpenInfo.hwndOwner = g_hWndTool;							// Assign the window owner and give it desired flags
	g_OpenInfo.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
	
	g_Buffer.SetPen(PS_SOLID,2,RGB(255,0,0));
    		
	// Set the backbuffer to black first (This clears the backbuffer)
	g_Buffer.ClearScreen(BLACK_BRUSH);			
	g_ToolBuffer.ClearScreen(BLACK_BRUSH);			
}

main.h

#ifndef _MAIN_H
#define _MAIN_H

#include <vector>
#include <iostream>
#include <Windows.h>
#include <string>
#include "bitmap.h"
#include "resource.h"
using namespace std;

#include "tile.h"

#define SCREEN_WIDTH    640
#define SCREEN_HEIGHT   500

#define SCROLL_BAR_OFFSET 16
#define MENU_OFFSET       16
#define SCROLLOFFSET      25

extern int g_mapsx;
extern int g_mapsy;
extern HWND g_hWnd;
extern HWND g_hWndTool;
extern HINSTANCE g_hInstance;

extern CBuffer g_Buffer;
extern CBuffer g_ToolBuffer;

extern CMap g_Map;
extern CMap *g_pCurrentMap;

extern vector<CTile> g_vTiles;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);	
LRESULT CALLBACK WndProcPopup(HWND, UINT, WPARAM, LPARAM);
void Init(HWND hWnd);
WPARAM MainLoop();
void DeInit();

#endif

CTile.cpp

#include "main.h"
#include "tile.h"

//Tile class init
CTile::CTile():m_bmpTile(0),m_type(0)
{
	memset(m_szFile, 0, MAX_PATH);
}


//Map class init
CMap::CMap():m_currentTypeFlag(TILE_TYPE)
{
	memset(m_szMapName, 0, MAX_PATH);
}

//draws all the placed tiles
void CMap::Draw()
{
	// Here we set the portion to draw for our transparent tiles
	int x = 0, y = 0;
	
	// Go through and draw all the normal map tiles
	for(int i = 0; i < (int)m_vTiles.size(); i++)
	{
		x = m_vTiles.GetIndex().x * TILE_WIDTH + g_mapsx;
		y = m_vTiles.GetIndex().y * TILE_HEIGHT + g_mapsy;
		
		g_Buffer.DisplayBitmap(m_vTiles.GetBitmap(), x, y);
	}
}

//sets up a default map with just grass tiles
void CMap::SetDefault()
{
	char szFile[MAX_PATH] = {0};

	// Here we setup the file to load as the default texture (grass)
	sprintf(szFile, "%s\\%s", TILES_DIR, DEFAULT_TILE);

	// Load the default tile file
	HBITMAP bmpDefault = g_Buffer.LoadABitmap(szFile);
	CTile temp;

	// Use a temporary tile for the default tile (Set it's type and name)
	temp.SetFileName(szFile);
	temp.SetType(FLOOR);

	// Make sure we  and release all the data with our global lists
	m_vTiles.clear();

	// Reset the current map's name so we know for the Save As / Save menu items
	strcpy(m_szMapName, "");

	// Go through the whole map (make it like a 2D array) and assign the
	// map a tile for every spot (the default tile with grass).  Set it's index too.
	for(int y = 0; y < MAP_HEIGHT; y++)
	{
		for(int x = 0; x < MAP_WIDTH; x++)
		{
			// Set the current index for the map and add it to the tile list
			temp.SetIndex(x, y);
			m_vTiles.push_back(temp);
		}
	}

	// Go through all the tiles that were just assigned and load the default bitmap
	for(int i = 0; i < (int)m_vTiles.size(); i++)
		m_vTiles.SetBitmap( g_Buffer.LoadABitmap( m_vTiles.GetFileName() ) );
}

//returns the size of the current global vector
int CMap::GetCurrentTypeSize()
{
        return (int)m_vTiles.size();
}    

//This function returns a tile from the current global vector being used
CTile *CMap::GetCurrentTypeTile(int index)
{
        return &g_vTiles[index];
}

//This function returns a tile from the current <map> list being used
CTile *CMap::GetCurrentListTile(int index)
{
        return &m_vTiles[index];
}

//takes a filename and returns the type
char SetTileType(char *szFile)
{
	// See if the substring "*_FILE" is in the current file, then return type
	if(strstr(szFile, WALL_FILE))
		return WALL;
	else if(strstr(szFile, DOOR_FILE))
		return DOOR;
	else		
		return FLOOR;	// It must be a normal floor tile then
}

//This takes a path and loads the tiles from that path (i.e. items)
void LoadTileType(char *szDir, int type)
{
	WIN32_FIND_DATA findFileData;
	HANDLE hFind;
	char szFile[MAX_PATH] = {0};

	// We copy the file to a temp variable and add a * so that
	// FindFirstFile() knows to look in the desired directory at all files.
	strcpy(szFile, szDir);
	strcat(szFile, "*");

	// Find the first file in the directory (usually "." or "..")
	hFind = FindFirstFile(szFile, &findFileData);

	// Make sure we have a valid handle and a file was found
	if(hFind == INVALID_HANDLE_VALUE)
	{
		// Print an error message so we know we need to add files, then quit
		sprintf(szFile, "Unable to find tiles in %s", szDir);
		MessageBox(g_hWnd, szFile, "Error", MB_OK);
		PostQuitMessage(0);
	}
	else
	{
		// Continue to look in this directory until we run out of files
		while (FindNextFile(hFind, &findFileData) != 0) 
		{
			// Store the current file found with it's respective directory
			sprintf(szFile, "%s\\%s", szDir, findFileData.cFileName);

			// Check if it's a bmp file using a simple substring check
			if(strstr(findFileData.cFileName, ".bmp"))
			{				
					// Initialize a temp tile and add it to our global list
					CTile temp;
					temp.SetFileName(szFile);
					temp.SetType( SetTileType( temp.GetFileName() ) );
					g_vTiles.push_back(temp);
				
			}
		}

		// Destroy the handle and close the files
		FindClose(hFind);
	}
}

//This function loads all the tiles information and bitmaps
int LoadTiles()
{
	// Here we load all the tile types from their respective directories
	LoadTileType(TILES_DIR, TILE_TYPE);

	// Here we go through all of the global tiles and load their bitmaps
	for(int i = 0; i < (int)g_vTiles.size(); i++)
		g_vTiles.SetBitmap(g_Buffer.LoadABitmap(g_vTiles.GetFileName()));

	// We return the tile's size because that is our default type.
	// We also subtract the MAP_HEIGHT - 1 to offset the scroll bar max position.
	return (int)g_vTiles.size() - MAP_HEIGHT - 1;
}


void CMap::InsertTile(CTile *pTile, int x, int y)
{
	char type = 'e';						// Set a type for exit since exit's don't have tiles

	// Since the CTile *pTile isn't always going to be a CTile, we need to
	// do some type casting depending on each type that we are inserting.
	// We first do a cast to the desired type, and then de-reference the tile
	// since the vector class member function push_back() does not take a pointer.

		type = pTile->GetType();

		// Since every spot on our map has a tile, we don't need to add a tile,
		// but replace the current tile in the spot we chose to draw on.
		// So, go through all of the tiles and check for the desired index and change the data.
		for(int i = 0; i < (int)m_vTiles.size(); i++)
		{
			// Get the current index of the tile we are checking
			POINT index = m_vTiles.GetIndex();

			// If this tile is under our cursor, we can then replace it's data
			if(index.x == x && index.y == y)
			{
				// Set the data of the tile on the map with the new inserted tile data
				m_vTiles = *pTile;
				m_vTiles.SetIndex(index.x, index.y);
				break;
			}
		}
}

void CMap::DeleteTile(int mapX, int mapY)
{
	// Since we only delete tiles of the current type selected, we need to do a 
	// check for the current type of tile, then delete the tile under the cursor.
	// We don't delete normal map tiles so we don't check for TILE_TYPE.
}

//This function returns a bitmap handle for a tile type when loading a map
HBITMAP CMap::FindHBitmap(char *szFile, int type)
{
	// So that we only load the bitmaps once and not duplicate any unnecessary
	// memory, we use this function to search the global tile lists and
	// gives us back an HBITMAP with the same file name.  We then share that
	// between the map tile lists and the tool bar tile lists.
	
	// Go through all of the tiles in the global tile list
	for(int i = 0; i < (int)g_vTiles.size(); i++)
	{
		// If the file name is the same then return it's bitmap handle
		if(strcmp(szFile, g_vTiles.GetFileName()) == 0)
			return g_vTiles.GetBitmap();
	}

	// Return default 0
	return 0;
}

//This function loads a map from a file
void CMap::Load(char *szFile)
{
	// Open our map that we want to load in "read" and "binary" mode
	FILE *fp = fopen(szFile, "rb");

	// In this function we want to load all of the data from a specified map.
	// We do this by first opening the file, then clearing our map tile lists.
	// We read the tile size for each list so we know how many tiles of that
	// type to read in.  Using our vector lists it's easy to allocate the 
	// memory we need for each list.  Lastly, we go through the global lists
	// and get a bitmap handle to each tile that we load.  This way we save
	// some memory and don’t need to worry about freeing more memory.
	// (* IMPORTANT *) It's probably common sense, but you have to make sure
	// you load the file the SAME exact order it was saved.  For instance, if
	// you load the map tiles first, you cant save the items first.  "Capish"?

	// If we couldn’t load the file report an error message
	if(!fp)
	{
		MessageBox(g_hWnd, "Can't Find File", "Error", MB_OK);
		return;
	}

	// Reset all of our map list data before we load more in
	m_vTiles.clear();

	int tileSize = 0, exitSize = 0;

	// Read in the sizes of each of the lists (the tile count of each list)
	fread(&tileSize, sizeof(int), 1, fp);

	// Depending on the list size, reallocate the memory for our map lists
	m_vTiles.resize(tileSize);

	// Go through all the lists in our map and load their tile information
	for(int i = 0; i < tileSize; i++)
		fread(&m_vTiles, sizeof(CTile), 1, fp);

	// Assign bitmap handles to each of the tiles
	for(int i = 0; i < tileSize; i++)
		m_vTiles.SetBitmap( FindHBitmap(m_vTiles.GetFileName(), TILE_TYPE) );

	// Close our file that we opened, a success!
	fclose(fp);

	// Let's create some string variables to store just the file name, not including the path
	char szTemp[255] = {0};
	char szFileName[255] = {0};
	LPSTR pFileName = szFileName;

	// Here we use a good win32 function to extract just the file name from the path.
	// The file name will be stored in pFileName.  This way it's not hard coded to a path.
	GetFullPathName(szFile, 255, (LPSTR)szTemp, &pFileName);

	// Set the name of this map
	strcpy(m_szMapName, pFileName);
}

//This function saves a map to a file
void CMap::Save(char *szFile)
{
	FILE *fp = NULL;

	// Just like the Load() function, to save the map we will be doing pretty 
	// much the same things, except instead of fread() we will use fwrite() to
	// write the information to disk for our .map file.

	// Check if the file doesn't have a .map extension.  If not, add it at no extra charge :)
	if(!strstr(szFile, MAP_EXT))
		strcat(szFile, MAP_EXT);

	// Open our file to "write" and in "binary".
	fp = fopen(szFile, "wb");

	// If we couldn’t create or open the file report an error
	if(!fp)
	{
		MessageBox(g_hWnd, "Can't Write File", "Error", MB_OK);
		return;
	}

	// Store all the sizes for each of the map's lists
	int tileSize		= (int)m_vTiles.size();

	// Save the sizes for each of the map lists
	fwrite(&tileSize, sizeof(int), 1, fp);

	// Go through all the lists and save each tile to disk
	for(int i = 0; i < tileSize; i++)
		fwrite(&m_vTiles, sizeof(CTile), 1, fp);

	// Close the file, the file is saved!
	fclose(fp);

	// Set the file name for this map that we just saved
	strcpy(m_szMapName, szFile);
}

CTile.h

#ifndef _TILE_H
#define _TILE_H

#include "main.h"

#define MAP_WIDTH 10
#define MAP_HEIGHT 10

#define TILE_WIDTH 32
#define TILE_HEIGHT 32

#define MAX_NAME_LEN		50

#define HEIGHT				(TILE_HEIGHT * MAP_HEIGHT)
#define WIDTH				(TILE_WIDTH * MAP_WIDTH)	

#define TILES_DIR			"Tiles\\"

#define DEFAULT_TILE		"grass_f.bmp"

#define MAP_EXT				".map"

// The normal map tiles have these written in the file name
#define FLOOR_FILE			"_f"
#define WALL_FILE			"_w"
#define DOOR_FILE			"_d"

// The defines are stored in the CTile: m_type variable
#define FLOOR				'f'
#define WALL				'w'
#define DOOR				'd'
#define EXIT				'e'

#define TILE_TYPE			0
#define EXIT_TYPE			4


// Our <Base Class> for each tile type.  
// This is then inherited from to create other tile types.
class CTile
{
public:

	CTile();	// The constructor (initializing function)

	// Our standard "Set" functions for our protected data
	void SetBitmap(HBITMAP bmpNew)	{ m_bmpTile = bmpNew;			 }
	void SetFileName(char *szNew)	{ strcpy(m_szFile, szNew);		 }
	void SetType(char newType)		{ m_type = newType;				 } 
	void SetIndex(int x, int y)		{ m_index.x = x;  m_index.y = y; }

	// Our standard "Get" functions for the protected data
	HBITMAP GetBitmap()		{ return m_bmpTile; }
	char *GetFileName()		{ return m_szFile;  }
	char GetType()			{ return m_type;	}
	POINT GetIndex()		{ return m_index;   }

// Set it protected so that inherited classes can access if necessary
private:	

	HBITMAP m_bmpTile;				// The handle to the tile's bitmap
	char m_szFile[MAX_PATH];		// The file name of the bitmap
	char m_type;					// The type of the tile
	POINT m_index;					// The index on the map of the tile
};

// This class handles most of our map functionality
class CMap
{
public:

	CMap();			// The constructor

	// This resets the map to green grass
	void SetDefault();

	// This draws our map
	void Draw();

	// These set and get the map name
	void SetMapName(char *szName)	{ strcpy(m_szMapName, szName); }
	char *GetMapName()				{ return m_szMapName;			 }

	// These set and get the current tile type being used
	void SetCurrentType(int type)	{ m_currentTypeFlag = type;    }
	int GetCurrentType()			{ return m_currentTypeFlag;	 }
	
	// These get the current global list's size and specific tile
	int GetCurrentTypeSize();
	CTile *GetCurrentTypeTile(int index);

	// These get the current map list's size and specific tile
	int GetCurrentListSize();
	CTile *GetCurrentListTile(int index);
	
	void InsertTile(CTile *pTile, int x, int y);
	void DeleteTile(int mapX, int mapY);
	
	// This returns a handle to a bitmap from the global lists for the same file name
	HBITMAP FindHBitmap(char *szFile, int type);

	// This loads a file
	void Load(char *szFile);

	// This saves a file
	void Save(char *szFile);
	
	
private:
    
	char m_szMapName[MAX_PATH];		// The name of the map
	int m_currentTypeFlag;			// The flag that determines the list type in the tool bar

	vector<CTile> m_vTiles;			// The normal map tiles list on our map
};

// This loads the tiles for the global tile lists and returns the size for the scroll bar
int LoadTiles();

#endif

errors Access Violation occured, the debug came up with this: HBITMAP GetBitmap() { return m_bmpTile; } If anyone could help me out with finding whats wrong, it would be really appreciated. Thanx.
You can leave and beat the traffic or you can stay and beat your meat.Blink 182
Kylotan
Kylotan
What do you mean, the debug came up with that? If you have access to a debugger, why not look at the call stack or the local variables to get an idea of what's going wrong? Posting 7 files for us to look through is a bit lazy really. Narrow down the problem first.
darookie
darookie
I smell an invalid index into some vector/array thus trying to access an invalid memory location that is supposed to hold an instance of the class that has the GetBitmap() member (e.g. GetBitmap fails because the this pointer is broken).
Dransic
Dransic
lol i guess posting so many fails is a bit lazy :P ill do what you 2 said, thanx.
You can leave and beat the traffic or you can stay and beat your meat.Blink 182

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.