/*******************************************************************************
Running Man Asm originally written by Nick Kouvaris "retromaniax FTL"
(http://lightforce.freestuff.gr/),
C port of Running Man, by xdir (directx@freemail.gr)
v2 - Adds a road under the man, spiked mountains on the background and a
3D Horizon effect.
v3 - Rewritten in CodeGear Turbo C++ as a standard Windows application
(no console interface).
v4 - Refined (_Make3D) horizon.
.1- Refined WindowProcedure
Proper RNG initilization
.2- Timing logic isolated from paint logic (eliminating out-of-sync WM_PAINT bugs)
Tested under Windows XP (SP3) - 1024x768.
*******************************************************************************/
#include
#include /* v4.1 */
#pragma hdrstop
#define _FRAME 7
#define _SPEED 60
#define _MAX_3D_LINE 10
//---------------------------------------------------------------------------
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
BOOL _DrawRoad(HWND, int nY, RECT*);
BOOL _DrawMountain(HWND, POINT[], int nMax, RECT*);
BOOL _DrawMan(HWND, HICON[], int nX, int nY, int nFrame, RECT*);
BOOL _ClearRect(HWND, RECT*);
void _LoadMan(HICON icoArray[], int nMax = _FRAME);
void _MakeMountain(POINT point[], int nMaxElement, int nSpace = 10,
int nTopline = 70, int nBaseline = 30);
BOOL _Make3D(HWND hWnd, POINT[], RECT *rcClient, int nOffset, BOOL bInit = FALSE);
void _Update3D(POINT[], int nOffset, RECT*);
#pragma argsused
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wcOwner;
HWND hWnd;
try
{
try
{
// Create Window Class ...
ZeroMemory(&wcOwner, sizeof(wcOwner));
wcOwner.style = CS_HREDRAW|CS_VREDRAW;
wcOwner.lpfnWndProc = (WNDPROC)WindowProcedure;
wcOwner.hInstance = hInstance;
wcOwner.hbrBackground = GetStockObject(BLACK_BRUSH);
wcOwner.lpszClassName = "RunManV4";
wcOwner.hCursor = LoadCursor(NULL, IDC_ARROW);
if(!RegisterClass(&wcOwner))
throw "Window Error: Cannot "Register Class"";
// Create Window ...
hWnd = CreateWindow(wcOwner.lpszClassName,
"Running Man v4",
WS_MINIMIZEBOX|WS_SYSMENU|WS_VISIBLE|WS_OVERLAPPED,
CW_USEDEFAULT, CW_USEDEFAULT,
350, 350,
NULL, NULL,
hInstance,
NULL);
if(hWnd == NULL)
throw "Window Error: Cannot "Create Window"";
// Kickstart Window (do not use ShowWindow -- ignore nCmdShow) ...
UpdateWindow(hWnd);
int nSuccess;
MSG msgData;
// Set-up primary message loop ...
while((nSuccess = GetMessage(&msgData, hWnd, 0, 0L))!=0)
{
// Microsoft suggestion, since GetMessage can return -1
if(nSuccess == -1)
break;
TranslateMessage(&msgData);
DispatchMessage(&msgData);
}
}
catch(char *pszError)
{
// Error!
KillTimer(hWnd, WM_USER+5);
MessageBeep(MB_ICONSTOP);
MessageBox(NULL, pszError, NULL, MB_ICONSTOP);
}
}
__finally
{
// Application clean-up ...
UnregisterClass(wcOwner.lpszClassName, hInstance);
}
return 0;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT nMessage,
WPARAM wParam, LPARAM lParam)
{
// Main window procedure
static RECT rcClient, rcClear;
static POINT ptMountain[100];
static HICON hIcon[7];
static PAINTSTRUCT PS;
static int nMan_X = -32,
nMan_Frame = 0;
static POINT pt3D_Lines[10]; /* v4.2 */
static BOOL bMountainUpd = FALSE;
switch(nMessage)
{
case WM_CREATE:
// Load sprite ...
_LoadMan(hIcon);
// v4.1) Randomize pseudo-RNG
srand(time(NULL));
// Initialize Mountain points ...
_MakeMountain(ptMountain, 100);
// Get Client dimensions
GetClientRect(hWnd, &rcClient);
// Make initial 3D horizon
_Make3D(hWnd, pt3D_Lines, &rcClient, 201, TRUE);
// Setup our live-beat timer
SetTimer(hWnd, WM_USER+5,_SPEED, NULL);
return FALSE;
case WM_TIMER:
switch(wParam)
{
case WM_USER+5:
// v4.2) Update man position ...
if(nMan_X++ >= rcClient.right + 32)
{
// v1) Flip man to left
nMan_X = -32;
// v4.2) Issue a mountain background update
bMountainUpd = TRUE;
}
// v4.2) Update man frame ...
if(nMan_Frame++ >= _FRAME - 1)
nMan_Frame = 0;
// v4.2) Update horizon wireframe ...
_Update3D(pt3D_Lines, 205, &rcClient);
// Force a non destructive DC invalidation
InvalidateRect(hWnd, NULL, FALSE);
return FALSE;
}
return FALSE;
case WM_DESTROY:
// Shutdown live-beat timer
KillTimer(hWnd, WM_USER+5);
// Quit!
PostQuitMessage(0L);
return FALSE;
case WM_PAINT:
// Signal DC for paint
if(BeginPaint(hWnd, &PS) != NULL)
{
// Get Client dimensions
GetClientRect(hWnd, &rcClient);
// v2) Draw mountains
_DrawMountain(hWnd, ptMountain, 100, &rcClient);
// v2) Draw a road */
_DrawRoad(hWnd, 209, &rcClient);
// v1) Draw man on road
_DrawMan(hWnd, hIcon, nMan_X, 209 - 32, nMan_Frame, &rcClient);
// v1) Move man along the road
//if(nMan_X > rcClient.right + 32)
if(bMountainUpd)
{
// Shutdown current update
bMountainUpd = FALSE;
// v2) Clear previous mountain
rcClear.left = rcClear.top = 0;
rcClear.right= rcClient.right;
rcClear.bottom=100;
_ClearRect(hWnd, &rcClear);
// v2) Generate a new mountain background
_MakeMountain(ptMountain, 100);
}
// v2) Clear previous horizon
rcClear.left = 0;
rcClear.top = 210;
rcClear.right= rcClient.right;
rcClear.bottom=rcClient.bottom;
_ClearRect(hWnd, &rcClear);
_Make3D(hWnd, pt3D_Lines, &rcClient, 210);
// Signal DC off paint
EndPaint(hWnd, &PS);
}
return FALSE; /* v4.1) big-fix */
}
return DefWindowProc(hWnd, nMessage, wParam, lParam);
}
BOOL _DrawRoad(HWND hWnd, int nY, RECT *prcClient)
{
static HDC hDC;
// Get DC for draw ...
if((hDC=GetDC(hWnd))!=NULL)
{
// Set Road color to Red
SelectObject(hDC, GetStockObject(WHITE_PEN));
// Draw Road ...
MoveToEx(hDC, 0, nY, NULL);
LineTo(hDC, prcClient->right, nY);
// Release DC
ReleaseDC(hWnd, hDC);
// No error!
return TRUE;
}
return FALSE;
}
void _MakeMountain(POINT point[], int nMaxElement, int nSpace,
int nTopline, int nBaseline)
{
/* Generate a extra-large enough mountain like polyline point array */
int nPointCount = 0;
for(; nPointCount < nMaxElement; nPointCount++)
{
point[nPointCount].x = nPointCount*nSpace;
point[nPointCount].y = (rand() % nBaseline) + nTopline;
}
}
BOOL _DrawMountain(HWND hWnd, POINT point[], int nMax, RECT *prcClient)
{
static HDC hDC;
// Get DC for draw ...
if((hDC=GetDC(hWnd))!=NULL)
{
// Create a thick Green pen ...
LOGPEN penGray;
penGray.lopnStyle = PS_SOLID;
penGray.lopnWidth.x = 2;
penGray.lopnColor = RGB(0, 255, 0);
HPEN hOldPen = NULL,
hPen = CreatePenIndirect(&penGray);
// Set Mountain color to Green
hOldPen = SelectObject(hDC, hPen);
// Draw Mountains ...
Polyline(hDC, point, nMax);
// Restore previous pen ...
SelectObject(hDC, hOldPen);
// Delete Green pen ...
DeleteObject(hPen);
// Release DC
ReleaseDC(hWnd, hDC);
// No error!
return TRUE;
}
return FALSE;
}
void _LoadMan(HICON icoArray[], int nMax)
{
/* Read Man icons directly from disk (no PE resource) */
int nIconCount = 0;
for(; nIconCount < nMax; nIconCount++)
{
static char szFileName[128];
wsprintf(szFileName, "IMG%d.ICO", nIconCount + 1);
if((icoArray[nIconCount] = LoadImage(0, szFileName, IMAGE_ICON, 32, 32,
LR_DEFAULTCOLOR|LR_LOADFROMFILE )) == NULL)
{
wsprintf(szFileName,"DOS Error: Cannot load file - "IMG%d.ICO"",
nIconCount + 1);
throw szFileName;
}
}
}
BOOL _DrawMan(HWND hWnd, HICON icoArray[], int nX, int nY, int nFrame, RECT *prcClient)
{
static HDC hDC;
// Get DC for draw ...
if((hDC=GetDC(hWnd))!=NULL)
{
// Set Mountain color to White
SelectObject(hDC, GetStockObject(WHITE_PEN));
// Draw Man ...
DrawIcon(hDC, nX, nY, icoArray[nFrame]);
// Release DC
ReleaseDC(hWnd, hDC);
// No error!
return TRUE;
}
return FALSE;
}
BOOL _ClearRect(HWND hWnd, RECT *prcClient)
{
static HDC hDC;
// Get DC for draw ...
if((hDC=GetDC(hWnd))!=NULL)
{
// Paint prcClient black ...
FillRect(hDC, prcClient, GetStockObject(BLACK_BRUSH));
// Release DC
ReleaseDC(hWnd, hDC);
// No error!
return TRUE;
}
return FALSE;
}
BOOL _Make3D(HWND hWnd, POINT ptLine[], RECT *prcClient, int nOffset, BOOL bInit)
{
static HDC hDC;
static int nLineCount;
// Init
if(bInit)
{
for(nLineCount = 0; nLineCount < _MAX_3D_LINE; nLineCount++)
{
ptLine[nLineCount].x = prcClient->right;
ptLine[nLineCount].y = (nLineCount * 11) + nOffset;
}
return TRUE;
}
// Get DC for draw ...
if((hDC=GetDC(hWnd))!=NULL)
{
// v4) Define & set a clip region (used later by vertical Horizon)
HRGN rgnClip = CreateRectRgn(0, nOffset,
prcClient->right, prcClient->bottom);
SelectClipRgn(hDC, rgnClip);
// Create a thick Green pen ...
LOGPEN penGray;
penGray.lopnStyle = PS_SOLID;
penGray.lopnWidth.x = 1;
penGray.lopnColor = RGB(0, 0, 255);
HPEN hOldPen = NULL,
hPen = CreatePenIndirect(&penGray);
// Set Horizon color to Blue
hOldPen = SelectObject(hDC, hPen);
// Draw vertical Horizon
for(int n3D = 3000; n3D > -3000; n3D -= 150)
{
MoveToEx(hDC, 170, 195, NULL);
LineTo(hDC , n3D, prcClient->bottom);
}
// Draw horizontal Horizon
for(nLineCount = 0; nLineCount < 10; nLineCount++)
{
MoveToEx(hDC, 0 , ptLine[nLineCount].y, NULL);
LineTo(hDC, prcClient->right, ptLine[nLineCount].y);
}
// Restore previous pen ...
SelectObject(hDC, hOldPen);
// Delete Blue pen
DeleteObject(hPen);
// Release Region
SelectClipRgn(hDC, NULL);
DeleteObject(rgnClip);
// Release DC
ReleaseDC(hWnd, hDC);
// No error!
return TRUE;
}
return FALSE;
}
void _Update3D(POINT ptLine[], int nOffset, RECT *prcClient)
{
// v4.2) 3D Horizon Lines Roll/Update (as a separated function)
for(int nLineCount = 0; nLineCount < _MAX_3D_LINE; nLineCount++)
if(ptLine[nLineCount].y > prcClient->bottom-5)
ptLine[nLineCount].y = nOffset;
else
ptLine[nLineCount].y+=3;
}