
3. Приложения
Приложение
1
Инструкция для пользователя
Минимальные технические требования: процессор Pentium II, 16Мб оперативной памяти, OpenGL-совместимая видеокарта, 2Мб на жестком диске. В системе должен быть установлен шрифт Bookman Old Style.
Программа имитации бомбометания состоит из одного исполняемого файла (bomber.exe) и не требует установки. После запуска указанного файла на экране появляется окно настроек имитационной модели:
В списке «Размер окна вывода ролика» выбирается размер окна, в который будет выводиться имитационный ролик. Качество вывода ролика зависит от возможностей видеокарты. На слабых видеокартах при больших размерах окна может наблюдаться прерывистость вывода ролика. На современных машинах при любых размерах окна ролик будет выводиться плавно.
В списке «Количество сбрасываемых бомб» выбирается число сбрасываемых самолетом бомб. Их число варьируется от одной до четырех.
В
списке «Тип
бомбометания»
выбирается тип имитации: либо серийное
бомбометание по мосту с последовательным
углом захода
самолета
от 0 до 90 градусов, либо одиночное
бомбометание с углом захода, выбранном
в списке «Угол
захода самолета на мост».
После нажатия кнопки «Имитация» появится окно, в которое и будет выводиться ролик:
Помимо визуализации самого бомбометания в окно выводится угол захода самолета и вероятность разрушения моста.
После каждого захода самолета и визуализации падения бомб, на экран выводится окно результатов бомбардировки:
Окно содержит информацию об угле захода самолета, координат падения бомб, качестве бомбометания (отношение количества попавших бомб к общему их количеству) и схему падения бомб относительно моста.
Приложение
2
Блок-схема алгоритма
Ниже приведены блок-схемы алгоритма генерации случайных чисел, распределенных по нормальному закону распределения (слева) и блок-схема алгоритма имитации серийного бомбометания (справа):
Приложение
3
Листинг программы
program bomber;
{$r dialog.res}
{$r models.res}
{$r textures.res}
uses
bm_core in 'bm_core.pas',
bm_wind in 'bm_wind.pas',
bm_rend in 'bm_rend.pas',
bm_timer in 'bm_timer.pas',
bm_river in 'bm_river.pas',
bm_scene in 'bm_scene.pas',
bm_file in 'bm_file.pas',
bm_tga in 'bm_tga.pas',
bm_mem in 'bm_mem.pas',
bm_graph in 'bm_graph.pas',
bm_model in 'bm_model.pas',
bm_dlg in 'bm_dlg.pas',
bm_brid in 'bm_brid.pas',
bm_plane in 'bm_plane.pas',
bm_math in 'bm_math.pas',
bm_effct in 'bm_effct.pas',
bm_expl in 'bm_expl.pas';
begin
Randomize ();
Bomber_Simulate ();
end.
unit bm_core;
interface
const
FULL_SCREEN = FALSE;
DEBUG_MODE = FALSE;
DEBUG_FONT_FACE = 'Bookman Old Style';
DEBUG_FONT_SIZE = 11;
RENDER_FOV = 45.0;
RENDER_DISTANCE = 1000;
BRIDGE_LENGTH = 110;
BRIDGE_WIDTH = 10;
var
WINDOW_WIDTH: INTEGER = 640;
WINDOW_HEIGHT: INTEGER = 480;
DEBUG_FONT_HEIGHT: INTEGER = 13;
procedure Bomber_FatalError (Text: STRING);
function Bomber_Simulate (): BOOLEAN;
function Bomber_KeyDown (key: BYTE): BOOLEAN;
function IntToStr (l: LONGINT): STRING;
function FloatToStr (e: EXTENDED; precision, digits: INTEGER): STRING;
implementation
uses
Windows,
Messages,
OpenGL,
bm_dlg,
bm_timer,
bm_wind,
bm_rend,
bm_model,
bm_brid,
bm_plane,
bm_river,
bm_expl,
bm_scene,
bm_effct;
function CoreInitialize (): BOOLEAN;
begin
if (not Bomber_DialogExecute ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_TimerInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_WindowInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_RendInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_ModelInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_BridgeInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_PlaneInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_RiverInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_ExploInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_EffectInitialize ()) then
begin
result := FALSE;
Exit;
end;
if (not Bomber_SceneInitialize ()) then
begin
result := FALSE;
Exit;
end;
result := TRUE;
end;
procedure CoreRelease ();
begin
Bomber_SceneRelease
();
Bomber_RiverRelease ();
Bomber_BridgeRelease ();
Bomber_ModelRelease ();
Bomber_RendRelease ();
Bomber_WindowRelease ();
Bomber_TimerRelease ();
end;
procedure CoreRunLoop ();
var
msg: TMSG;
dc: HDC;
begin
dc := wglGetCurrentDC ();
Bomber_TimerStart ();
while (TRUE) do
begin
if (PeekMessage (msg, 0, 0, 0, PM_REMOVE)) then
if (msg.message <> WM_QUIT) then
begin
TranslateMessage (msg);
DispatchMessage (msg);
end
else
Break;
if (not Bomber_SceneDraw ()) then
Break;
glViewPort (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
Bomber_EffectDraw ();
if (DEBUG_MODE) then
Bomber_RendDrawText (0, 10, 'fps: ' + IntToStr (Round (Bomber_TimerGetFps ())));
Bomber_TimerWait ();
SwapBuffers (dc);
end;
end;
function Bomber_Simulate (): BOOLEAN;
begin
if (CoreInitialize ()) then
begin
CoreRunLoop ();
CoreRelease ();
result := TRUE;
end
else
begin
CoreRelease ();
result := FALSE;
end;
end;
function Bomber_KeyDown (key: BYTE): BOOLEAN;
begin
result := GetAsyncKeyState (key) and $8000 <> 0;
end;
procedure Bomber_FatalError (Text: STRING);
begin
MessageBox (0, PCHAR (Text), 'Ошибка', MB_OK or MB_ICONERROR or MB_TASKMODAL);
end;
function IntToStr (l: LONGINT): STRING;
var
s: STRING;
begin
Str (l, s);
result := s;
end;
function FloatToStr (e: EXTENDED; precision, digits: INTEGER): STRING;
var
s: STRING;
begin
Str (e : precision : digits, s);
result := s;
end;
end.
unit bm_wind;
interface
function Bomber_WindowInitialize (): BOOLEAN;
procedure Bomber_WindowRelease ();
implementation
uses
Windows,
Messages,
OpenGL,
bm_core;
const
CLASS_NAME = 'BOMBER_CLASS';
var
wndClass: TWNDCLASS;
handle: HWND;
dc: HDC;
glrc: HGLRC;
function GetMaxFreq (): DWORD;
var
devMode: TDEVMODE;
mode, freq: INTEGER;
begin
mode := 0;
freq := -1;
ZeroMemory (@devMode, sizeof (devMode));
devMode.dmSize := sizeof (devMode);
while (EnumDisplaySettings (nil, mode, devMode)) do
begin
if (devMode.dmBitsPerPel = 32)
and (INTEGER (devMode.dmPelsWidth) = WINDOW_WIDTH)
and (INTEGER (devMode.dmPelsHeight) = WINDOW_HEIGHT)
and (LONGINT (devMode.dmDisplayFrequency) > freq) then
freq := devMode.dmDisplayFrequency;
inc (mode);
end;
result := freq;
end;
function OnCreate (wnd: HWND): BOOLEAN;
var
pfd: PIXELFORMATDESCRIPTOR;
format: DWORD;
begin
ZeroMemory (@pfd, sizeof (pfd));
pfd.nSize := sizeof (pfd);
pfd.nVersion := 1;
pfd.dwFlags := PFD_DOUBLEBUFFER or PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW;
pfd.cColorBits := 32;
pfd.cDepthBits := 24;
pfd.iLayerType := PFD_MAIN_PLANE;
pfd.iPixelType := PFD_TYPE_RGBA;
dc := GetDC (wnd);
if (dc = 0) then
begin
Bomber_FatalError ('Ошибка получения DC. Код '+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
format := ChoosePixelFormat (dc, @pfd);
if (format = 0) then
begin
Bomber_FatalError
('Ошибка выбора формата пикселя. Код
'+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
if (not SetPixelFormat (dc, format, @pfd)) then
begin
Bomber_FatalError ('Ошибка установки формата пикселя. Код '+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
glrc := wglCreateContext (dc);
if (glrc = 0) then
begin
Bomber_FatalError ('Ошибка создания GLRC. Код '+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
if (not wglMakeCurrent (dc, glrc)) then
begin
Bomber_FatalError ('Ошибка установки GLRC. Код '+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
result := TRUE;
end;
procedure OnDestroy ();
begin
if (glrc <> 0) then
begin
wglMakeCurrent (0, 0);
wglDeleteContext (glrc);
glrc := 0;
end;
if (dc <> 0) then
begin
ReleaseDC (handle, dc);
dc := 0;
end;
end;
function WndProc (wnd: HWND; msg: UINT; wpar: WPARAM; lpar: LPARAM): LRESULT; stdcall;
begin
case (msg) of
WM_CREATE:
begin
if (not OnCreate (wnd)) then
begin
result := -1;
Exit;
end;
end;
WM_DESTROY:
begin
OnDestroy ();
end;
WM_CLOSE:
begin
PostQuitMessage (0);
end;
else
begin
result := DefWindowProc (wnd, msg, wpar, lpar);
Exit;
end;
end;
result := 0;
end;
function Bomber_WindowInitialize (): BOOLEAN;
var
devMode: TDEVMODE;
style, exStyle: DWORD;
rc: TRECT;
begin
ZeroMemory (@wndClass, sizeof (wndClass));
wndClass.style := CS_OWNDC;
wndClass.lpfnWndProc := @WndProc;
wndClass.hInstance := GetModuleHandle (nil);
wndClass.hIcon := LoadIcon (0, IDI_APPLICATION);
wndClass.hCursor := LoadCursor (0, IDC_ARROW);
wndClass.hbrBackground := GetStockObject (BLACK_BRUSH);
wndClass.lpszClassName := CLASS_NAME;
if (RegisterClass (wndClass) = 0) then
begin
Bomber_FatalError ('Ошибка регистрации класса окна. Код '+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
if (FULL_SCREEN) then
begin
ZeroMemory (@devMode, sizeof (devMode));
devMode.dmSize := sizeof (devMode);
devMode.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL or DM_DISPLAYFREQUENCY;
devMode.dmPelsWidth := WINDOW_WIDTH;
devMode.dmPelsHeight := WINDOW_HEIGHT;
devMode.dmBitsPerPel := 32;
devMode.dmDisplayFrequency := GetMaxFreq ();
if (ChangeDisplaySettings (devMode, CDS_FULLSCREEN) <> 0) then
begin
Bomber_FatalError ('Ошибка установки режима экрана');
result := FALSE;
Exit;
end;
style := WS_POPUP;
exStyle := WS_EX_TOPMOST;
rc.Left := 0;
rc.Top := 0;
rc.Right := WINDOW_WIDTH;
rc.Bottom := WINDOW_HEIGHT;
end
else
begin
style := WS_POPUP;
exStyle := WS_EX_DLGMODALFRAME;
rc.Left := GetSystemMetrics (SM_CXSCREEN) div 2 - WINDOW_WIDTH div 2;
rc.Top := GetSystemMetrics (SM_CYSCREEN) div 2 - WINDOW_HEIGHT div 2;
rc.Right := rc.Left + WINDOW_WIDTH;
rc.Bottom := rc.Top + WINDOW_HEIGHT;
AdjustWindowRectEx (rc, style, FALSE, exStyle);
end;
style := style or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
handle := CreateWindowEx (exStyle, wndClass.lpszClassName, 'Bridge Bomber',style,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0, 0, wndClass.hInstance, nil);
if (handle = 0) then
begin
Bomber_FatalError ('Ошибка создания окна. Код '+IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
ShowWindow (handle, SW_SHOW);
SetForegroundWindow (handle);
result
:= TRUE;
end;
procedure Bomber_WindowRelease ();
begin
if (handle <> 0) then
begin
ShowWindow (handle, SW_HIDE);
DestroyWindow (handle);
handle := 0;
end;
if (@wndClass.lpszClassName <> nil) then
begin
UnregisterClass (wndClass.lpszClassName, wndClass.hInstance);
ZeroMemory (@wndClass, sizeof (wndClass));
end;
end;
end.
unit bm_rend;
interface
function Bomber_RendInitialize (): BOOLEAN;
function Bomber_RendRelease (): BOOLEAN;
function Bomber_RendSet2dMode (): BOOLEAN;
function Bomber_RendSet3dMode (): BOOLEAN;
function Bomber_RendDrawText (x, y: INTEGER; Text: STRING; WhiteColor: BOOLEAN = TRUE): BOOLEAN;
implementation
uses
Windows,
OpenGL,
bm_core;
var
fontList: DWORD;
mode3d: BOOLEAN;
function Bomber_RendInitialize (): BOOLEAN;
var
font, oldFont: HFONT;
logFont: TLOGFONT;
dc: HDC;
begin
dc := wglGetCurrentDC ();
if (dc = 0) then
begin
Bomber_FatalError ('Ошибка инициализации рендерера. Код' + IntToStr (GetLastError()));
result := FALSE;
Exit;
end;
fontList := glGenLists (256);
if (fontList = 0) then
begin
Bomber_FatalError ('Ошибка инициализации рендерера');
result := FALSE;
Exit;
end;
ZeroMemory (@logFont, sizeof (logFont));
logFont.lfHeight := -MulDiv (DEBUG_FONT_SIZE, GetDeviceCaps (dc, LOGPIXELSY), 72);
logFont.lfCharSet := RUSSIAN_CHARSET;
logFont.lfFaceName := DEBUG_FONT_FACE;
logFont.lfWeight := FW_BOLD;
DEBUG_FONT_HEIGHT := -logFont.lfHeight;
font := CreateFontIndirect (logFont);
oldFont := SelectObject (dc, font);
wglUseFontBitmaps (dc, 0, 256, fontList);
DeleteObject (SelectObject (dc, oldFont));
glViewport
(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective (RENDER_FOV, WINDOW_WIDTH / WINDOW_HEIGHT, 0.01, RENDER_DISTANCE);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
mode3D := TRUE;
glDepthFunc (GL_LEQUAL);
glShadeModel (GL_SMOOTH);
glEnable (GL_DEPTH_TEST);
glEnable (GL_TEXTURE_2D);
glEnable (GL_LIGHTING);
result := TRUE;
end;
function Bomber_RendRelease (): BOOLEAN;
begin
if (fontList <> 0) then
begin
glDeleteLists (fontList, 256);
fontList := 0;
end;
result := TRUE;
end;
function Bomber_RendSet2dMode (): BOOLEAN;
begin
if (not mode3d) then
begin
result := FALSE;
Exit;
end;
glMatrixMode (GL_PROJECTION);
glPushMatrix ();
glLoadIdentity ();
glOrtho (0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, 1);
glMatrixMode (GL_MODELVIEW);
glPushMatrix ();
glLoadIdentity ();
mode3d := FALSE;
result := TRUE;
end;
function Bomber_RendSet3dMode (): BOOLEAN;
begin
if (mode3d) then
begin
result := FALSE;
Exit;
end;
glMatrixMode (GL_PROJECTION);
glPopMatrix ();
glMatrixMode (GL_MODELVIEW);
glPopMatrix ();
mode3d := TRUE;
result := TRUE;
end;
function Bomber_RendDrawText (x, y: INTEGER; Text: STRING; WhiteColor: BOOLEAN = TRUE): BOOLEAN;
begin
Bomber_RendSet2dMode ();
glPushAttrib (GL_ALL_ATTRIB_BITS);
glDisable (GL_DEPTH);
glDisable (GL_LIGHTING);
glDisable (GL_TEXTURE_2D);
if (WhiteColor) then
glColor3f (1.0, 1.0, 1.0);
glListBase (fontList);
glRasterPos2i (x, y);
glCallLists
(Length (Text), GL_UNSIGNED_BYTE, @Text [1]);
glPopAttrib ();
Bomber_RendSet3dMode ();
result := TRUE;
end;
end.
unit bm_timer;
interface
function Bomber_TimerInitialize (): BOOLEAN;
procedure Bomber_TimerRelease ();
procedure Bomber_TimerStart ();
function Bomber_TimerGetTick (): INT64;
procedure Bomber_TimerSleep (milliseconds: INT64);
procedure Bomber_TimerWait ();
function Bomber_TimerGetFps (): SINGLE;
implementation
uses
Windows,
bm_core;
const
MAX_FPS = 24;
FRAME_COUNT = 30;
var
frequency: INT64;
startTick: INT64;
lastTick: INT64;
nominalTick: INT64;
frames: ARRAY [0..FRAME_COUNT-1] OF INT64;
function Bomber_TimerInitialize (): BOOLEAN;
var
counter: INT64;
begin
if (MAX_FPS <> 1) then
nominalTick := 1000 div MAX_FPS
else
nominalTick := 1;
if not ((QueryPerformanceFrequency (frequency))
or (QueryPerformanceCounter (counter))) then
begin
Bomber_FatalError ('Высокочастотный таймер не поддерживается');
result := FALSE;
end
else
result := TRUE
end;
procedure Bomber_TimerRelease ();
begin
end;
procedure Bomber_TimerStart ();
begin
QueryPerformanceCounter (startTick);
end;
function Bomber_TimerGetTick (): INT64;
var
tick: INT64;
begin
QueryPerformanceCounter (tick);
result := (tick - startTick) div (frequency div 1000);
end;
procedure Bomber_TimerSleep (milliseconds: INT64);
var
tick: INT64;
begin
tick
:= Bomber_TimerGetTick ();
while (Bomber_TimerGetTick () - tick < milliseconds) do;
end;
procedure Bomber_TimerWait ();
var
tick: INT64;
i: INTEGER;
begin
tick := Bomber_TimerGetTick ();
if (tick - lastTick < nominalTick) then
begin
Bomber_TimerSleep (nominalTick - tick + lastTick);
tick := Bomber_TimerGetTick ();
end;
for i := FRAME_COUNT - 1 downto 0 do
frames [i] := frames [i-1];
frames [0] := tick - lastTick;
lastTick := tick;
end;
function Bomber_TimerGetFps (): SINGLE;
var
i: INTEGER;
sum, res: SINGLE;
begin
sum := 0;
for i := 0 to FRAME_COUNT-1 do
sum := sum + frames [i];
res := 1000 / (sum / FRAME_COUNT);
if (res < 1) then
result := 1
else
result := res;
end;
end.
unit bm_river;
interface
function Bomber_RiverInitialize (): BOOLEAN;
procedure Bomber_RiverRelease ();
procedure Bomber_RiverDraw ();
function Bomber_RiverGetLevel (): SINGLE;
implementation
uses
Windows,
OpenGL,
Math,
bm_graph,
bm_core,
bm_tga,
bm_timer;
const
TEXTURE_WATER = 'water.tga';
TEXTURE_GRASS = 'grass.tga';
TEXTURE_SLOPE = 'slope.tga';
TEXTURE_BOTTOM = 'bottom.tga';
var
bankList: DWORD;
waterList: DWORD;
waterTex: DWORD;
grassTex: DWORD;
slopeTex: DWORD;
bottomTex: DWORD;
xValue: SINGLE = 0;
zValue: SINGLE = 0;
xSign: SINGLE
= 1;
zSign: SINGLE = 1;
procedure BuildList ();
var
x1, x2, x3, z1: SINGLE;
begin
x1 := RENDER_DISTANCE / 2;
x2 := 50;
x3 := 45;
z1 := RENDER_DISTANCE / 2;
glNewList (bankList, GL_COMPILE);
glPushAttrib (GL_ALL_ATTRIB_BITS);
// Grass
glBindTexture (GL_TEXTURE_2D, grassTex);
glBegin (GL_QUADS);
// Left bank
glTexCoord2f (20.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x2, 0.0, -z1);
glTexCoord2f (0.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x1, 0.0, -z1);
glTexCoord2f (0.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x1, 0.0, z1);
glTexCoord2f (20.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x2, 0.0, z1);
// Right bank
glTexCoord2f (20.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x1, 0.0, -z1);
glTexCoord2f (0.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x2, 0.0, -z1);
glTexCoord2f (0.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x2, 0.0, z1);
glTexCoord2f (20.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x1, 0.0, z1);
glEnd ();
// Bank slopes
glBindTexture (GL_TEXTURE_2D, slopeTex);
glBegin (GL_QUADS);
// Left slope
glTexCoord2f (1.0, 50.0);
glNormal3f (0.5, 0.5, 0.0);
glVertex3f (-x3, -5.0, -z1);
glTexCoord2f (0.0, 50.0);
glNormal3f (0.5, 0.5, 0.0);
glVertex3f (-x2, 0.0, -z1);
glTexCoord2f (0.0, 0.0);
glNormal3f (0.5, 0.5, 0.0);
glVertex3f (-x2, 0.0, z1);
glTexCoord2f (1.0, 0.0);
glNormal3f (0.5, 0.5, 0.0);
glVertex3f (-x3, -5.0, z1);
// Right slope
glTexCoord2f (1.0, 50.0);
glNormal3f (-0.5, 0.5, 0.0);
glVertex3f (x2, 0.0, -z1);
glTexCoord2f
(0.0, 50.0);
glNormal3f (-0.5, 0.5, 0.0);
glVertex3f (x3, -5.0, -z1);
glTexCoord2f (0.0, 0.0);
glNormal3f (-0.5, 0.5, 0.0);
glVertex3f (x3, -5.0, z1);
glTexCoord2f (1.0, 0.0);
glNormal3f (-0.5, 0.5, 0.0);
glVertex3f (x2, 0.0, z1);
glEnd ();
// River bottom
glBindTexture (GL_TEXTURE_2D, bottomTex);
glBegin (GL_QUADS);
glTexCoord2f (10.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x3, -5.0, -z1);
glTexCoord2f (0.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x3, -5.0, -z1);
glTexCoord2f (0.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x3, -5.0, z1);
glTexCoord2f (10.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x3, -5.0, z1);
glEnd ();
glPopAttrib ();
glEndList ();
// River water
glNewList (waterList, GL_COMPILE);
glPushAttrib (GL_ALL_ATTRIB_BITS);
glBindTexture (GL_TEXTURE_2D, waterTex);
glEnable (GL_BLEND);
glColor4f (0.25, 0.25, 0.25, 0.5);
glBlendFunc (GL_ONE, GL_SRC_ALPHA);
glBegin (GL_QUADS);
glTexCoord2f (10.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x2, -1.5, -z1);
glTexCoord2f (0.0, 50.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x2, -1.5, -z1);
glTexCoord2f (0.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (-x2, -1.5, z1);
glTexCoord2f (10.0, 0.0);
glNormal3f (0.0, 1.0, 0.0);
glVertex3f (x2, -1.5, z1);
glEnd ();
glDisable (GL_BLEND);
glPopAttrib ();
glEndList ();
end;
function Bomber_RiverInitialize (): BOOLEAN;
begin
bankList := glGenLists (1);
if (bankList = 0) then
begin
Bomber_FatalError ('Ошибка инициализации рендерера');
result
:= FALSE;
Exit;
end;
waterList := glGenLists (1);
if (waterList = 0) then
begin
Bomber_FatalError ('Ошибка инициализации рендерера');
result := FALSE;
Exit;
end;
waterTex := Bomber_TgaLoad (TEXTURE_WATER);
grassTex := Bomber_TgaLoad (TEXTURE_GRASS);
slopeTex := Bomber_TgaLoad (TEXTURE_SLOPE);
bottomTex := Bomber_TgaLoad (TEXTURE_BOTTOM);
BuildList ();
result := TRUE;
end;
procedure Bomber_RiverRelease ();
begin
if (waterList <> 0) then
begin
glDeleteLists (waterList, 1);
waterList := 0;
end;
if (bankList <> 0) then
begin
glDeleteLists (bankList, 1);
bankList := 0;
end;
end;
procedure Bomber_RiverDraw ();
begin
xValue := xValue + (20 / Bomber_TimerGetFps ());
zValue := zValue + (10 / Bomber_TimerGetFps ());
if (xValue >= 360.0) then
xValue := 0;
if (zValue >= 360.0) then
zValue := 0;
glPushAttrib (GL_ALL_ATTRIB_BITS);
glPushMatrix ();
glCallList (bankList);
glTranslatef (sin (DegToRad (xValue)), sin (DegToRad (xValue + zValue)) * 0.25, sin (DegToRad (zValue)));
glDisable (GL_LIGHTING);
glCallList (waterList);
glPopMatrix ();
glPopAttrib ();
end;
function Bomber_RiverGetLevel (): SINGLE;
begin
result := sin (DegToRad (xValue + zValue)) * 0.25;
end;
end.
unit bm_scene;
interface
var
ALFA_VALUE: SINGLE = 0;
SERIAL_BOMBING: BOOLEAN = TRUE;
function Bomber_SceneInitialize (): BOOLEAN;
procedure Bomber_SceneRelease ();
function Bomber_SceneDraw (): BOOLEAN;
implementation
uses
Windows,
OpenGL,
bm_core,
bm_rend,
bm_brid,
bm_plane,
bm_river,
bm_timer,
bm_expl,
bm_math;
const
SPLASH_LINE_COUNT = 6;
splashText: ARRAY [1..SPLASH_LINE_COUNT, 1..2] OF STRING =
(
('',''),
('ТРОИЦКНАУЧФИЛЬМ','представляет'),
('БОМБОМЕТАНИЕ','ПО ЖЕЛЕЗНОДОРОЖНОМУ МОСТУ'),
('фильм курсантов 331 группы ТАТК ГА','ГАЛИМОВА А. и КОЗЛОВА А.'),
('по заказу','СМОЛЯКОВОЙ Г.Н.'),
('','')
);
var
fFogColor: ARRAY [0..3] OF SINGLE = (0.6, 0.6, 0.6, 1.0);
fSplashColor: ARRAY [0..3] OF SINGLE = (0.25, 0.25, 0.25, 1.0);
fLampDifColor: ARRAY [0..3] OF SINGLE = (0.8, 0.8, 0.8, 1.0);
fLampAmbColor: ARRAY [0..3] OF SINGLE = (0.5, 0.5, 0.5, 1.0);
fLampPosition: ARRAY [0..3] OF SINGLE = (0.0, 5000.0, 0.0, 1.0);
fLampDirection: ARRAY [0..2] OF SINGLE = (0.0, 0.0, 0.0);
resultFrame: INTEGER;
splashFrame: INTEGER = -1;
splashLine: INTEGER = 1;
exploSetup: BOOLEAN = TRUE;
function Bomber_SceneInitialize (): BOOLEAN;
begin
glClearColor (fFogColor [0], fFogColor [1], fFogColor [2], fFogColor [3]);
glFogfv (GL_FOG_COLOR, @fFogColor);
glFogi (GL_FOG_MODE, GL_LINEAR);
glFogf (GL_FOG_START, RENDER_DISTANCE * 0.4);
glFogf (GL_FOG_END, RENDER_DISTANCE * 0.75);
glFogf (GL_FOG_DENSITY, 1.0);
glEnable (GL_FOG);
glLightModelf (GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
glLightfv (GL_LIGHT0, GL_AMBIENT, @fLampAmbColor);
glLightfv (GL_LIGHT0, GL_DIFFUSE, @fLampDifColor);
glLightfv (GL_LIGHT0, GL_SPOT_DIRECTION, @fLampDirection);
glLightfv (GL_LIGHT0, GL_POSITION, @fLampPosition);
glEnable (GL_LIGHT0);
result := TRUE;
end;
procedure Bomber_SceneRelease ();
begin
end;
function SplashDraw (cl: SINGLE): BOOLEAN;
var
sz: TSize;
begin
if (splashLine > SPLASH_LINE_COUNT) then
begin
result := FALSE;
Exit;
end;
if (splashFrame = -1) then
begin
splashFrame := 45;
end
else
dec (splashFrame);
if
(splashFrame = 0) then
begin
splashFrame := Round (5 * Bomber_TimerGetFps ());
inc (splashLine);
if (splashLine > SPLASH_LINE_COUNT) then
begin
result := FALSE;
Exit;
end;
end;
glClearColor (fSplashColor [0], fSplashColor [1], fSplashColor [2], fSplashColor [3]);
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glColor3f (0.8 + cl, 0.8 + cl, 0.8 + cl);
GetTextExtentPoint32 (wglGetCurrentDC (), PCHAR (splashText [splashLine, 1]), Length (splashText [splashLine, 1]), sz);
Bomber_RendDrawText (WINDOW_WIDTH div 2 - sz.cx div 2,
WINDOW_HEIGHT div 2 - sz.cy, splashText [splashLine, 1], FALSE);
GetTextExtentPoint32 (wglGetCurrentDC (), PCHAR (splashText [splashLine, 2]), Length (splashText [splashLine, 2]), sz);
Bomber_RendDrawText (WINDOW_WIDTH div 2 - sz.cx div 2,
WINDOW_HEIGHT div 2 + sz.cy, splashText [splashLine, 2], FALSE);
result := TRUE;
end;
procedure DrawBridgeScheme (cl: SINGLE);
var
xc, yc: SINGLE;
x, y: SINGLE;
d, l: SINGLE;
i: INTEGER;
begin
xc := WINDOW_WIDTH - WINDOW_WIDTH div 4;
yc := WINDOW_HEIGHT div 2;
d := BRIDGE_LENGTH * 1.5;
l := BRIDGE_WIDTH * 1.5;
Bomber_RendSet2dMode ();
glPushAttrib (GL_ALL_ATTRIB_BITS);
glDisable (GL_LIGHTING);
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glColor3f (0.8 + cl, 0.8 + cl, 0.8 + cl);
glLineWidth (2);
glBegin (GL_QUADS);
glVertex2f (xc + l / 2, yc - d / 2);
glVertex2f (xc - l / 2, yc - d / 2);
glVertex2f (xc - l / 2, yc + d / 2);
glVertex2f (xc + l / 2, yc + d / 2);
glEnd ();
glLineWidth (1);
glBegin (GL_LINES);
glVertex2f (xc, yc - 100);
glVertex2f (xc, yc + 100);
glVertex2f (xc - 100, yc);
glVertex2f (xc + 100, yc);
glEnd ();
glBegin (GL_LINES);
for i := 1 to BOMB_COUNT do
begin
x := xc + bombPos [Round (ALFA_VALUE) div 10, i-1].x * 1.5;
y := yc - bombPos [Round (ALFA_VALUE) div 10, i-1].y * 1.5;
glVertex2f (x - 3, y - 3);
glVertex2f (x + 3, y + 3);
glVertex2f (x - 3, y + 3);
glVertex2f (x + 3, y - 3);
end;
glEnd ();
glPopAttrib ();
Bomber_RendSet3dMode ();
end;
function ShowResults (cl: SINGLE): BOOLEAN;
var
i,
n, h: INTEGER;
begin
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity ();
h := WINDOW_HEIGHT - WINDOW_HEIGHT div 10;
DrawBridgeScheme (cl);
glColor3f (0.8 + cl, 0.8 + cl, 0.8 + cl);
Bomber_RendDrawText (40, h - 190, 'РЕЗУЛЬТАТЫ БОМБАРДИРОВКИ', FALSE);
Bomber_RendDrawText (40, h - 160, 'Угол захода самолета: ' + IntToStr (Round (ALFA_VALUE)) + #176, FALSE);
Bomber_RendDrawText (40, h - 130, 'Координаты падения бомб:', FALSE);
n := 0;
for i := 1 to BOMB_COUNT do
begin
Bomber_RendDrawText (60, h - 130 + i * DEBUG_FONT_HEIGHT, IntToStr (i) + ': ('
+ FloatToStr (bombPos [Round (ALFA_VALUE) div 10, BOMB_COUNT - i].x, 7, 3) + ';'
+ FloatToStr (bombPos [Round (ALFA_VALUE) div 10, BOMB_COUNT - i].y, 7, 3) + ')', FALSE);
if (bombPos [Round (ALFA_VALUE) div 10, BOMB_COUNT - i].crash) then
begin
Bomber_RendDrawText (240, h - 130 + i * DEBUG_FONT_HEIGHT, 'ПОПАДАНИЕ', FALSE);
inc (n);
end;
end;
Bomber_RendDrawText (40, h - 130 + DEBUG_FONT_HEIGHT * (BOMB_COUNT + 2),
'Качество бомбардировки: ' + FloatToStr ((n / BOMB_COUNT) * 100, 5, 2) + '%', FALSE);
dec (resultFrame);
if (resultFrame = 0) then
begin
if (SERIAL_BOMBING) then
begin
if (ALFA_VALUE = 90) then
begin
result := FALSE;
Exit;
end
else
ALFA_VALUE := ALFA_VALUE + 10;
end
else
begin
result := FALSE;
Exit;
end;
glClearColor (fFogColor [0], fFogColor [1], fFogColor [2], fFogColor [3]);
Bomber_PlaneRestart ();
Bomber_BridgeBuild ();
exploSetup := TRUE;
end;
result := TRUE;
end;
function Bomber_SceneDraw (): BOOLEAN;
var
yt, h: INTEGER;
cl: SINGLE;
begin
yt := random (2);
cl := (-5 + random (10)) / 100;
if (Bomber_KeyDown (VK_ESCAPE)) then
begin
result := FALSE;
Exit;
end;
glViewPort (0, yt, WINDOW_WIDTH, WINDOW_HEIGHT);
glClearColor (fFogColor [0], fFogColor [1], fFogColor [2], fFogColor [3]);
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity ();
glTranslatef (0.0, -1.85, -150.0);
glRotatef (20.0, 0.0, 1.0, 0.0);
Bomber_BridgeDraw ();
if (SERIAL_BOMBING) and (SplashDraw (cl)) then
begin
result := TRUE;
Exit;
end;
if (exploSetup) then
begin
Bomber_ExploSetup (Round (ALFA_VALUE) div 10);
exploSetup := FALSE;
end;
if (not Bomber_PlaneDraw ()) then
begin
glClearColor (fSplashColor [0], fSplashColor [1], fSplashColor [2], fSplashColor [3]);
if (resultFrame = 0) then
resultFrame := Round (7 * Bomber_TimerGetFps ());
if (ShowResults (cl)) then
result := TRUE
else
result := FALSE;
Exit;
end;
glColor3f (0.8 + cl, 0.8 + cl, 0.8 + cl);
h := WINDOW_HEIGHT - WINDOW_HEIGHT div 10;
Bomber_RendDrawText (40, h - 130 + DEBUG_FONT_HEIGHT * 6,
'Угол захода самолета: ' + IntToStr (Trunc (ALFA_VALUE)) + #176, FALSE);
Bomber_RendDrawText (40, h - 130 + DEBUG_FONT_HEIGHT * 7,
'Вероятность разрушения моста: ' + FloatToStr (crashProbab [Trunc (ALFA_VALUE / 10)] * 100, 5, 2) + '%', FALSE);
Bomber_ExploDraw ();
Bomber_RiverDraw ();
result := TRUE;
end;
end.
unit bm_file;
interface
uses
Windows;
function Bomber_FileOpen (FileName: STRING): THANDLE;
procedure Bomber_FileClose (FileHandle: THANDLE);
function Bomber_FileRead (FileHandle: THANDLE; Buff: POINTER; Size: DWORD): DWORD;
function Bomber_FileWrite (FileHandle: THANDLE; Buff: POINTER; Size: DWORD): DWORD;
implementation
var
localDir: STRING = '';
function GetLocalDir (): STRING;
var
name: ARRAY
[0..MAX_PATH+1] OF CHAR;
i: INTEGER;
begin
if (localDir <> '') then
begin
result := localDir;
Exit;
end;
GetModuleFileName (0, @name, sizeof (name));
CharLowerBuff (@name, Length (name));
for i := Length (name)-1 downto 0 do
if (name [i] = '\') then
begin
Copy (localDir, 1, i);
Break;
end;
result := localDir;
end;
function Bomber_FileOpen (FileName: STRING): THANDLE;
begin
FileName := GetLocalDir + FileName;
result := CreateFile (PCHAR (FileName), GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);
end;
procedure Bomber_FileClose (FileHandle: THANDLE);
begin
CloseHandle (FileHandle);
end;
function Bomber_FileRead (FileHandle: THANDLE; Buff: POINTER; Size: DWORD): DWORD;
var
read: DWORD;
begin
ReadFile (FileHandle, Buff^, Size, read, nil);
result := read;
end;
function Bomber_FileWrite (FileHandle: THANDLE; Buff: POINTER; Size: DWORD): DWORD;
var
written: DWORD;
begin
WriteFile (FileHandle, Buff^, Size, written, nil);
result := written;
end;
end.
unit bm_tga;
interface
uses
Windows;
function Bomber_TgaLoad (FileName: STRING): DWORD;
implementation
uses
OpenGL,
bm_graph,
bm_core,
bm_file,
bm_mem;
const
TARGA_TRUECOLOR_IMAGE = 2;
TARGA_TRUECOLOR_RLE_IMAGE = 10;
type
TGAFILEHEADER = packed record
idFieldLength: BYTE;
colorMapType: BYTE;
imageType: BYTE;
firstColorMapEntry: WORD;
colorMapLength: WORD;
colorMapEntrySize: BYTE;
imageXOrigin: WORD;
imageYOrigin: WORD;
imageWidth: WORD;
imageHeight: WORD;
bitsPerPixel: BYTE;
imageDescriptorBits: BYTE;
end;
procedure SwapColors (data: PBYTE; dataSize: DWORD; bpp: BYTE);
var
i: DWORD;
t: BYTE;
begin
if (bpp = 24) then
begin
i := 0;
while (i < dataSize) do
begin
t := PBYTE (DWORD (data) + i + 0)^;
PBYTE (DWORD (data) + i + 0)^ := PBYTE (DWORD (data) + i + 2)^;
PBYTE (DWORD (data) + i + 2)^ := t;
inc (i, 3);
end;
Exit;
end;
if (bpp = 32) then
begin
i := 0;
while (i < dataSize) do
begin
t := PBYTE (DWORD (data) + i + 0)^;
PBYTE (DWORD (data) + i + 0)^ := PBYTE (DWORD (data) + i + 2)^;
PBYTE (DWORD (data) + i + 2)^ := t;
inc (i, 4);
end;
Exit;
end;
end;
procedure FlipImage (data: PBYTE; width, height: DWORD; bpp: BYTE);
var
t, k: BYTE;
x, y: DWORD;
begin
k := bpp div 8;
width := width * k;
for y := 0 to height div 2 - 1 do
for x := 0 to width - 1 do
begin
t := PBYTE (DWORD (data) + y * width + x)^;
PBYTE (DWORD (data) + y * width + x)^ := PBYTE (DWORD (data) + (height - y - 1) * width + x)^;
PBYTE (DWORD (data) + (height - y - 1) * width + x)^ := t;
end;
end;
function Bomber_TgaLoad (FileName: STRING): DWORD;
var
res: HRSRC;
resPtr: HGLOBAL;
resPos: DWORD;
header: TGAFILEHEADER;
dataSize: DWORD;
data: PBYTE;
format: DWORD;
texture: DWORD;
formatbyte: DWORD;
begin
if (Pos ('.', FileName) <> 0) then
FileName := 'T_' + Copy (FileName, 1, Pos ('.', FileName) - 1);
res := FindResource (0, PCHAR (FileName), RT_RCDATA);
if
(res = 0) then
begin
result := 0;
Exit;
end;
resPos := 0;
resPtr := LoadResource (0, res);
if (resPtr = 0) then
begin
result := 0;
Exit;
end;
CopyMemory (@header, POINTER (resptr + resPos), sizeof (header));
inc (resPos, sizeof (header));
if (header.imageType <> TARGA_TRUECOLOR_IMAGE)
and (header.imageType <> TARGA_TRUECOLOR_RLE_IMAGE) then
begin
result := 0;
Exit;
end;
dataSize := header.imageWidth * header.imageHeight * (header.bitsPerPixel div 8);
data := Bomber_MemAlloc (dataSize);
if (data = nil) then
begin
result := 0;
Exit;
end;
if (header.imageType = TARGA_TRUECOLOR_IMAGE) then
CopyMemory (data, POINTER (resPtr + resPos), dataSize);
SwapColors (data, dataSize, header.bitsPerPixel);
if (header.imageDescriptorBits and $20 = $20) then
FlipImage (data, header.imageWidth, header.imageHeight, header.bitsPerPixel);
if (header.bitsPerPixel = 24) then
begin
format := GL_RGB;
formatbyte := 3;
end
else
begin
format := GL_RGBA;
formatbyte := 4;
end;
glPushAttrib (GL_TEXTURE_BIT);
glGenTextures (1, texture);
glBindTexture (GL_TEXTURE_2D, texture);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
if (Pos ('GL_SGIS_generate_mipmap', glGetString (GL_EXTENSIONS)) <> 0) then
begin
glTexParameteri (GL_TEXTURE_2D, $8191, 1);
glTexImage2D (GL_TEXTURE_2D, 0, formatbyte, header.imageWidth, header.imageHeight,
0, format, GL_UNSIGNED_BYTE, data);
end
else
begin
gluBuild2DMipmaps (GL_TEXTURE_2D, header.bitsPerPixel div 8,
header.imageWidth, header.imageHeight, format, GL_UNSIGNED_BYTE, data);
end;
glPopAttrib ();
ПОВТиАС
Bomber_MemFree (data);
result := texture;
end;
end.
unit bm_mem;
interface
uses
Windows;
function Bomber_MemAlloc (size: DWORD): POINTER;
procedure Bomber_MemFree (ptr: POINTER);
implementation
function Bomber_MemAlloc (size: DWORD): POINTER;
begin
result := POINTER (GlobalAlloc (GPTR, size));
end;
procedure Bomber_MemFree (ptr: POINTER);
begin
GlobalFree (HGLOBAL (ptr));
end;
end.
unit bm_graph;
interface
uses
OpenGL;
function gluBuild2DMipmaps(Target: GLenum; Components, Width, Height: GLint; Format, atype: GLenum; Data: Pointer): GLint; stdcall; external glu32;
procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external 'OpenGL32.dll';
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external 'OpenGL32.dll';
implementation
end.
unit bm_model;
interface
function Bomber_ModelInitialize (): BOOLEAN;
procedure Bomber_ModelRelease ();
function Bomber_ModelLoad (FileName: STRING): INTEGER;
procedure Bomber_ModelDraw (model: INTEGER; x, y, z: SINGLE; ya, za: SINGLE);
implementation
uses
Windows,
OpenGL,
bm_file,
bm_graph,
bm_tga;
const
MAX_MODEL_COUNT = 15;
INVALID_MODEL_INDEX = -1;
const
FILE_ID = $004D5354;
FILE_VER = $0010;
MAX_MATERIAL_COUNT = 65535;
MAX_VERTEX_COUNT = 65535;
MAX_NORMAL_COUNT = 65535;
MAX_COORD_COUNT = 65535;
MAX_GROUP_COUNT =
65535;
MAX_FACE_COUNT = 65535;
type
HEADER_T = packed record
FileId: DWORD;
FileVer: WORD;
MaterialCount: WORD;
VertexCount: WORD;
CoordCount: WORD;
NormalCount: WORD;
GroupCount: WORD;
end;
MATERIAL_T = packed record
AmbientColor: ARRAY [0..3] OF SINGLE;
DiffuseColor: ARRAY [0..3] OF SINGLE;
SpecularColor: ARRAY [0..3] OF SINGLE;
Shininess: SINGLE;
TextureName: ARRAY [0..15] OF CHAR;
end;
GROUP_T = packed record
Material: WORD;
FaceCount: WORD;
end;
FACE_ITEM_T = packed record
VertexIndex: WORD;
CoordIndex: WORD;
NormalIndex: WORD;
end;
FACE_T = packed record
Item: ARRAY [0..2] OF FACE_ITEM_T;
end;
VERTEX_T = packed record
x, y, z: SINGLE;
end;
COORD_T = packed record
s, t: SINGLE;
end;
NORMAL_T = packed record
nx, ny, nz: SINGLE;
end;
var
material: ARRAY [1..MAX_MATERIAL_COUNT] OF MATERIAL_T;
vertex: ARRAY [1..MAX_VERTEX_COUNT] OF VERTEX_T;
texCoord: ARRAY [1..MAX_COORD_COUNT] OF COORD_T;
normal: ARRAY [1..MAX_NORMAL_COUNT] OF NORMAL_T;
modelList: ARRAY [0..MAX_MODEL_COUNT-1] OF DWORD;
modelIndex: INTEGER = 0;
function Bomber_ModelInitialize (): BOOLEAN;
begin
ZeroMemory (@modelList, sizeof (modelList));
ZeroMemory (@material, sizeof (material));
ZeroMemory (@texCoord, sizeof (texCoord));
ZeroMemory (@normal, sizeof (normal));
result := TRUE;
end;
procedure Bomber_ModelRelease ();
var
i: INTEGER;
begin
for i := 0 to MAX_MODEL_COUNT-1 do
if (modelList [i] <> 0) then
begin
glDeleteLists (modelList [i], 1);
modelList [i] := 0;
end;
end;
function GetFreeIndex (): INTEGER;
begin
if
(modelIndex < MAX_MODEL_COUNT) then
begin
result := modelIndex;
inc (modelIndex);
end
else
result := INVALID_MODEL_INDEX;
end;
function Bomber_ModelLoad (FileName: STRING): INTEGER;
var
res: HRSRC;
resPtr: HGLOBAL;
resPos: DWORD;
hdr: HEADER_T;
ghdr: GROUP_T;
size: DWORD;
g, f: INTEGER;
index: INTEGER;
item: FACE_ITEM_T;
fColor: ARRAY [0..3] OF SINGLE;
begin
index := GetFreeIndex ();
if (index = INVALID_MODEL_INDEX) then
begin
result := -1;
Exit;
end;
if (Pos ('.', FileName) <> 0) then
FileName := 'M_' + Copy (FileName, 1, Pos ('.', FileName) - 1);
res := FindResource (0, PCHAR (FileName), RT_RCDATA);
if (res = 0) then
begin
result := -1;
Exit;
end;
resPos := 0;
resPtr := LoadResource (0, res);
if (resPtr = 0) then
begin
result := -1;
Exit;
end;
CopyMemory (@hdr, POINTER (resPtr + resPos), sizeof (hdr));
inc (resPos, sizeof (hdr));
if (hdr.FileId <> FILE_ID) or (hdr.FileVer <> FILE_VER) then
begin
result := -1;
Exit;
end;
size := sizeof (material [1]) * hdr.MaterialCount;
CopyMemory (@material [1], POINTER (resPtr + resPos), size);
inc (resPos, size);
size := sizeof (vertex [1]) * hdr.VertexCount;
CopyMemory (@vertex [1], POINTER (resPtr + resPos), size);
inc (resPos, size);
size := sizeof (texCoord [1]) * hdr.CoordCount;
CopyMemory (@texCoord [1], POINTER (resPtr + resPos), size);
inc (resPos, size);
size := sizeof (normal [1]) * hdr.NormalCount;
CopyMemory (@normal [1], POINTER (resPtr + resPos), size);
inc (resPos, size);
modelList [index] := glGenLists (1);
glNewList (modelList [index], GL_COMPILE);
glPushAttrib (GL_ALL_ATTRIB_BITS);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
for g := 1 to hdr.GroupCount do
begin
CopyMemory (@ghdr, POINTER (resPtr + resPos), sizeof (ghdr));
inc
(resPos, sizeof (ghdr));
if (ghdr.Material <> 0) then
begin
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, @material [ghdr.Material].AmbientColor [0]);
glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, @material [ghdr.Material].DiffuseColor [0]);
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, @material [ghdr.Material].SpecularColor [0]);
if (material [ghdr.Material].TextureName <> '') then
begin
glBindTexture (GL_TEXTURE_2D, Bomber_TgaLoad (material [ghdr.Material].TextureName));
glEnable (GL_TEXTURE_2D);
end
else
glDisable (GL_TEXTURE_2D);
end
else
begin
fColor [0] := 0.2;
fColor [1] := 0.2;
fColor [2] := 0.2;
fColor [3] := 1.0;
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, @fColor [0]);
fColor [0] := 0.8;
fColor [1] := 0.8;
fColor [2] := 0.8;
glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, @fColor [0]);
fColor [0] := 1.0;
fColor [1] := 1.0;
fColor [2] := 1.0;
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, @fColor [0]);
glDisable (GL_TEXTURE_2D);
end;
glBegin (GL_TRIANGLES);
for f := 1 to ghdr.FaceCount*3 do
begin
CopyMemory (@item, POINTER (resPtr + resPos), sizeof (item));
inc (resPos, sizeof (item));
glTexCoord2fv (@texCoord [item.CoordIndex]);
glNormal3fv (@normal [item.NormalIndex]);
glVertex3fv (@vertex [item.VertexIndex]);
end;
glEnd ();
end;
glPopAttrib ();
glEndList ();
result := index;
end;
procedure Bomber_ModelDraw (model: INTEGER; x, y, z: SINGLE; ya, za: SINGLE);
begin
if (modelList [model] = 0) then
Exit;
glPushMatrix ();
glTranslatef (x, y, z);
glRotatef (ya, 0.0, 1.0, 0.0);
glRotatef (za, 0.0, 0.0, 1.0);
glCallList (modelList [model]);
glPopMatrix ();
end;
end.
unit bm_dlg;
interface
function Bomber_DialogExecute (): BOOLEAN;
implementation
uses
Windows,
Messages,
bm_math,
bm_scene,
bm_core;
const
ID_DIALOG = 1000;
IDCB_WIND_SIZE = 2003;
IDCB_BOMB_COUNT = 2005;
IDCB_TYPE = 2007;
IDST_ALFA = 2008;
IDCB_ALFA = 2009;
IDB_OK = 2010;
IDB_CANCEL = 2011;
function InitCommonControls (): BOOL; external 'comctl32.dll';
procedure ProcessControls (hDlg: HWND);
begin
case (SendDlgItemMessage (hDlg, IDCB_WIND_SIZE, CB_GETCURSEL, 0, 0)) of
0:
begin
WINDOW_WIDTH := 640;
WINDOW_HEIGHT := 480;
end;
1:
begin
WINDOW_WIDTH := 800;
WINDOW_HEIGHT := 600;
end;
2:
begin
WINDOW_WIDTH := 1024;
WINDOW_HEIGHT := 768;
end;
end;
BOMB_COUNT := SendDlgItemMessage (hDlg, IDCB_BOMB_COUNT, CB_GETCURSEL, 0, 0) + 1;
SERIAL_BOMBING := SendDlgItemMessage (hDlg, IDCB_TYPE, CB_GETCURSEL, 0, 0) = 1;
if (SERIAL_BOMBING) then
ALFA_VALUE := 0
else
ALFA_VALUE := SendDlgItemMessage (hDlg, IDCB_ALFA, CB_GETCURSEL, 0, 0) * 10;
Bomber_MathProcess ();
end;
function DialogProc (hDlg: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
var
i: INTEGER;
begin
case (uMsg) of
WM_INITDIALOG:
begin
SendDlgItemMessage (hDlg, IDCB_WIND_SIZE, CB_ADDSTRING, 0, INTEGER (PCHAR ('640 x 480')));
SendDlgItemMessage (hDlg, IDCB_WIND_SIZE, CB_ADDSTRING, 0, INTEGER (PCHAR ('800 x 600')));
SendDlgItemMessage (hDlg, IDCB_WIND_SIZE, CB_ADDSTRING, 0, INTEGER (PCHAR ('1024 x 768')));
for i := 1 to 4 do
SendDlgItemMessage (hDlg, IDCB_BOMB_COUNT, CB_ADDSTRING, 0, INTEGER (PCHAR (IntToStr (i))));
SendDlgItemMessage (hDlg, IDCB_TYPE, CB_ADDSTRING, 0, INTEGER (PCHAR ('Одиночное')));
SendDlgItemMessage (hDlg, IDCB_TYPE, CB_ADDSTRING, 0, INTEGER (PCHAR ('Серийное')));
for i := 0 to 9 do
SendDlgItemMessage (hDlg, IDCB_ALFA, CB_ADDSTRING, 0, INTEGER (PCHAR (IntToStr (i * 10))));
SendDlgItemMessage (hDlg, IDCB_WIND_SIZE, CB_SETCURSEL, 0, 0);
SendDlgItemMessage (hDlg, IDCB_BOMB_COUNT, CB_SETCURSEL, 0, 0);
SendDlgItemMessage (hDlg, IDCB_TYPE, CB_SETCURSEL, 0, 0);
SendDlgItemMessage (hDlg, IDCB_ALFA, CB_SETCURSEL, 0, 0);
end;
WM_COMMAND:
begin
if (LOWORD (wParam) = IDB_CANCEL)
or (LOWORD (wParam) = IDCANCEL) then
EndDialog (hDlg, -1);
if (LOWORD (wParam) = IDB_OK) then
begin
ProcessControls (hDlg);
EndDialog (hDlg, 1);
end;
if (LOWORD (wParam) = IDCB_TYPE) then
case (SendDlgItemMessage (hDlg, IDCB_TYPE, CB_GETCURSEL, 0, 0)) of
0:
begin
EnableWindow (GetDlgItem (hDlg, IDST_ALFA), TRUE);
EnableWindow (GetDlgItem (hDlg, IDCB_ALFA), TRUE);
end;
1:
begin
EnableWindow (GetDlgItem (hDlg, IDST_ALFA), FALSE);
EnableWindow (GetDlgItem (hDlg, IDCB_ALFA), FALSE);
end;
end;
end;
WM_DESTROY:
begin
end;
else
begin
result := FALSE;
Exit;
end;
end;
result := TRUE;
end;
function Bomber_DialogExecute (): BOOLEAN;
var
res: INTEGER;
begin
InitCommonControls ();
res := DialogBox (GetModuleHandle (nil), MAKEINTRESOURCE (ID_DIALOG), 0, @DialogProc);
if (res = -1) then
result := FALSE
else
result := TRUE;
end;
end.
unit bm_brid;
interface
function Bomber_BridgeInitialize (): BOOLEAN;
procedure Bomber_BridgeRelease
();
procedure Bomber_BridgeDraw ();
procedure Bomber_BridgeBuild ();
procedure Bomber_BridgeCrashLeft ();
procedure Bomber_BridgeCrashRight ();
implementation
uses
bm_timer,
bm_model;
const
MODEL_BLOCK1 = 'bridone.tsm';
MODEL_BLOCK2 = 'bridtwo.tsm';
MODEL_SUPPORT = 'support.tsm';
type
BLOCK_STATE_T = record
model: INTEGER;
posX: SINGLE;
posY: SINGLE;
angl: SINGLE;
zangl: SINGLE;
fllspd: SINGLE;
rotspd: SINGLE;
end;
var
modelBlock1,
modelBlock2,
modelSupport: INTEGER;
bridge: ARRAY [0..7] OF BLOCK_STATE_T;
leftCrashed: BOOLEAN;
rightCrashed: BOOLEAN;
procedure Bomber_BridgeBuild ();
var
i: INTEGER;
begin
for i := 0 to 7 do
begin
if (i in [0, 3, 4, 7]) then
bridge [i].model := modelBlock1
else
bridge [i].model := modelBlock2;
bridge [i].posX := -13.75 * (4 - i) + 6.875;
bridge [i].posY := 0.0;
if (i in [3, 7]) then
bridge [i].angl := 180.0
else
bridge [i].angl := 0.0;
bridge [i].fllspd := 0;
bridge [i].zangl := 0.0;
end;
leftCrashed := FALSE;
rightCrashed := FALSE;
end;
function Bomber_BridgeInitialize (): BOOLEAN;
begin
modelBlock1 := Bomber_ModelLoad (MODEL_BLOCK1);
modelBlock2 := Bomber_ModelLoad (MODEL_BLOCK2);
modelSupport := Bomber_ModelLoad (MODEL_SUPPORT);
Bomber_BridgeBuild ();
if (modelBlock1 = -1)
or (modelBlock2 = -1)
or (modelSupport = -1) then
result := FALSE
else
result := TRUE;
end;
procedure Bomber_BridgeRelease ();
begin
end;
procedure Bomber_BridgeDraw ();
var
i: INTEGER;
begin
Bomber_ModelDraw (modelSupport, 0.0, -5.0, 0.0, 0.0, 0.0);
for i := 0 to 7 do
begin
Bomber_ModelDraw (bridge [i].model,
bridge [i].posX, bridge [i].posY, 0.0, bridge [i].angl, bridge [i].zangl);
bridge [i].posY := bridge [i].posY + bridge [i].fllspd / Bomber_TimerGetFps ();
if (bridge [i].posY <= -5.0) then
begin
bridge [i].posY := -5.0;
bridge [i].fllspd := 0.0;
end;
if (i in [0, 4]) then
bridge [i].zangl := bridge [i].zangl + bridge [i].rotspd / Bomber_TimerGetFps ()
else
bridge [i].zangl := bridge [i].zangl - bridge [i].rotspd / Bomber_TimerGetFps ();
if (abs (bridge [i].zangl) >= 45.0) then
begin
bridge [i].rotspd := 0.0;
end;
end;
end;
procedure Bomber_BridgeCrashLeft ();
begin
if not (leftCrashed) then
begin
leftCrashed := TRUE;
bridge [0].rotspd := -30 - random (30);
bridge [3].rotspd := 30 + random (30);
bridge [1].fllspd := -5 - random (5);
bridge [2].fllspd := -5 - random (5);
end;
end;
procedure Bomber_BridgeCrashRight ();
begin
if not (rightCrashed) then
begin
rightCrashed := TRUE;
bridge [4].rotspd := -30 - random (30);
bridge [7].rotspd := 30 + random (30);
bridge [5].fllspd := -5 - random (5);
bridge [6].fllspd := -5 - random (5);
end;
end;
end.
unit bm_plane;
interface
function Bomber_PlaneInitialize (): BOOLEAN;
function Bomber_PlaneDraw (): BOOLEAN;
procedure Bomber_PlaneRestart ();
implementation
uses
Windows,
OpenGL,
Math,
bm_core,
bm_timer,
bm_math,
bm_model,
bm_brid,
bm_expl,
bm_scene;
const
MODEL_BOMB = 'bomb.tsm';
MODEL_PLANE = 'biplane.tsm';
MODEL_PROP = 'prop.tsm';
type
BOMB_T = record
OnBoard: BOOLEAN;
end;
var
modelBomb: INTEGER;
modelPlane: INTEGER;
modelProp: INTEGER;
bomb: ARRAY [0..MAX_BOMB_COUNT] OF BOMB_T;
planeX: SINGLE;
planeY: SINGLE;
planeZ: SINGLE;
propAngle: SINGLE;
bombfall: SET OF BYTE;
procedure Bomber_PlaneRestart ();
var
i: INTEGER;
begin
planeX := 0.0;
planeY := 20.0;
planeZ := 150.0;
propAngle := 0.0;
bombfall := [];
for i := BOMB_COUNT+1 to 4 do
bombfall := bombfall + [i];
end;
function Bomber_PlaneInitialize (): BOOLEAN;
var
i: INTEGER;
begin
modelBomb := Bomber_ModelLoad (MODEL_BOMB);
modelPlane := Bomber_ModelLoad (MODEL_PLANE);
modelProp := Bomber_ModelLoad (MODEL_PROP);
if (modelBomb = -1)
or (modelPlane = -1)
or (modelProp = -1) then
result := FALSE
else
result := TRUE;
for i := 0 to MAX_BOMB_COUNT-1 do
bomb [i].OnBoard := TRUE;
Bomber_PlaneRestart ();
end;
procedure DropBomb (i: INTEGER);
var
a: INTEGER;
begin
a := Round (ALFA_VALUE / 10);
if (bombpos [a, BOMB_COUNT - i].crash) then
begin
if (bombpos [a, BOMB_COUNT - i].y > 0) then
Bomber_BridgeCrashLeft ()
else
Bomber_BridgeCrashRight ();
end;
Bomber_ExploEnable (BOMB_COUNT - i + 1);
bombfall := bombfall + [i];
end;
function Bomber_PlaneDraw (): BOOLEAN;
var
fps: SINGLE;
x, z: SINGLE;
alfa: SINGLE;
i: INTEGER;
begin
if (planeZ > -100.0) then
begin
for i := 1 to 4 do
if (planeZ <= -i*10)
and (not (i in bombfall)) then
DropBomb (i);
alfa := 90 + ALFA_VALUE;
x := planeX * cos (DegToRad (alfa)) - planeZ * sin (DegToRad (90-alfa));
z := planeX * sin (DegToRad (alfa)) + planeZ * cos (DegToRad (90-alfa));
Bomber_ModelDraw (modelPlane, x, planeY, z, alfa + 90, 0.0);
Bomber_ModelDraw (modelProp, x, planeY, z, alfa + 90, propAngle);
fps := Bomber_TimerGetFps ();
propAngle := propAngle + 720.0 / fps;
if (propAngle >= 360.0) then
propAngle := propAngle - 360.0;
planeZ := planez - 33.3 / fps;
if (planeZ < 25) then
planeY := planeY + 5.0 / fps;
result := TRUE
end
else
result := FALSE;
end;
end.
unit bm_math;
interface
const
MAX_BOMB_COUNT = 4;
MONTE_CARLO_ITERATIONS = 1000;
type
BOMB_POS_T = record
x, y: SINGLE;
crash: BOOLEAN;
end;
var
BOMB_COUNT: INTEGER = 4;
bombPos: ARRAY [0..9, 0..MAX_BOMB_COUNT-1] OF BOMB_POS_T;
crashProbab: ARRAY [0..9] OF SINGLE;
procedure Bomber_MathProcess ();
function GenerateRandomX (a, sigma: SINGLE): SINGLE;
implementation
uses
Windows,
Math,
bm_core;
const
DELTA_L = 10; // Интервал между бомбами в серии
S_X_PR = 25; // СКО ошибки прицеливания по X
S_Y_PR = 40; // СКО ошибки прицеливания по Y
S_X_T =
10; // Техническое рассеивание по X
S_Y_T = 5; // Техническое рассеивание по Y
var
holdrand: CARDINAL = 0;
function GenerateRandomX (a, sigma: SINGLE): SINGLE;
const
k = 10;
var
r1, r2, v1, v2, s, res: SINGLE;
n: INTEGER;
begin
res := 0;
for n := 1 to k do
begin
repeat
r1 := random (100) / 100;
r2 := random (100) / 100;
v1 := 2 * r1 - 1;
v2 := 2 * r2 - 1;
s := v1 * v1 + v2 * v2;
until (not ((s > 1) or (s = 0)));
res := res + (a + sigma * (v1 * sqrt ((-2 * log10 (s)) / s)));
end;
result := res / k;
end;
procedure Bomber_MathProcess ();
var
i, k: INTEGER;
xpr, ypr: SINGLE;
dx, dy: SINGLE;
x, y: SINGLE;
n, m: INTEGER;
f: BOOLEAN;
begin
holdrand := GetTickCount ();
ZeroMemory (@bombPos, sizeof (bombPos));
ZeroMemory (@crashProbab, sizeof (crashProbab));
for n := 1 to MONTE_CARLO_ITERATIONS do
for i := 0 to 9 do
begin
f := TRUE;
for k := 1 to BOMB_COUNT do
begin
xpr := GenerateRandomX (0, S_X_PR);
ypr := GenerateRandomX (0, S_Y_PR);
dx := GenerateRandomX (0, S_X_T);
dy := GenerateRandomX (0, S_Y_T);
bombPos [i, k-1].x := xpr + (((BOMB_COUNT-1) / 2) - k + 1) * DELTA_L + dx;
bombPos [i, k-1].y := ypr + dy;
x := bombPos [i, k-1].x * cos (DegToRad (i * 10))
- bombPos [i, k-1].y * sin (DegToRad (i * 10));
y := bombPos [i, k-1].x * sin (DegToRad (i * 10))
- bombPos [i, k-1].y * cos (DegToRad (i * 10));
bombPos [i, k-1].x := x;
bombPos [i, k-1].y := y;
bombPos [i, k-1].crash :=
(abs (x) <= BRIDGE_WIDTH div 2)
and (abs (y) <= BRIDGE_LENGTH div 2);
if (bombPos [i, k-1].crash) and (f) then
begin
crashProbab [i] := crashProbab [i] + 1;
f := FALSE;
end;
end;
end;
for i := 0 to 9 do
crashProbab [i] := crashProbab [i] / MONTE_CARLO_ITERATIONS;
end;
end.
unit bm_effct;
interface
function Bomber_EffectInitialize (): BOOLEAN;
procedure Bomber_EffectDraw ();
implementation
uses
OpenGL,
bm_core,
bm_rend;
const
LINES_COUNT = 2;
EFFECT_COUNT = 2;
type
LINE_T = record
xpos: SINGLE;
lifetime: INTEGER;
end;
var
line: ARRAY [0..LINES_COUNT-1] OF LINE_T;
function Bomber_EffectInitialize (): BOOLEAN;
var
i: INTEGER;
begin
for i := 0 to LINES_COUNT-1 do
begin
line [i].xpos := random (WINDOW_WIDTH);
line [i].lifetime := random (5);
end;
result := TRUE;
end;
procedure Bomber_EffectDraw ();
var
i: INTEGER;
x, y: SINGLE;
begin
Bomber_RendSet2dMode ();
glBegin (GL_LINES);
for i := 0 to LINES_COUNT-1 do
begin
glVertex2f (line [i].xpos, 0.0);
glVertex2f (line [i].xpos, WINDOW_HEIGHT);
line [i].xpos := line [i].xpos - 5 + random (10);
if (line [i].xpos < 0) then
line [i].xpos := WINDOW_WIDTH + line [i].xpos;
if (line [i].xpos > WINDOW_WIDTH) then
line [i].xpos := line [i].xpos - WINDOW_WIDTH;
dec (line [i].lifetime);
if (line [i].lifetime <= 0) then
begin
line [i].xpos := random (WINDOW_WIDTH);
line [i].lifetime := random (10);
end;
end;
glEnd ();
for i := 0 to EFFECT_COUNT-1 do
begin
x := random (WINDOW_WIDTH);
y
:= random (WINDOW_HEIGHT);
glBegin (GL_LINE_STRIP);
glVertex2f (x, y);
glVertex2f (x + random (15), y + random (15));
glVertex2f (x + random (15), y + random (15));
glVertex2f (x + random (15), y + random (15));
glEnd ();
end;
glBegin (GL_QUADS);
glVertex2f (WINDOW_WIDTH, 0);
glVertex2f (0, 0);
glVertex2f (0, WINDOW_HEIGHT / 10);
glVertex2f (WINDOW_WIDTH, WINDOW_HEIGHT / 10);
glVertex2f (WINDOW_WIDTH, WINDOW_HEIGHT - WINDOW_HEIGHT / 10);
glVertex2f (0, WINDOW_HEIGHT - WINDOW_HEIGHT / 10);
glVertex2f (0, WINDOW_HEIGHT);
glVertex2f (WINDOW_WIDTH, WINDOW_HEIGHT);
glEnd ();
Bomber_RendSet3dMode ();
end;
end.
unit bm_expl;
interface
function Bomber_ExploInitialize (): BOOLEAN;
procedure Bomber_ExploSetup (index: INTEGER);
procedure Bomber_ExploDraw ();
procedure Bomber_ExploEnable (bomb: BYTE);
implementation
uses
Windows,
OpenGL,
bm_timer,
bm_math;
const
MAX_PARTICLE_COUNT = 2000;
type
PARTICLE_T = record
enabled: BOOLEAN;
method: BYTE;
x, y, z: SINGLE;
sx, sy, sz: SINGLE;
end;
var
particle: ARRAY [1..MAX_BOMB_COUNT, 1..MAX_PARTICLE_COUNT] OF PARTICLE_T;
function Bomber_ExploInitialize (): BOOLEAN;
begin
ZeroMemory (@particle, sizeof (particle));
result := TRUE;
end;
procedure Bomber_ExploSetup (index: INTEGER);
var
b, i: INTEGER;
x, y: SINGLE;
begin
for b := 1 to MAX_BOMB_COUNT do
for i := 1 to MAX_PARTICLE_COUNT do
begin
x := bombPos [index, b-1].x;
y := bombPos [index, b-1].y;
if (abs (y) <= 50) and ((x > 5) or (x < -5)) then
particle [b, i].method := 1
else
particle [b, i].method := 2;
particle [b, i].enabled := FALSE;
particle [b, i].x := -y + (-30 + random (60)) / 7;
particle [b, i].z := -x + (-30 + random (60)) / 7;
if (particle [b, i].method = 1) then
begin
particle [b, i].y := -3.0;
particle [b, i].sx := 0;
particle [b, i].sy := random (750) / 10;
particle [b, i].sz := 0;
end;
if (particle [b, i].method = 2) then
begin
particle [b, i].y := 0;
particle [b, i].sx := -5 + random (10);
particle [b, i].sy := random (250) / 10;
particle [b, i].sz := -5 + random (10);
end;
end;
end;
procedure Bomber_ExploDraw ();
var
b, i: INTEGER;
begin
glPushAttrib (GL_ALL_ATTRIB_BITS);
glDisable (GL_LIGHTING);
glEnable (GL_BLEND);
glBlendFunc (GL_ONE, GL_SRC_ALPHA);
glBegin (GL_LINES);
for b := 1 to MAX_BOMB_COUNT do
for i := 1 to MAX_PARTICLE_COUNT do
if (particle [b, i].enabled) then
begin
if (particle [b, i].method = 1) then
begin
glColor4f (0.5, 0.5, 0.5, 0.25);
particle [b, i].y := particle [b, i].y + particle [b, i].sy / Bomber_TimerGetFps ();
particle [b, i].sy := particle [b, i].sy - 75 / Bomber_TimerGetFps ();
if (particle [b, i].y < -3.0) then
particle [b, i].enabled := FALSE
else
begin
glVertex3f (particle [b, i].x - 0.5, particle [b, i].y, particle [b, i].z);
glVertex3f (particle [b, i].x, particle [b, i].y, particle [b, i].z + 0.5);
end;
end;
if (particle [b, i].method = 2) then
begin
glColor4f (0.1, 0.1, 0.1, 0.25);
particle [b, i].x := particle [b, i].x + particle [b, i].sz / Bomber_TimerGetFps ();
particle [b, i].y := particle [b, i].y + particle [b, i].sy / Bomber_TimerGetFps ();
particle [b, i].z := particle [b, i].z + particle [b, i].sx / Bomber_TimerGetFps ();
particle [b, i].sy := particle [b, i].sy - 10 / Bomber_TimerGetFps ();
if (particle [b, i].y < 0.0) then
begin
particle [b, i].enabled := FALSE;
end
else
begin
glVertex3f (particle [b, i].x - 0.5, particle [b, i].y, particle [b, i].z);
glVertex3f (particle [b, i].x, particle [b, i].y, particle [b, i].z + 0.5);
end;
end;
end;
glEnd ();
glPopAttrib
();
end;
procedure Bomber_ExploEnable (bomb: BYTE);
var
i: INTEGER;
begin
for i := 1 to MAX_PARTICLE_COUNT do
particle [bomb, i].enabled := TRUE;
end;
end.