Simppeli retrotyylinen matopeli. Toimii esimerkkinä WinAPI:n ja GDI:n käytöstä PL/I:n kanssa touhutessa.
valmis binääri -ja lähdekoodipaketti löytyypi tällä hetkellä: pli_snake.zip
*PROCESS MARGINS(1,160) LANGLVL(SAA2) pp(macro);
*PROCESS LIMITS(EXTNAME(100) fixedbin(63) fixeddec(31) name(100) );
*PROCESS NOT('ª^') DFT(BYVALUE);
*PROCESS INCLUDE (EXT('CPY','INC'));
Snake: package;
/* Include win32 stuff */
%include winbase;
%include wingdi;
%include winuser;
%include commctrl;
%include snake; /* application include file */
dcl MAP_SIZE type INT value (24);
dcl TILE_SIZE type INT value (16);
dcl CRASH_ADD type INT value (4);
dcl ID_TIMER type INT value (1);
dcl TIMER_DELAY type INT value (180);
dcl (addr, binvalue, mod, iand, inot, ior, length, null, sysnull, size, time, random) builtin;
/* Prototypes and constants*/
dcl 1 point_type based,
2 x type INT,
2 y type INT;
dcl 1 ZERO,
2 x type INT value (0),
2 y type INT value (0);
dcl 1 LEFT,
2 x type INT value (-1),
2 y type INT value (0);
dcl 1 RIGHT,
2 x type INT value (1),
2 y type INT value (0);
dcl 1 UP,
2 x type INT value (0),
2 y type INT value (-1);
dcl 1 DOWN,
2 x type INT value (0),
2 y type INT value (1);
define ordinal MAP_ID (EMPTY, SNAKE, FOOD, CRASH);
dcl IDC_MAIN_STATUS type INT value (103);
/******************************************************************************************************/
/* global variables */
dcl g_hbmBuffer type HBITMAP init (sysnull());
dcl g_hGreenPen type HPEN;
dcl g_hRedPen type HPEN;
dcl g_hYellowPen type HPEN;
dcl g_hGreenBrush type HBRUSH;
dcl g_hRedBrush type HBRUSH;
dcl g_hYellowBrush type HBRUSH;
dcl g_hFont type HFONT;
dcl g_map (0:MAP_SIZE - 1, 0:MAP_SIZE - 1) type INT;
dcl g_snake (0:((MAP_SIZE - 1)*(MAP_SIZE - 1))) like point_type;
dcl g_len_snake type INT;
dcl g_dir like point_type;
dcl g_ndir like point_type;
dcl g_food like point_type;
dcl g_crash like point_type;
dcl g_leave_it type BOOL;
dcl g_crashed type BOOL;
dcl g_szScore char (12) varz init (' SCORE: ');
dcl g_score pic '9999' init ('0000');
dcl wndclass type WNDCLASSEX;
dcl hInstance type HINSTANCE;
/******************************************************************************************************/
/* main procedure */
WinMain: proc (hInstance, hPrevInstance, szCmdLine, iCmdShow) returns(type INT) options (winmain);
dcl hInstance type HINSTANCE;
dcl hPrevInstance type HINSTANCE;
dcl szCmdLine ptr;
dcl iCmdShow type INT;
/* local variables */
dcl hwnd type HWND;
dcl msg type MSG;
dcl szAppName char (50) varz init ('Snake');
dcl szAppTitle char (50) varz init ('Simple Snake Game in Windows PL/I!');
/* initialize */
wndclass.cbSize = size(wndclass);
wndclass.style = ior(CS_HREDRAW, CS_VREDRAW);
wndclass.lpfnWndProc = WinProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hCursor = LoadCursor(sysnull(), pli_b2z(binvalue(IDC_ARROW)));
wndclass.hIcon = LoadIcon(hInstance, pli_b2z(IDI_BALL));
wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = sysnull();
wndclass.lpszClassName = addr(szAppName);
wndclass.hIconSm = LoadIcon(hInstance, pli_b2z(IDI_SBALL));
/* register class */
call RegisterClassEx (wndclass);
/* Create a window */
hwnd = CreateWindow(szAppName, /* window class name */
szAppTitle, /* window caption */
ior(WS_OVERLAPPED, WS_SYSMENU), /* window style */
CW_USEDEFAULT, /* x pos */
CW_USEDEFAULT, /* y pos */
0, /* x size */
0, /* y size */
sysnull(), /* parent window hand*/
sysnull(), /* window menu hand */
hInstance, /* pgm instance hand */
sysnull() ); /* creation params */
/* Show the window */
call ShowWindow(hwnd, iCmdShow) ;
call UpdateWindow(hwnd);
/* Message Loop */
do while (GetMessage(msg, sysnull(), 0, 0) ^= 0);
call TranslateMessage(msg);
call DispatchMessage(msg);
end; /* of do */
return (msg.wParam);
end WinMain; /* of program */
/******************************************************************************************************/
/* Window procedure */
WinProc: proc (hwnd, msg, mp1, mp2) options(byvalue, linkage (stdcall)) returns (type LRESULT);
dcl hwnd type HWND;
dcl msg type UINT;
dcl mp1 type WPARAM;
dcl mp2 type LPARAM;
/* local variables */
dcl hdc type HDC;
dcl hStatus type HWND;
dcl ps type PAINTSTRUCT;
dcl (rectl, rcStatus) type RECT;
dcl lfHeight type INT;
dcl iStatusHeight type INT;
dcl szScore char (20) varz;
select (msg);
when (WM_CREATE)
do;
/* Create statusbar for score info */
hStatus = CreateWindowEx(0, STATUSCLASSNAME, '',
ior(WS_CHILD, WS_VISIBLE),
0, 0, 0, 0, hwnd,
cast(:HMENU, IDC_MAIN_STATUS:), hInstance, sysnull());
call GetWindowRect(hStatus, rcStatus);
iStatusHeight = rcStatus.bottom - rcStatus.top;
call ResizeClient(hwnd, MAP_SIZE * TILE_SIZE, MAP_SIZE * TILE_SIZE + iStatusHeight);
call SendMessage(hStatus, WM_SIZE, 0, 0);
szScore = g_szScore || g_score;
call SetWindowText(hStatus, szScore);
/* Init pens and brushes */
g_hGreenPen = CreatePen(PS_SOLID, 1, RGB(0,255,0));
g_hGreenBrush = CreateSolidBrush(RGB(0,255,0));
g_hRedPen = CreatePen(PS_SOLID, 1, RGB(255,0,0));
g_hRedBrush = CreateSolidBrush(RGB(255,0,0));
g_hYellowPen = CreatePen(PS_SOLID, 1, RGB(255,255,0));
g_hYellowBrush = CreateSolidBrush(RGB(255,255,0));
/* Init game over font */
hdc = GetDC(sysnull());
lfHeight = -MulDiv(32, GetDeviceCaps(hdc, LOGPIXELSY), 72);
call ReleaseDC(sysnull(), hdc);
g_hFont = CreateFont(lfHeight, 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, 0, 0, 'Times New Roman');
/* Init and start the game */
call InitSnake;
call SetTimer(hwnd, ID_TIMER, TIMER_DELAY);
end; /* of when */
when (WM_KEYDOWN)
select (mp1);
when (VK_LEFT)
g_ndir = (LEFT);
when (VK_RIGHT)
g_ndir = (RIGHT);
when (VK_UP)
g_ndir = (UP);
when (VK_DOWN)
g_ndir = (DOWN);
when (VK_SPACE)
if g_crashed = TRUE then
do;
call InitSnake;
call SetTimer(hwnd, ID_TIMER, TIMER_DELAY);
end;
end; /* of select (mp1) */
when (WM_TIMER)
do;
/* Update game and force redraw */
call UpdateSnake;
call GetClientRect(hwnd, rectl);
hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);
call GetWindowRect(hStatus, rcStatus);
iStatusHeight = rcStatus.bottom - rcStatus.top;
rectl.bottom = rectl.bottom - iStatusHeight;
call InvalidateRect(hwnd, rectl, FALSE);
szScore = g_szScore || g_score;
call SetWindowText(hStatus, szScore);
if g_crashed = TRUE then call KillTimer(hwnd, ID_TIMER);
end; /* of when */
when (WM_PAINT)
do;
hdc = BeginPaint(hwnd, ps);
call GetClientRect(hwnd, rectl);
hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);
call GetWindowRect(hStatus, rcStatus);
iStatusHeight = rcStatus.bottom - rcStatus.top;
rectl.bottom = rectl.bottom - iStatusHeight;
if g_hbmBuffer = sysnull() then
g_hbmBuffer = CreateCompatibleBitmap(hdc, rectl.right, rectl.bottom);
call DrawSnake(hdc, rectl, g_hbmBuffer);
call EndPaint(hwnd, ps);
end; /* of when */
when (WM_DESTROY)
do;
/* Terminate the application */
call KillTimer(hwnd, ID_TIMER);
/* Delete used GDI objects */
call DeleteObject(g_hGreenPen);
call DeleteObject(g_hRedPen);
call DeleteObject(g_hYellowPen);
call DeleteObject(g_hGreenBrush);
call DeleteObject(g_hRedBrush);
call DeleteObject(g_hYellowBrush);
call DeleteObject(g_hFont);
call DeleteObject(g_hbmBuffer);
call PostQuitMessage(0);
end; /* of when */
otherwise
return (DefWindowProc(hwnd,msg,mp1,mp2));
end; /* of select (msg) */
return (0);
end WinProc; /* of procedure */
/******************************************************************************************************/
/* Resize client rectangle */
ResizeClient: proc (hwnd, nWidth, nHeight);
dcl hwnd type HWND;
dcl (nWidth, nHeight) type INT;
dcl (rcClient, rcWindow) type RECT;
dcl ptDiff type POINT;
call GetClientRect(hwnd, rcClient);
call GetWindowRect(hwnd, rcWindow);
ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
call MoveWindow(hwnd, rcWindow.left, rcWindow.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE);
end ResizeClient;
/******************************************************************************************************/
/* Init snake procedure */
InitSnake: proc;
dcl (i, j) type INT;
dcl start like point_type;
dcl seed float bin (53) static init (0);
if seed = 0 then seed = random(time());
g_score = '0000';
do i = 0 to MAP_SIZE - 1;
do j = 0 to MAP_SIZE - 1;
g_map (i, j) = binvalue(EMPTY);
end;
end;
g_len_snake = 3;
g_dir = (RIGHT);
g_ndir = g_dir;
start.x = (MAP_SIZE - 1) * random();
start.y = (MAP_SIZE - 1) * random();
do i = 0 to g_len_snake;
g_snake(i) = start;
end;
g_map(start.y, start.x) = binvalue(SNAKE);
g_crash = (ZERO);
g_leave_it = FALSE;
g_crashed = FALSE;
g_food.x = (MAP_SIZE - 1) * random();
g_food.y = (MAP_SIZE - 1) * random();
g_map(g_food.y, g_food.x) = binvalue(FOOD);
end InitSnake;
/******************************************************************************************************/
/* Update snake procedure */
UpdateSnake: proc;
dcl i type INT;
dcl np like point_type;
if (g_dir.x * (-1) ^= g_ndir.x) & (g_dir.y * (-1) ^= g_ndir.y) then g_dir = g_ndir;
np = g_snake(g_len_snake) + g_dir;
np = mod(np + MAP_SIZE, MAP_SIZE);
if g_map(np.y, np.x) = binvalue(SNAKE) then
do;
g_crashed = TRUE;
g_map(np.y, np.x) = binvalue(CRASH);
g_crash = np;
g_leave_it = TRUE;
end;
else if g_map(np.y, np.x) = binvalue(FOOD) then
do;
do loop;
g_food.x = (MAP_SIZE - 1) * random();
g_food.y = (MAP_SIZE - 1) * random();
if g_map(g_food.y, g_food.x) = binvalue(EMPTY) then leave;
end;
g_map(g_food.y, g_food.x) = binvalue(FOOD);
g_score += 1;
g_leave_it = TRUE;
end;
if g_leave_it = TRUE then
do;
g_len_snake += 1;
g_leave_it = FALSE;
end;
else do;
g_map(g_snake(0).y, g_snake(0).x) = binvalue(EMPTY);
do i = 0 to g_len_snake - 1;
g_snake(i) = g_snake(i+1);
end;
end;
g_snake(g_len_snake) = np;
g_map(np.y, np.x) = binvalue(SNAKE);
end UpdateSnake;
/******************************************************************************************************/
/* Draw snake procedure */
DrawSnake: proc (hdc, prc, hbuffer);
dcl hdc type HDC;
dcl prc type RECT;
dcl hbuffer type HBITMAP;
/* static variables */
dcl szGameOver char (12) varz static init ('GAME OVER');
/* local variables */
dcl hdcBuffer type HDC;
dcl hbmOldBuffer type HBITMAP;
dcl hOldPen type HPEN;
dcl hOldBrush type HBRUSH;
dcl hOldFont type HFONT;
dcl i type INT;
hdcBuffer = CreateCompatibleDC(hdc);
hbmOldBuffer = SelectObject(hdcBuffer, g_hbmBuffer);
/* Clear game screen */
call FillRect(hdcBuffer, prc, GetStockObject(WHITE_BRUSH));
/* Draw snake */
hOldPen = SelectObject(hdcBuffer, g_hGreenPen);
hOldBrush = SelectObject(hdcBuffer, g_hGreenBrush);
do i = 0 to g_len_snake;
call Rectangle(hdcBuffer, g_snake(i).x * TILE_SIZE, g_snake(i).y * TILE_SIZE,
g_snake(i).x * TILE_SIZE + TILE_SIZE, g_snake(i).y * TILE_SIZE + TILE_SIZE);
end;
/* Draw food */
call SelectObject(hdcBuffer, g_hRedPen);
call SelectObject(hdcBuffer, g_hRedBrush);
call Ellipse(hdcBuffer, g_food.x * TILE_SIZE, g_food.y * TILE_SIZE,
g_food.x * TILE_SIZE + TILE_SIZE, g_food.y * TILE_SIZE + TILE_SIZE);
/* Draw crash image and GAME OVER text if snake crashed */
if g_crashed = TRUE then
do;
call SelectObject(hdcBuffer, g_hYellowPen);
call SelectObject(hdcBuffer, g_hYellowBrush);
call Ellipse(hdcBuffer, g_crash.x * TILE_SIZE - CRASH_ADD, g_crash.y * TILE_SIZE - CRASH_ADD,
g_crash.x * TILE_SIZE + TILE_SIZE + CRASH_ADD, g_crash.y * TILE_SIZE + TILE_SIZE + CRASH_ADD);
hOldFont = SelectObject(hdcBuffer, g_hFont);
call SetBkMode(hdcBuffer, TRANSPARENT);
call DrawText(hdcBuffer, szGameOver, -1, prc, ior(DT_SINGLELINE, DT_CENTER, DT_VCENTER));
call SelectObject(hdcBuffer, hOldFont);
end;
/* Blit memory buffer to screen */
call BitBlt(hdc, 0, 0, prc.right, prc.bottom, hdcBuffer, 0, 0, SRCCOPY);
/* Clean up and restore default GDI objects */
call SelectObject(hdcBuffer, hOldPen);
call SelectObject(hdcBuffer, hOldBrush);
call SelectObject(hdcBuffer, hbmOldBuffer);
call DeleteDC(hdcBuffer);
end DrawSnake;
/******************************************************************************************************/
end Snake; /* of package */Aihe on jo aika vanha, joten et voi enää vastata siihen.