#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);
}