#include <windows.h> #include <time.h> #include "Wad.h" #include "Render.h" // For keyboard handling (in moving player), these query the keyboard in real-time #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) // Information needed by the application itself. HWND ghWnd; // Handle to the main window. BOOL bIsActive; // Tells us when we should be rendering, and when we should stop. BOOL bRunGame; // Tells us wether we are running the game or showing the title screen. HINSTANCE hInst; // current application instance LPCTSTR lpszAppName = "WadReader"; // The application and window class name. LPCTSTR lpszTitle = "WadReader"; // The main window title. short ClientWidth; short ClientHeight; short ClientxPos; short ClientyPos; // Pointers to the WAD file and Rendering engine. GameWad* TheWadFile; Renderer* GameRenderer; short PlayerX; short PlayerY; short PlayerHeight; short ViewAngle; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // // This copies the double buffer to the visible window. It also performs the stretching // when the window is not the default size. // void blit( HBITMAP DblBuff ) { HDC hDC, hMemDC; HBITMAP DefaultBitmap; char szMessage[256]; hDC = GetDC(ghWnd); // Create memory device context compatible with the engines window dc hMemDC = CreateCompatibleDC(GetDC(ghWnd)); // Save the default bitmap and select the engines double buffer into the memory dc DefaultBitmap = SelectObject(hMemDC, DblBuff); // Copy double buffer to visible windows client area StretchBlt(hDC, 0, 0, ClientWidth, ClientHeight, hMemDC, 0, 0, 640, 400, SRCCOPY); // Draw at the top of the screen: <X.Y.Z> /* SetTextAlign( hDC, TA_CENTER | TA_TOP ); SetTextColor( hDC, RGB(255,255,255)); SetBkColor( hDC, RGB(0,0,0)); TextOut(hDC,ClientWidth/2,0,szMessage,sprintf(szMessage,"Player Coords: <%d,%d,%d> Ang: %d" ,PlayerX, PlayerY, PlayerHeight, (short)(ViewAngle/184.04444444))); */ // put back the default bitmap SelectObject(hMemDC, DefaultBitmap); // delete the memory dc DeleteDC(hMemDC); // delete the default bitmap DeleteObject(DefaultBitmap); // release the engines window dc ReleaseDC(ghWnd,hDC); } // // This is a delay that can be cancelled by pressing SPACE or ESCape. // It is used during the start-up screens. // void delay2(long time) // time is in seconds. { time_t start; start= clock(); while (KEY_DOWN(VK_ESCAPE) || KEY_DOWN(VK_SPACE)) // Wait for the keys to clear. ; while ((clock() - start) < time*CLK_TCK) // Start the delay { if (KEY_DOWN(VK_ESCAPE) || KEY_DOWN(VK_SPACE)) // if space or ESC, break out. break; } } // // This prints an error message, clears all the tables, and informs the // message loop that we need to exit the program. // int Die(char *string) { MessageBox(ghWnd, string, lpszTitle, MB_APPLMODAL | MB_ICONERROR | MB_OK ); SendMessage(ghWnd, WM_DESTROY, 0, 0); return -1; } // // This method performs the user movement and collision detection. // void ProccessUserMovement( void ) { short dx, dy; // Deltas to add to the position. short x, y; // The new positions. x = PlayerX; y = PlayerY; // Step 1. Check for movement. The lower nibble of KeyState is for movement ONLY. ( Arrow Keys ) if ( KEY_DOWN(VK_LEFT) || KEY_DOWN(VK_RIGHT) || KEY_DOWN(VK_UP) || KEY_DOWN(VK_DOWN) ) { // Reset dx and dy. dx=dy=0; // If the player is pressing the Right arrow, if (KEY_DOWN(VK_LEFT)) { // Decrement the view angle by 5 degrees, wrap at zero. ViewAngle += 910; } // Else, if the player is pressing the Left arrow. if (KEY_DOWN(VK_RIGHT)) { // Increment the view angle by 5 degrees, Wrap at 360. ViewAngle -= 910; } // Else, if the player wants to go FORWARD... if (KEY_DOWN(VK_UP)) { // Calculate the dx and dy to move forward, at the current angle. (Trig) dx = (short)((GameRenderer->cos(ViewAngle)*50l)>>16); dy = (short)((GameRenderer->sin(ViewAngle)*50l)>>16); } // Else, if the player wants to move BACKWARDS if (KEY_DOWN(VK_DOWN)) { // Calculate the dx and dy to move backward, at the current angle. (Trig) dx = -(short)((GameRenderer->cos(ViewAngle)*50l)>>16); dy = -(short)((GameRenderer->sin(ViewAngle)*50l)>>16); } // Now , add the deltas to the current location, x += dx; y += dy; // Step 4. Now, we do collision detection... (Bumping into walls..) // Check the blockmap for the linedefs in this block // See if we're crossing any. // Are they 1 sided or 2? // Are they passable or not? // If not, then don't allow movement // If they are, then Trigger any special effects. PlayerX = x; PlayerY = y; PlayerHeight = GameRenderer->changeViewPosn( PlayerX, PlayerY, ViewAngle ); } return; } // // This method sets up the rendering engine. // int StartEngine(void) { // // Load the WAD file. // TheWadFile = new GameWad(); TheWadFile->InitWadFile("C:\\temp\\Doom1.wad"); TheWadFile->LoadMap(1,1); TheWadFile->getPlayerStart(PlayerX, PlayerY, ViewAngle); ViewAngle = (short)(ViewAngle * 184.04444444); // Convert to BAM angle. //Initialize the Rendering engine. GameRenderer = new Renderer(ghWnd, TheWadFile); GameRenderer->changeViewPosn( PlayerX, PlayerY, ViewAngle ); return 1; } // // This method performs the main game loop. It calls a set of methods to do background processing, // then it performs the user movement, and then processing of triggers. Finally, it renders the view. // void MainLoop(void) { // Step 1. Process background things (doors, animations, lights, etc.) // Step 2. Allow the user to move ProccessUserMovement(); // Step 3. Render the new view. GameRenderer->render(PlayerX, PlayerY); blit(GameRenderer->getDIB()); return; } // // The main entry function. It creates the main widow, and calls the necessary functions to // initialize the engine, and then enters the main message loop. // int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HWND hWnd; WNDCLASSEX wc; bIsActive = FALSE; bRunGame = FALSE; // Register the main application window class. wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, lpszTitle); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = lpszAppName; wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInstance, lpszTitle, IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); if(!RegisterClassEx(&wc)) { return(FALSE); } hInst = hInstance; // Create the main application window. hWnd = CreateWindowEx(WS_OVERLAPPED, lpszAppName, lpszTitle, WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME, 50, 50, (640 + 6), (400 + 44), NULL, NULL, hInstance, NULL); if(!hWnd) { return(FALSE); } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); ghWnd = hWnd; StartEngine() ; bRunGame = TRUE; // We're going to try and use the windows timer to set the upper limit to 60 frames / sec. SetTimer(ghWnd, 1, 16, NULL); // enter main event loop while(1) { if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // test if this is a quit if(msg.message == WM_QUIT) { break; } // translate any accelerator keys TranslateMessage(&msg); // send the message to the window proc DispatchMessage(&msg); } } KillTimer(ghWnd, 1); return(msg.wParam); } // // The winproc for the main window handles messages from the menu bar, and from user input. // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; switch(message){ case WM_ACTIVATEAPP: bIsActive = (BOOL) wParam; break; case WM_KEYDOWN: // Check to see if the user wants to see the auto map. if (((int)wParam == VK_TAB ) && (bRunGame == TRUE )) { // Render Automap } // Check to see if the user wants to quit (ESC KEY). if((int)wParam == VK_ESCAPE) PostMessage(ghWnd,WM_CLOSE,0,0); // Check to se if the user is requesting a screen shot ( F1 Key ). if ((int)wParam == VK_F1) { GameRenderer->Save_Pcx("scrnsht.pcx"); } // Now check to see if the player wants to change the torch level ( F2 Key ). if (((int)wParam == VK_F2) && (bRunGame == TRUE )) { // Change the ambient light level. } // Check to see if the player wants to open a door ( SPACE key ). if (((int)wParam == VK_SPACE) && (bRunGame == TRUE )) { // This will activate a linedef just in front of us. } break; case WM_SIZE: ClientWidth = LOWORD(lParam); ClientHeight = HIWORD(lParam); break; case WM_MOVE : ClientxPos = (int) LOWORD(lParam); ClientyPos = (int) HIWORD(lParam); break; case WM_PAINT: BeginPaint(hWnd, &ps ); EndPaint(hWnd, &ps ); break; case WM_TIMER: if( bRunGame == TRUE ) { MainLoop(); // call main logic module. } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, message, wParam, lParam); }