DEADSOFTWARE

render: use only r_render to access render
[d2df-sdl.git] / src / game / opengl / r_game.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../../shared/a_modes.inc}
16 unit r_game;
18 interface
20 procedure r_Game_Load;
21 procedure r_Game_Free;
23 procedure r_Game_LoadTextures;
24 procedure r_Game_FreeTextures;
26 procedure r_Game_Draw;
27 procedure r_Game_DrawLoadingStat;
28 procedure r_Game_DrawMenuBackground (tex: AnsiString);
30 procedure r_Game_SetupScreenSize;
32 var
33 gStdFont: DWORD;
35 implementation
37 uses
38 {$INCLUDE ../nogl/noGLuses.inc}
39 {$IFDEF ENABLE_HOLMES}
40 g_holmes,
41 {$ENDIF}
42 SysUtils, Classes, Math,
43 g_base, g_basic, r_graphics,
44 g_system, g_touch,
45 MAPDEF, xprofiler, utils, wadreader,
46 e_input, e_sound,
47 g_language, g_console, g_menu, g_triggers, g_player, g_options, g_monsters, g_map, g_panel,
48 g_items, g_weapons, g_gfx, g_phys, g_net, g_gui, g_netmaster,
49 g_game, r_console, r_gfx, r_items, r_map, r_monsters, r_weapons, r_netmaster, r_player, r_textures,
50 r_playermodel
51 ;
53 var
54 profileFrameDraw: TProfiler = nil;
56 FPS: Word;
57 FPSCounter: Word;
58 FPSTime: LongWord;
59 hasPBarGfx: Boolean;
61 BackID: DWORD = DWORD(-1);
62 gBackSize: TDFPoint;
64 procedure r_Game_Load;
65 var
66 wl, hl: Integer;
67 wr, hr: Integer;
68 wb, hb: Integer;
69 wm, hm: Integer;
70 begin
71 // early load
72 g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD + ':TEXTURES\TITLE', gTextureFilter);
73 g_Texture_CreateWADEx('INTER', GameWAD + ':TEXTURES\INTER', gTextureFilter);
74 g_Texture_CreateWADEx('ENDGAME_EN', GameWAD + ':TEXTURES\ENDGAME_EN', gTextureFilter);
75 g_Texture_CreateWADEx('ENDGAME_RU', GameWAD + ':TEXTURES\ENDGAME_RU', gTextureFilter);
76 LoadStdFont('STDTXT', 'STDFONT', gStdFont);
77 LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
78 LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
79 // game data
80 g_Texture_CreateWADEx('NOTEXTURE', GameWAD + ':TEXTURES\NOTEXTURE');
81 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD + ':TEXTURES\HUD');
82 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD + ':TEXTURES\AIRBAR');
83 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD + ':TEXTURES\JETBAR');
84 g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD + ':TEXTURES\HUDBG');
85 g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD + ':TEXTURES\ARMORHUD');
86 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD + ':TEXTURES\FLAGHUD_R_BASE');
87 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD + ':TEXTURES\FLAGHUD_R_STOLEN');
88 g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD + ':TEXTURES\FLAGHUD_R_DROP');
89 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD + ':TEXTURES\FLAGHUD_B_BASE');
90 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD + ':TEXTURES\FLAGHUD_B_STOLEN');
91 g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD + ':TEXTURES\FLAGHUD_B_DROP');
92 g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD + ':TEXTURES\TALKBUBBLE');
93 g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD + ':TEXTURES\PENTA');
94 g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD + ':TEXTURES\PLRIND');
95 // bar
96 hasPBarGfx := true;
97 if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false;
98 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false;
99 if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false;
100 if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false;
101 if hasPBarGfx then
102 begin
103 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
104 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
105 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
106 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
107 if (wl > 0) and (hl > 0) and (wr > 0) and (hr = hl) and (wb > 0) and (hb = hl) and (wm > 0) and (hm > 0) and (hm <= hl) then
108 begin
109 // yay!
110 end
111 else
112 hasPBarGfx := false;
113 end;
114 end;
116 procedure r_Game_Free;
117 begin
118 g_Texture_Delete('NOTEXTURE');
119 g_Texture_Delete('TEXTURE_PLAYER_HUD');
120 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
121 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
122 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
123 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
124 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
125 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
126 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
127 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
128 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
129 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
130 end;
132 procedure r_Game_SetupScreenSize;
133 const
134 RES_FACTOR = 4.0 / 3.0;
135 var
136 s: Single;
137 rf: Single;
138 bw, bh: Word;
139 begin
140 // Размер экранов игроков:
141 gPlayerScreenSize.X := gScreenWidth-196;
142 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
143 gPlayerScreenSize.Y := gScreenHeight div 2
144 else
145 gPlayerScreenSize.Y := gScreenHeight;
147 // Размер заднего плана:
148 if BackID <> DWORD(-1) then
149 begin
150 s := SKY_STRETCH;
151 if (gScreenWidth*s > gMapInfo.Width) or
152 (gScreenHeight*s > gMapInfo.Height) then
153 begin
154 gBackSize.X := gScreenWidth;
155 gBackSize.Y := gScreenHeight;
156 end
157 else
158 begin
159 e_GetTextureSize(BackID, @bw, @bh);
160 rf := Single(bw) / Single(bh);
161 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
162 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
163 s := Max(gScreenWidth / bw, gScreenHeight / bh);
164 if (s < 1.0) then s := 1.0;
165 gBackSize.X := Round(bw*s);
166 gBackSize.Y := Round(bh*s);
167 end;
168 end;
169 end;
171 procedure r_Game_LoadTextures;
172 begin
173 g_Texture_CreateWADEx('TEXTURE_endpic', EndPicPath, gTextureFilter);
174 if gMapInfo.SkyFullName <> '' then
175 g_Texture_CreateWAD(BackID, gMapInfo.SkyFullName, gTextureFilter);
176 r_Game_SetupScreenSize;
177 end;
179 procedure r_Game_FreeTextures;
180 begin
181 g_Texture_Delete('TEXTURE_endpic');
182 if BackID <> DWORD(-1) then
183 begin
184 gBackSize.X := 0;
185 gBackSize.Y := 0;
186 e_DeleteTexture(BackID);
187 BackID := DWORD(-1);
188 end
189 end;
191 procedure r_Map_DrawBack(dx, dy: Integer);
192 begin
193 if gDrawBackGround and (BackID <> DWORD(-1)) then
194 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
195 else
196 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
197 end;
199 function GetActivePlayer_ByID(ID: Integer): TPlayer;
200 var
201 a: Integer;
202 begin
203 Result := nil;
204 if ID < 0 then
205 Exit;
206 if gPlayers = nil then
207 Exit;
208 for a := Low(gPlayers) to High(gPlayers) do
209 if IsActivePlayer(gPlayers[a]) then
210 begin
211 if gPlayers[a].UID <> ID then
212 continue;
213 Result := gPlayers[a];
214 break;
215 end;
216 end;
218 function calcProfilesHeight (prof: TProfiler): Integer;
219 begin
220 result := 0;
221 if (prof = nil) then exit;
222 if (length(prof.bars) = 0) then exit;
223 result := length(prof.bars)*(16+2);
224 end;
226 // returns width
227 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
228 var
229 wdt, hgt: Integer;
230 yy: Integer;
231 ii: Integer;
232 begin
233 result := 0;
234 if (prof = nil) then exit;
235 // gScreenWidth
236 if (length(prof.bars) = 0) then exit;
237 wdt := 192;
238 hgt := calcProfilesHeight(prof);
239 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
240 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
241 // background
242 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
243 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
244 e_DarkenQuadWH(x, y, wdt, hgt, 150);
245 // title
246 yy := y+2;
247 for ii := 0 to High(prof.bars) do
248 begin
249 e_TextureFontPrintEx(x+2+4*prof.bars[ii].level, yy, Format('%s: %d', [prof.bars[ii].name, prof.bars[ii].value]), gStdFont, 255, 255, 0, 1, false);
250 Inc(yy, 16+2);
251 end;
252 result := wdt;
253 end;
255 procedure drawTime(X, Y: Integer); inline;
256 begin
257 e_TextureFontPrint(x, y, Format('%d:%.2d:%.2d', [gTime div 1000 div 3600, (gTime div 1000 div 60) mod 60, gTime div 1000 mod 60]), gStdFont);
258 end;
260 procedure DrawStat();
261 var
262 pc, x, y, w, h: Integer;
263 w1, w2, w3, w4: Integer;
264 a, aa: Integer;
265 cw, ch, r, g, b, rr, gg, bb: Byte;
266 s1, s2, s3: String;
267 _y: Integer;
268 stat: TPlayerStatArray;
269 wad, map: string;
270 mapstr: string;
271 namestr: string;
272 begin
273 s1 := '';
274 s2 := '';
275 s3 := '';
276 pc := g_Player_GetCount;
277 e_TextureFontGetSize(gStdFont, cw, ch);
279 w := gScreenWidth-(gScreenWidth div 5);
280 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
281 h := 32+ch*(11+pc)
282 else
283 h := 40+ch*5+(ch+8)*pc;
284 x := (gScreenWidth div 2)-(w div 2);
285 y := (gScreenHeight div 2)-(h div 2);
287 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
288 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
290 drawTime(x+w-78, y+8);
292 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
293 map := g_ExtractFileName(gMapInfo.Map);
294 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
296 case gGameSettings.GameMode of
297 GM_DM:
298 begin
299 if gGameSettings.MaxLives = 0 then
300 s1 := _lc[I_GAME_DM]
301 else
302 s1 := _lc[I_GAME_LMS];
303 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
304 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
305 end;
307 GM_TDM:
308 begin
309 if gGameSettings.MaxLives = 0 then
310 s1 := _lc[I_GAME_TDM]
311 else
312 s1 := _lc[I_GAME_TLMS];
313 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
314 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
315 end;
317 GM_CTF:
318 begin
319 s1 := _lc[I_GAME_CTF];
320 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
321 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
322 end;
324 GM_COOP:
325 begin
326 if gGameSettings.MaxLives = 0 then
327 s1 := _lc[I_GAME_COOP]
328 else
329 s1 := _lc[I_GAME_SURV];
330 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
331 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
332 end;
334 else
335 begin
336 s1 := '';
337 s2 := '';
338 end;
339 end;
340 _y := y+8;
341 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
342 _y := _y+ch+8;
343 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
344 _y := _y+ch+8;
345 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
347 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
348 gStdFont, 200, 200, 200, 1);
350 if NetMode = NET_SERVER then
351 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
352 else
353 if NetMode = NET_CLIENT then
354 e_TextureFontPrintEx(x+8, y + 8,
355 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
357 if pc = 0 then
358 Exit;
359 stat := g_Player_GetStats();
360 SortGameStat(stat);
362 w2 := (w-16) div 6 + 48; // ширина 2 столбца
363 w3 := (w-16) div 6; // ширина 3 и 4 столбцов
364 w4 := w3;
365 w1 := w-16-w2-w3-w4; // оставшееся пространство - для цвета и имени игрока
367 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
368 begin
369 _y := _y+ch+ch;
371 for a := TEAM_RED to TEAM_BLUE do
372 begin
373 if a = TEAM_RED then
374 begin
375 s1 := _lc[I_GAME_TEAM_RED];
376 r := 255;
377 g := 0;
378 b := 0;
379 end
380 else
381 begin
382 s1 := _lc[I_GAME_TEAM_BLUE];
383 r := 0;
384 g := 0;
385 b := 255;
386 end;
388 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
389 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
390 gStdFont, r, g, b, 1);
392 _y := _y+ch+(ch div 4);
393 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
394 _y := _y+(ch div 4);
396 for aa := 0 to High(stat) do
397 if stat[aa].Team = a then
398 with stat[aa] do
399 begin
400 if Spectator then
401 begin
402 rr := r div 2;
403 gg := g div 2;
404 bb := b div 2;
405 end
406 else
407 begin
408 rr := r;
409 gg := g;
410 bb := b;
411 end;
412 if gShowPIDs then
413 namestr := Format('[%5d] %s', [UID, Name])
414 else
415 namestr := Name;
416 // Имя
417 e_TextureFontPrintEx(x+16, _y, namestr, gStdFont, rr, gg, bb, 1);
418 // Пинг/потери
419 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
420 // Фраги
421 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
422 // Смерти
423 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
424 _y := _y+ch;
425 end;
427 _y := _y+ch;
428 end;
429 end
430 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
431 begin
432 _y := _y+ch+ch;
433 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
434 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
435 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
436 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
438 _y := _y+ch+8;
439 for aa := 0 to High(stat) do
440 with stat[aa] do
441 begin
442 if Spectator then
443 begin
444 r := 127;
445 g := 64;
446 end
447 else
448 begin
449 r := 255;
450 g := 127;
451 end;
452 if gShowPIDs then
453 namestr := Format('[%5d] %s', [UID, Name])
454 else
455 namestr := Name;
456 // Цвет игрока
457 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
458 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
459 // Имя
460 e_TextureFontPrintEx(x+16+16+8, _y+4, namestr, gStdFont, r, g, 0, 1);
461 // Пинг/потери
462 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
463 // Фраги
464 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
465 // Смерти
466 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
467 _y := _y+ch+8;
468 end;
469 end
470 end;
472 procedure DrawCustomStat();
473 var
474 pc, x, y, w, _y,
475 w1, w2, w3,
476 t, p, m: Integer;
477 ww1, hh1: Word;
478 ww2, hh2, r, g, b, rr, gg, bb: Byte;
479 s1, s2, topstr: String;
480 begin
481 e_TextureFontGetSize(gStdFont, ww2, hh2);
483 sys_HandleInput;
485 if g_Console_Action(ACTION_SCORES) then
486 begin
487 if not gStatsPressed then
488 begin
489 gStatsOff := not gStatsOff;
490 gStatsPressed := True;
491 end;
492 end
493 else
494 gStatsPressed := False;
496 if gStatsOff then
497 begin
498 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
499 w := (Length(s1) * ww2) div 2;
500 x := gScreenWidth div 2 - w;
501 y := 8;
502 e_TextureFontPrint(x, y, s1, gStdFont);
503 Exit;
504 end;
506 if (gGameSettings.GameMode = GM_COOP) then
507 begin
508 if gMissionFailed then
509 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
510 else
511 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
512 end
513 else
514 topstr := _lc[I_MENU_INTER_ROUND_OVER];
516 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
517 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
519 if g_Game_IsNet then
520 begin
521 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
522 if not gChatShow then
523 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
524 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
525 end;
527 if g_Game_IsClient then
528 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
529 else
530 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
531 if not gChatShow then
532 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
533 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
535 x := 32;
536 y := 16+hh1+16;
538 w := gScreenWidth-x*2;
540 w2 := (w-16) div 6;
541 w3 := w2;
542 w1 := w-16-w2-w3;
544 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
545 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
547 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
549 case CustomStat.GameMode of
550 GM_DM:
551 begin
552 if gGameSettings.MaxLives = 0 then
553 s1 := _lc[I_GAME_DM]
554 else
555 s1 := _lc[I_GAME_LMS];
556 end;
557 GM_TDM:
558 begin
559 if gGameSettings.MaxLives = 0 then
560 s1 := _lc[I_GAME_TDM]
561 else
562 s1 := _lc[I_GAME_TLMS];
563 end;
564 GM_CTF: s1 := _lc[I_GAME_CTF];
565 GM_COOP:
566 begin
567 if gGameSettings.MaxLives = 0 then
568 s1 := _lc[I_GAME_COOP]
569 else
570 s1 := _lc[I_GAME_SURV];
571 end;
572 else s1 := '';
573 end;
575 _y := y+16;
576 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
577 _y := _y+8;
579 _y := _y+16;
580 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
581 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
583 _y := _y+16;
584 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
585 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
586 (CustomStat.GameTime div 1000 div 60) mod 60,
587 CustomStat.GameTime div 1000 mod 60]), gStdFont);
589 pc := Length(CustomStat.PlayerStat);
590 if pc = 0 then Exit;
592 if CustomStat.GameMode = GM_COOP then
593 begin
594 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
595 _y := _y+32;
596 s2 := _lc[I_GAME_MONSTERS];
597 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
598 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
599 _y := _y+16;
600 s2 := _lc[I_GAME_SECRETS];
601 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
602 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
603 if gLastMap then
604 begin
605 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
606 _y := _y-16;
607 s2 := _lc[I_GAME_MONSTERS_TOTAL];
608 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
609 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
610 _y := _y+16;
611 s2 := _lc[I_GAME_SECRETS_TOTAL];
612 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
613 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
614 end;
615 end;
617 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
618 begin
619 _y := _y+16+16;
621 with CustomStat do
622 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
623 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
624 else s1 := _lc[I_GAME_WIN_DRAW];
626 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
627 _y := _y+40;
629 for t := TEAM_RED to TEAM_BLUE do
630 begin
631 if t = TEAM_RED then
632 begin
633 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
634 gStdFont, 255, 0, 0, 1);
635 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
636 gStdFont, 255, 0, 0, 1);
637 r := 255;
638 g := 0;
639 b := 0;
640 end
641 else
642 begin
643 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
644 gStdFont, 0, 0, 255, 1);
645 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
646 gStdFont, 0, 0, 255, 1);
647 r := 0;
648 g := 0;
649 b := 255;
650 end;
652 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
653 _y := _y+24;
655 for p := 0 to High(CustomStat.PlayerStat) do
656 if CustomStat.PlayerStat[p].Team = t then
657 with CustomStat.PlayerStat[p] do
658 begin
659 if Spectator then
660 begin
661 rr := r div 2;
662 gg := g div 2;
663 bb := b div 2;
664 end
665 else
666 begin
667 rr := r;
668 gg := g;
669 bb := b;
670 end;
671 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
672 e_TextureFontPrintEx(x+16, _y, Name + ' *', gStdFont, rr, gg, bb, 1)
673 else
674 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
675 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
676 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
677 _y := _y+24;
678 end;
680 _y := _y+16+16;
681 end;
682 end
683 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
684 begin
685 _y := _y+40;
686 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
687 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
688 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
690 _y := _y+24;
691 for p := 0 to High(CustomStat.PlayerStat) do
692 with CustomStat.PlayerStat[p] do
693 begin
694 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
696 if Spectator then
697 r := 127
698 else
699 r := 255;
701 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
702 e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True)
703 else
704 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
705 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
706 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
707 _y := _y+24;
708 end;
709 end;
711 // HACK: take stats screenshot immediately after the first frame of the stats showing
712 if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then
713 begin
714 {$IFNDEF HEADLESS}
715 g_TakeScreenShot('stats/' + StatFilename);
716 {$ENDIF}
717 StatShotDone := True;
718 end;
719 end;
721 procedure DrawSingleStat();
722 var
723 tm, key_x, val_x, y: Integer;
724 w1, w2, h: Word;
725 s1, s2: String;
727 procedure player_stat(n: Integer);
728 var
729 kpm: Real;
731 begin
732 // "Kills: # / #":
733 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
734 s2 := Format(' %d', [gTotalMonsters]);
736 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
737 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
738 e_CharFont_GetSize(gMenuFont, s1, w1, h);
739 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
740 s1 := s1 + '/';
741 e_CharFont_GetSize(gMenuFont, s1, w1, h);
742 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
744 // "Kills-per-minute: ##.#":
745 s1 := _lc[I_MENU_INTER_KPM];
746 if tm > 0 then
747 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
748 else
749 kpm := SingleStat.PlayerStat[n].Kills;
750 s2 := Format(' %.1f', [kpm]);
752 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
753 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
755 // "Secrets found: # / #":
756 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
757 s2 := Format(' %d', [SingleStat.TotalSecrets]);
759 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
760 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
761 e_CharFont_GetSize(gMenuFont, s1, w1, h);
762 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
763 s1 := s1 + '/';
764 e_CharFont_GetSize(gMenuFont, s1, w1, h);
765 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
766 end;
768 begin
769 // "Level Complete":
770 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
771 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
773 // Определяем координаты выравнивания по самой длинной строке:
774 s1 := _lc[I_MENU_INTER_KPM];
775 e_CharFont_GetSize(gMenuFont, s1, w1, h);
776 Inc(w1, 16);
777 s1 := ' 9999.9';
778 e_CharFont_GetSize(gMenuFont, s1, w2, h);
780 key_x := (gScreenWidth-w1-w2) div 2;
781 val_x := key_x + w1;
783 // "Time: #:##:##":
784 tm := SingleStat.GameTime div 1000;
785 s1 := _lc[I_MENU_INTER_TIME];
786 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
788 e_CharFont_Print(gMenuFont, key_x, 80, s1);
789 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
791 if SingleStat.TwoPlayers then
792 begin
793 // "Player 1":
794 s1 := _lc[I_MENU_PLAYER_1];
795 e_CharFont_GetSize(gMenuFont, s1, w1, h);
796 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
798 // Статистика первого игрока:
799 y := 176;
800 player_stat(0);
802 // "Player 2":
803 s1 := _lc[I_MENU_PLAYER_2];
804 e_CharFont_GetSize(gMenuFont, s1, w1, h);
805 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
807 // Статистика второго игрока:
808 y := 336;
809 player_stat(1);
810 end
811 else
812 begin
813 // Статистика первого игрока:
814 y := 128;
815 player_stat(0);
816 end;
817 end;
819 procedure r_Game_DrawLoadingStat;
820 procedure drawRect (x, y, w, h: Integer);
821 begin
822 if (w < 1) or (h < 1) then exit;
823 glBegin(GL_QUADS);
824 glVertex2f(x+0.375, y+0.375);
825 glVertex2f(x+w+0.375, y+0.375);
826 glVertex2f(x+w+0.375, y+h+0.375);
827 glVertex2f(x+0.375, y+h+0.375);
828 glEnd();
829 end;
831 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
832 var
833 rectW, rectH: Integer;
834 x0, y0: Integer;
835 wdt: Integer;
836 wl, hl: Integer;
837 wr, hr: Integer;
838 wb, hb: Integer;
839 wm, hm: Integer;
840 idl, idr, idb, idm: LongWord;
841 f, my: Integer;
842 begin
843 result := false;
844 if (total < 1) then exit;
845 if (cur < 1) then exit; // don't blink
846 if (not washere) and (cur >= total) then exit; // don't blink
847 //if (cur < 0) then cur := 0;
848 //if (cur > total) then cur := total;
849 result := true;
851 if (hasPBarGfx) then
852 begin
853 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
854 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
855 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
856 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
857 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
858 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
859 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
860 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
862 //rectW := gScreenWidth-360;
863 rectW := trunc(624.0*gScreenWidth/1024.0);
864 rectH := hl;
866 x0 := (gScreenWidth-rectW) div 2;
867 y0 := gScreenHeight-rectH-64;
868 if (y0 < 2) then y0 := 2;
870 glEnable(GL_SCISSOR_TEST);
872 // left and right
873 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
874 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
875 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
877 // body
878 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
879 f := x0+wl;
880 while (f < x0+rectW) do
881 begin
882 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
883 f += wb;
884 end;
886 // filled part
887 wdt := (rectW-wl-wr)*cur div total;
888 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
889 if (wdt > 0) then
890 begin
891 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
892 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
893 f := x0+wl;
894 while (wdt > 0) do
895 begin
896 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
897 f += wm;
898 wdt -= wm;
899 end;
900 end;
902 glScissor(0, 0, gScreenWidth, gScreenHeight);
903 end
904 else
905 begin
906 rectW := gScreenWidth-64;
907 rectH := 16;
909 x0 := (gScreenWidth-rectW) div 2;
910 y0 := gScreenHeight-rectH-64;
911 if (y0 < 2) then y0 := 2;
913 glDisable(GL_BLEND);
914 glDisable(GL_TEXTURE_2D);
916 //glClearColor(0, 0, 0, 0);
917 //glClear(GL_COLOR_BUFFER_BIT);
919 glColor4ub(127, 127, 127, 255);
920 drawRect(x0-2, y0-2, rectW+4, rectH+4);
922 glColor4ub(0, 0, 0, 255);
923 drawRect(x0-1, y0-1, rectW+2, rectH+2);
925 glColor4ub(127, 127, 127, 255);
926 wdt := rectW*cur div total;
927 if (wdt > rectW) then wdt := rectW;
928 drawRect(x0, y0, wdt, rectH);
929 end;
930 end;
932 var
933 ww, hh: Word;
934 xx, yy, i: Integer;
935 s: String;
936 begin
937 if (Length(LoadingStat.Msgs) = 0) then exit;
939 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
940 yy := (gScreenHeight div 3);
941 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
942 xx := (gScreenWidth div 3);
944 with LoadingStat do
945 begin
946 for i := 0 to NextMsg-1 do
947 begin
948 if (i = (NextMsg-1)) and (MaxValue > 0) then
949 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
950 else
951 s := Msgs[i];
953 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
954 yy := yy + LOADING_INTERLINE;
955 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
956 end;
957 end;
958 end;
960 procedure r_Game_DrawMenuBackground (tex: AnsiString);
961 var
962 w, h: Word;
963 ID: DWord;
965 begin
966 if g_Texture_Get(tex, ID) then
967 begin
968 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
969 e_GetTextureSize(ID, @w, @h);
970 if w = h then
971 w := round(w * 1.333 * (gScreenHeight / h))
972 else
973 w := trunc(w * (gScreenHeight / h));
974 e_DrawSize(ID, (gScreenWidth - w) div 2, 0, 0, False, False, w, gScreenHeight);
975 end
976 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
977 end;
979 procedure DrawMinimap(p: TPlayer; RenderRect: TRect);
980 var
981 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
983 function monDraw (mon: TMonster): Boolean;
984 begin
985 result := false; // don't stop
986 with mon do
987 begin
988 if alive then
989 begin
990 // Левый верхний угол
991 aX := Obj.X div ScaleSz + 1;
992 aY := Obj.Y div ScaleSz + 1;
993 // Размеры
994 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
995 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
996 // Правый нижний угол
997 aX2 := aX + aX2 - 1;
998 aY2 := aY + aY2 - 1;
999 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
1000 end;
1001 end;
1002 end;
1004 begin
1005 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
1006 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
1007 begin
1008 Scale := 1;
1009 // Сколько пикселов карты в 1 пикселе мини-карты:
1010 ScaleSz := 16 div Scale;
1011 // Размеры мини-карты:
1012 aX := max(gMapInfo.Width div ScaleSz, 1);
1013 aY := max(gMapInfo.Height div ScaleSz, 1);
1014 // Рамка карты:
1015 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
1017 if gWalls <> nil then
1018 begin
1019 // Рисуем стены:
1020 for a := 0 to High(gWalls) do
1021 with gWalls[a] do
1022 if PanelType <> 0 then
1023 begin
1024 // Левый верхний угол:
1025 aX := X div ScaleSz;
1026 aY := Y div ScaleSz;
1027 // Размеры:
1028 aX2 := max(Width div ScaleSz, 1);
1029 aY2 := max(Height div ScaleSz, 1);
1030 // Правый нижний угол:
1031 aX2 := aX + aX2 - 1;
1032 aY2 := aY + aY2 - 1;
1034 case PanelType of
1035 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
1036 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
1037 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
1038 end;
1039 end;
1040 end;
1041 if gSteps <> nil then
1042 begin
1043 // Рисуем ступени:
1044 for a := 0 to High(gSteps) do
1045 with gSteps[a] do
1046 if PanelType <> 0 then
1047 begin
1048 // Левый верхний угол:
1049 aX := X div ScaleSz;
1050 aY := Y div ScaleSz;
1051 // Размеры:
1052 aX2 := max(Width div ScaleSz, 1);
1053 aY2 := max(Height div ScaleSz, 1);
1054 // Правый нижний угол:
1055 aX2 := aX + aX2 - 1;
1056 aY2 := aY + aY2 - 1;
1058 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
1059 end;
1060 end;
1061 if gLifts <> nil then
1062 begin
1063 // Рисуем лифты:
1064 for a := 0 to High(gLifts) do
1065 with gLifts[a] do
1066 if PanelType <> 0 then
1067 begin
1068 // Левый верхний угол:
1069 aX := X div ScaleSz;
1070 aY := Y div ScaleSz;
1071 // Размеры:
1072 aX2 := max(Width div ScaleSz, 1);
1073 aY2 := max(Height div ScaleSz, 1);
1074 // Правый нижний угол:
1075 aX2 := aX + aX2 - 1;
1076 aY2 := aY + aY2 - 1;
1078 case LiftType of
1079 LIFTTYPE_UP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
1080 LIFTTYPE_DOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
1081 LIFTTYPE_LEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
1082 LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
1083 end;
1084 end;
1085 end;
1086 if gWater <> nil then
1087 begin
1088 // Рисуем воду:
1089 for a := 0 to High(gWater) do
1090 with gWater[a] do
1091 if PanelType <> 0 then
1092 begin
1093 // Левый верхний угол:
1094 aX := X div ScaleSz;
1095 aY := Y div ScaleSz;
1096 // Размеры:
1097 aX2 := max(Width div ScaleSz, 1);
1098 aY2 := max(Height div ScaleSz, 1);
1099 // Правый нижний угол:
1100 aX2 := aX + aX2 - 1;
1101 aY2 := aY + aY2 - 1;
1103 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
1104 end;
1105 end;
1106 if gAcid1 <> nil then
1107 begin
1108 // Рисуем кислоту 1:
1109 for a := 0 to High(gAcid1) do
1110 with gAcid1[a] do
1111 if PanelType <> 0 then
1112 begin
1113 // Левый верхний угол:
1114 aX := X div ScaleSz;
1115 aY := Y div ScaleSz;
1116 // Размеры:
1117 aX2 := max(Width div ScaleSz, 1);
1118 aY2 := max(Height div ScaleSz, 1);
1119 // Правый нижний угол:
1120 aX2 := aX + aX2 - 1;
1121 aY2 := aY + aY2 - 1;
1123 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
1124 end;
1125 end;
1126 if gAcid2 <> nil then
1127 begin
1128 // Рисуем кислоту 2:
1129 for a := 0 to High(gAcid2) do
1130 with gAcid2[a] do
1131 if PanelType <> 0 then
1132 begin
1133 // Левый верхний угол:
1134 aX := X div ScaleSz;
1135 aY := Y div ScaleSz;
1136 // Размеры:
1137 aX2 := max(Width div ScaleSz, 1);
1138 aY2 := max(Height div ScaleSz, 1);
1139 // Правый нижний угол:
1140 aX2 := aX + aX2 - 1;
1141 aY2 := aY + aY2 - 1;
1143 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
1144 end;
1145 end;
1146 if gPlayers <> nil then
1147 begin
1148 // Рисуем игроков:
1149 for a := 0 to High(gPlayers) do
1150 if gPlayers[a] <> nil then with gPlayers[a] do
1151 if alive then begin
1152 // Левый верхний угол:
1153 aX := Obj.X div ScaleSz + 1;
1154 aY := Obj.Y div ScaleSz + 1;
1155 // Размеры:
1156 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
1157 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
1158 // Правый нижний угол:
1159 aX2 := aX + aX2 - 1;
1160 aY2 := aY + aY2 - 1;
1162 if gPlayers[a] = p then
1163 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
1164 else
1165 case Team of
1166 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
1167 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
1168 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
1169 end;
1170 end;
1171 end;
1172 // Рисуем монстров
1173 g_Mons_ForEach(monDraw);
1174 end;
1175 end;
1178 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
1179 begin
1180 if not hasAmbient then exit;
1181 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
1182 end;
1184 // ////////////////////////////////////////////////////////////////////////// //
1185 var
1186 ltexid: GLuint = 0;
1188 function g_Texture_Light (): Integer;
1189 const
1190 Radius: Integer = 128;
1191 var
1192 tex, tpp: PByte;
1193 x, y, a: Integer;
1194 dist: Double;
1195 begin
1196 if ltexid = 0 then
1197 begin
1198 GetMem(tex, (Radius*2)*(Radius*2)*4);
1199 tpp := tex;
1200 for y := 0 to Radius*2-1 do
1201 begin
1202 for x := 0 to Radius*2-1 do
1203 begin
1204 dist := 1.0-sqrt((x-Radius)*(x-Radius)+(y-Radius)*(y-Radius))/Radius;
1205 if (dist < 0) then
1206 begin
1207 tpp^ := 0; Inc(tpp);
1208 tpp^ := 0; Inc(tpp);
1209 tpp^ := 0; Inc(tpp);
1210 tpp^ := 0; Inc(tpp);
1211 end
1212 else
1213 begin
1214 //tc.setPixel(x, y, Color(cast(int)(dist*255), cast(int)(dist*255), cast(int)(dist*255)));
1215 if (dist > 0.5) then dist := 0.5;
1216 a := round(dist*255);
1217 if (a < 0) then a := 0 else if (a > 255) then a := 255;
1218 tpp^ := 255; Inc(tpp);
1219 tpp^ := 255; Inc(tpp);
1220 tpp^ := 255; Inc(tpp);
1221 tpp^ := Byte(a); Inc(tpp);
1222 end;
1223 end;
1224 end;
1226 glGenTextures(1, @ltexid);
1227 //if (tid == 0) assert(0, "VGL: can't create screen texture");
1229 glBindTexture(GL_TEXTURE_2D, ltexid);
1230 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1231 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1232 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1233 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1235 //GLfloat[4] bclr = 0.0;
1236 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
1238 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Radius*2, Radius*2, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
1239 end;
1241 result := ltexid;
1242 end;
1244 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
1245 //FIXME: broken for splitscreen mode
1246 procedure renderDynLightsInternal ();
1247 var
1248 //hasAmbient: Boolean;
1249 //ambColor: TDFColor;
1250 lln: Integer;
1251 lx, ly, lrad: Integer;
1252 scxywh: array[0..3] of GLint;
1253 wassc: Boolean;
1254 begin
1255 if e_NoGraphics then exit;
1257 //TODO: lights should be in separate grid, i think
1258 // but on the other side: grid may be slower for dynlights, as their lifetime is short
1259 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
1261 // rendering mode
1262 //ambColor := gCurrentMap['light_ambient'].rgba;
1263 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
1265 { // this will multiply incoming color to alpha from framebuffer
1266 glEnable(GL_BLEND);
1267 glBlendFunc(GL_DST_ALPHA, GL_ONE);
1270 (*
1271 * light rendering: (INVALID!)
1272 * glStencilFunc(GL_EQUAL, 0, $ff);
1273 * for each light:
1274 * glClear(GL_STENCIL_BUFFER_BIT);
1275 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
1276 * draw shadow volume into stencil buffer
1277 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1278 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
1279 * turn off blending
1280 * draw color-less quad with light alpha (WARNING! don't touch color!)
1281 * glEnable(GL_BLEND);
1282 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
1283 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
1284 *)
1285 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
1286 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
1288 // setup OpenGL parameters
1289 glStencilMask($FFFFFFFF);
1290 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
1291 glEnable(GL_STENCIL_TEST);
1292 glEnable(GL_SCISSOR_TEST);
1293 glClear(GL_STENCIL_BUFFER_BIT);
1294 glStencilFunc(GL_EQUAL, 0, $ff);
1296 for lln := 0 to g_dynLightCount-1 do
1297 begin
1298 lx := g_dynLights[lln].x;
1299 ly := g_dynLights[lln].y;
1300 lrad := g_dynLights[lln].radius;
1301 if (lrad < 3) then continue;
1303 if (lx-sX+lrad < 0) then continue;
1304 if (ly-sY+lrad < 0) then continue;
1305 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
1306 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
1308 // set scissor to optimize drawing
1309 if (g_dbg_scale = 1.0) then
1310 begin
1311 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
1312 end
1313 else
1314 begin
1315 glScissor(0, 0, gScreenWidth, gScreenHeight);
1316 end;
1317 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
1318 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
1319 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
1320 // draw extruded panels
1321 glDisable(GL_TEXTURE_2D);
1322 glDisable(GL_BLEND);
1323 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
1324 if (lrad > 4) then r_Map_DrawPanelShadowVolumes(lx, ly, lrad);
1325 // render light texture
1326 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1327 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
1328 // blend it
1329 glEnable(GL_BLEND);
1330 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1331 glEnable(GL_TEXTURE_2D);
1332 // color and opacity
1333 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
1334 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
1335 glBegin(GL_QUADS);
1336 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
1337 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
1338 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
1339 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
1340 glEnd();
1341 end;
1343 // done
1344 glDisable(GL_STENCIL_TEST);
1345 glDisable(GL_BLEND);
1346 glDisable(GL_SCISSOR_TEST);
1347 //glScissor(0, 0, sWidth, sHeight);
1349 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
1350 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
1351 end;
1354 function fixViewportForScale (): Boolean;
1355 var
1356 nx0, ny0, nw, nh: Integer;
1357 begin
1358 result := false;
1359 if (g_dbg_scale <> 1.0) then
1360 begin
1361 result := true;
1362 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
1363 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
1364 nw := round(sWidth/g_dbg_scale);
1365 nh := round(sHeight/g_dbg_scale);
1366 sX := nx0;
1367 sY := ny0;
1368 sWidth := nw;
1369 sHeight := nh;
1370 end;
1371 end;
1374 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
1375 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
1376 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
1377 type
1378 TDrawCB = procedure ();
1380 var
1381 hasAmbient: Boolean;
1382 ambColor: TDFColor;
1383 doAmbient: Boolean = false;
1385 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
1386 var
1387 tagmask: Integer;
1388 pan: TPanel;
1389 begin
1390 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
1391 if gdbg_map_use_accel_render then
1392 begin
1393 tagmask := panelTypeToTag(panType);
1394 while (gDrawPanelList.count > 0) do
1395 begin
1396 pan := TPanel(gDrawPanelList.front());
1397 if ((pan.tag and tagmask) = 0) then break;
1398 if doDraw then r_Panel_Draw(pan, doAmbient, ambColor);
1399 gDrawPanelList.popFront();
1400 end;
1401 end
1402 else
1403 begin
1404 if doDraw then r_Map_DrawPanels(panType, hasAmbient, ambColor);
1405 end;
1406 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1407 end;
1409 procedure drawOther (profname: AnsiString; cb: TDrawCB);
1410 begin
1411 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
1412 if assigned(cb) then cb();
1413 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1414 end;
1416 begin
1417 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
1419 // our accelerated renderer will collect all panels to gDrawPanelList
1420 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
1421 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
1422 if gdbg_map_use_accel_render then
1423 begin
1424 r_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
1425 end;
1426 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1428 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
1429 r_Map_DrawBack(backXOfs, backYOfs);
1430 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1432 if setTransMatrix then
1433 begin
1434 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
1435 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
1436 glTranslatef(-sX, -sY, 0);
1437 end;
1439 // rendering mode
1440 ambColor := gCurrentMap['light_ambient'].rgba;
1441 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
1444 if hasAmbient then
1445 begin
1446 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
1447 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
1448 glClear(GL_COLOR_BUFFER_BIT);
1449 end;
1451 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
1454 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
1455 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
1456 drawOther('items', @r_Items_Draw);
1457 drawOther('weapons', @r_Weapon_Draw);
1458 drawOther('shells', @r_Player_DrawShells);
1459 drawOther('drawall', @r_Player_DrawAll);
1460 drawOther('gibs', @r_PlayerModel_DrawGibs);
1461 drawOther('corpses', @r_Player_DrawCorpses);
1462 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
1463 drawOther('monsters', @r_Monsters_Draw);
1464 drawOther('itemdrop', @r_Items_DrawDrop);
1465 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
1466 drawOther('gfx', @r_GFX_Draw);
1467 drawOther('flags', @r_Map_DrawFlags);
1468 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
1469 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
1470 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
1471 drawOther('dynlights', @renderDynLightsInternal);
1473 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
1474 begin
1475 renderAmbientQuad(hasAmbient, ambColor);
1476 end;
1478 doAmbient := true;
1479 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
1482 if g_debug_HealthBar then
1483 begin
1484 r_Monsters_DrawHealth();
1485 r_Player_DrawHealth();
1486 end;
1488 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
1489 end;
1492 procedure DrawMapView(x, y, w, h: Integer);
1494 var
1495 bx, by: Integer;
1496 begin
1497 glPushMatrix();
1499 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
1500 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
1502 sX := x;
1503 sY := y;
1504 sWidth := w;
1505 sHeight := h;
1507 fixViewportForScale();
1508 renderMapInternal(-bx, -by, true);
1510 glPopMatrix();
1511 end;
1514 procedure DrawPlayer(p: TPlayer);
1515 var
1516 px, py, a, b, c, d, i, fX, fY: Integer;
1517 camObj: TObj;
1518 //R: TRect;
1519 begin
1520 if (p = nil) or (p.FDummy) then
1521 begin
1522 glPushMatrix();
1523 r_Map_DrawBack(0, 0);
1524 glPopMatrix();
1525 Exit;
1526 end;
1528 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
1529 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
1531 gPlayerDrawn := p;
1533 glPushMatrix();
1535 camObj := p.getCameraObj();
1536 camObj.lerp(gLerpFactor, fX, fY);
1537 px := fX + PLAYER_RECT_CX;
1538 py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor);
1540 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
1541 begin
1542 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
1543 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
1545 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
1546 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
1548 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
1549 else if (gMapInfo.Width < gPlayerScreenSize.X) then
1550 begin
1551 // hcenter
1552 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
1553 end;
1555 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
1556 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
1557 begin
1558 // vcenter
1559 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
1560 end;
1561 end
1562 else
1563 begin
1564 // scaled, ignore level bounds
1565 a := -px+(gPlayerScreenSize.X div 2);
1566 b := -py+(gPlayerScreenSize.Y div 2);
1567 end;
1569 sX := -a;
1570 sY := -b;
1571 sWidth := gPlayerScreenSize.X;
1572 sHeight := gPlayerScreenSize.Y;
1573 fixViewportForScale();
1575 i := py - (sY + sHeight div 2);
1576 if (p.IncCam > 0) then
1577 begin
1578 // clamp to level bounds
1579 if (sY - p.IncCam < 0) then
1580 p.IncCam := nclamp(sY, 0, 120);
1581 // clamp around player position
1582 if (i > 0) then
1583 p.IncCam := nclamp(p.IncCam, 0, max(0, 120 - i));
1584 end
1585 else if (p.IncCam < 0) then
1586 begin
1587 // clamp to level bounds
1588 if (sY + sHeight - p.IncCam > gMapInfo.Height) then
1589 p.IncCam := nclamp(sY + sHeight - gMapInfo.Height, -120, 0);
1590 // clamp around player position
1591 if (i < 0) then
1592 p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0);
1593 end;
1595 sY := sY - nlerp(p.IncCamOld, p.IncCam, gLerpFactor);
1597 if (not g_dbg_ignore_bounds) then
1598 begin
1599 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
1600 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
1601 if (sX < 0) then sX := 0;
1602 if (sY < 0) then sY := 0;
1603 end;
1605 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
1606 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
1608 //r_smallmap_h: 0: left; 1: center; 2: right
1609 //r_smallmap_v: 0: top; 1: center; 2: bottom
1610 // horiz small map?
1611 if (gMapInfo.Width = sWidth) then
1612 begin
1613 sX := 0;
1614 end
1615 else if (gMapInfo.Width < sWidth) then
1616 begin
1617 case r_smallmap_h of
1618 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
1619 2: sX := -(sWidth-gMapInfo.Width); // right
1620 else sX := 0; // left
1621 end;
1622 end;
1623 // vert small map?
1624 if (gMapInfo.Height = sHeight) then
1625 begin
1626 sY := 0;
1627 end
1628 else if (gMapInfo.Height < sHeight) then
1629 begin
1630 case r_smallmap_v of
1631 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
1632 2: sY := -(sHeight-gMapInfo.Height); // bottom
1633 else sY := 0; // top
1634 end;
1635 end;
1637 p.viewPortX := sX;
1638 p.viewPortY := sY;
1639 p.viewPortW := sWidth;
1640 p.viewPortH := sHeight;
1642 {$IFDEF ENABLE_HOLMES}
1643 if (p = gPlayer1) then
1644 begin
1645 g_Holmes_plrViewPos(sX, sY);
1646 g_Holmes_plrViewSize(sWidth, sHeight);
1647 end;
1648 {$ENDIF}
1650 renderMapInternal(-c, -d, true);
1652 if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then
1653 case gPlayerIndicator of
1654 1:
1655 r_Player_DrawIndicator(p, _RGB(255, 255, 255));
1657 2:
1658 for i := 0 to High(gPlayers) do
1659 if gPlayers[i] <> nil then
1660 if gPlayers[i] = p then
1661 r_Player_DrawIndicator(p, _RGB(255, 255, 255))
1662 else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then
1663 if gPlayerIndicatorStyle = 1 then
1664 r_Player_DrawIndicator(gPlayers[i], _RGB(192, 192, 192))
1665 else
1666 r_Player_DrawIndicator(gPlayers[i], gPlayers[i].GetColor);
1667 end;
1670 for a := 0 to High(gCollideMap) do
1671 for b := 0 to High(gCollideMap[a]) do
1672 begin
1673 d := 0;
1674 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
1675 d := d + 1;
1676 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
1677 d := d + 2;
1679 case d of
1680 1: e_DrawPoint(1, b, a, 200, 200, 200);
1681 2: e_DrawPoint(1, b, a, 64, 64, 255);
1682 3: e_DrawPoint(1, b, a, 255, 0, 255);
1683 end;
1684 end;
1687 glPopMatrix();
1689 r_Player_DrawPain(p);
1690 r_Player_DrawPickup(p);
1691 r_Player_DrawRulez(p);
1692 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
1693 if g_Debug_Player then
1694 r_Player_DrawDebug(p);
1695 r_Player_DrawGUI(p);
1696 end;
1698 procedure drawProfilers ();
1699 var
1700 px: Integer = -1;
1701 py: Integer = -1;
1702 begin
1703 if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
1704 if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
1705 if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
1706 end;
1708 procedure r_Game_Draw();
1709 var
1710 ID: DWORD;
1711 w, h: Word;
1712 ww, hh: Byte;
1713 Time: Int64;
1714 back: string;
1715 plView1, plView2: TPlayer;
1716 Split: Boolean;
1717 MsgLineLength: Integer;
1718 MsgText: String;
1719 begin
1720 if gExit = EXIT_QUIT then Exit;
1722 Time := GetTickCount64() {div 1000};
1723 FPSCounter := FPSCounter+1;
1724 if Time - FPSTime >= 1000 then
1725 begin
1726 FPS := FPSCounter;
1727 FPSCounter := 0;
1728 FPSTime := Time;
1729 end;
1731 e_SetRendertarget(True);
1732 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
1734 if gGameOn or (gState = STATE_FOLD) then
1735 begin
1736 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
1737 begin
1738 gSpectMode := SPECT_NONE;
1739 if not gRevertPlayers then
1740 begin
1741 plView1 := gPlayer1;
1742 plView2 := gPlayer2;
1743 end
1744 else
1745 begin
1746 plView1 := gPlayer2;
1747 plView2 := gPlayer1;
1748 end;
1749 end
1750 else
1751 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
1752 begin
1753 gSpectMode := SPECT_NONE;
1754 if gPlayer2 = nil then
1755 plView1 := gPlayer1
1756 else
1757 plView1 := gPlayer2;
1758 plView2 := nil;
1759 end
1760 else
1761 begin
1762 plView1 := nil;
1763 plView2 := nil;
1764 end;
1766 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
1767 gSpectMode := SPECT_STATS;
1769 if gSpectMode = SPECT_PLAYERS then
1770 if gPlayers <> nil then
1771 begin
1772 plView1 := GetActivePlayer_ByID(gSpectPID1);
1773 if plView1 = nil then
1774 begin
1775 gSpectPID1 := GetActivePlayerID_Next();
1776 plView1 := GetActivePlayer_ByID(gSpectPID1);
1777 end;
1778 if gSpectViewTwo then
1779 begin
1780 plView2 := GetActivePlayer_ByID(gSpectPID2);
1781 if plView2 = nil then
1782 begin
1783 gSpectPID2 := GetActivePlayerID_Next();
1784 plView2 := GetActivePlayer_ByID(gSpectPID2);
1785 end;
1786 end;
1787 end;
1789 if gSpectMode = SPECT_MAPVIEW then
1790 begin
1791 // Режим просмотра карты
1792 Split := False;
1793 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
1794 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
1795 gHearPoint1.Active := True;
1796 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
1797 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
1798 gHearPoint2.Active := False;
1799 end
1800 else
1801 begin
1802 Split := (plView1 <> nil) and (plView2 <> nil);
1804 // Точки слуха игроков
1805 if plView1 <> nil then
1806 begin
1807 gHearPoint1.Active := True;
1808 gHearPoint1.Coords.X := plView1.GameX + PLAYER_RECT.Width;
1809 gHearPoint1.Coords.Y := plView1.GameY + PLAYER_RECT.Height DIV 2;
1810 end else
1811 gHearPoint1.Active := False;
1812 if plView2 <> nil then
1813 begin
1814 gHearPoint2.Active := True;
1815 gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width;
1816 gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2;
1817 end else
1818 gHearPoint2.Active := False;
1820 // Размер экранов игроков:
1821 gPlayerScreenSize.X := gScreenWidth-196;
1822 if Split then
1823 begin
1824 gPlayerScreenSize.Y := gScreenHeight div 2;
1825 if gScreenHeight mod 2 = 0 then
1826 Dec(gPlayerScreenSize.Y);
1827 end
1828 else
1829 gPlayerScreenSize.Y := gScreenHeight;
1831 if Split then
1832 if gScreenHeight mod 2 = 0 then
1833 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
1834 else
1835 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
1837 DrawPlayer(plView1);
1838 gPlayer1ScreenCoord.X := sX;
1839 gPlayer1ScreenCoord.Y := sY;
1841 if Split then
1842 begin
1843 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
1845 DrawPlayer(plView2);
1846 gPlayer2ScreenCoord.X := sX;
1847 gPlayer2ScreenCoord.Y := sY;
1848 end;
1850 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
1852 if Split then
1853 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
1854 end;
1856 {$IFDEF ENABLE_HOLMES}
1857 // draw inspector
1858 if (g_holmes_enabled) then g_Holmes_Draw();
1859 {$ENDIF}
1861 if MessageText <> '' then
1862 begin
1863 w := 0;
1864 h := 0;
1865 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
1866 MsgLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
1867 MsgText := b_Text_Wrap(b_Text_Format(MessageText), MsgLineLength);
1868 if Split then
1869 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
1870 (gScreenHeight div 2)-(h div 2), MsgText)
1871 else
1872 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
1873 Round(gScreenHeight / 2.75)-(h div 2), MsgText);
1874 end;
1876 if IsDrawStat or (gSpectMode = SPECT_STATS) then
1877 DrawStat();
1879 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
1880 begin
1881 // Draw spectator GUI
1882 ww := 0;
1883 hh := 0;
1884 e_TextureFontGetSize(gStdFont, ww, hh);
1885 case gSpectMode of
1886 SPECT_STATS:
1887 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
1888 SPECT_MAPVIEW:
1889 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
1890 SPECT_PLAYERS:
1891 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
1892 end;
1893 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
1894 if gSpectMode = SPECT_STATS then
1895 begin
1896 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
1897 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
1898 end;
1899 if gSpectMode = SPECT_MAPVIEW then
1900 begin
1901 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
1902 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
1903 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
1904 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
1905 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
1906 end;
1907 if gSpectMode = SPECT_PLAYERS then
1908 begin
1909 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
1910 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
1911 if gSpectViewTwo then
1912 begin
1913 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
1914 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
1915 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
1916 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
1917 end
1918 else
1919 begin
1920 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
1921 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
1922 end;
1923 end;
1924 end;
1925 end;
1927 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
1928 begin
1929 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1930 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
1932 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
1933 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
1934 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
1935 end;
1937 if not gGameOn then
1938 begin
1939 if (gState = STATE_MENU) then
1940 begin
1941 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then r_Game_DrawMenuBackground('MENU_BACKGROUND');
1942 // F3 at menu will show game loading dialog
1943 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
1944 if (g_ActiveWindow <> nil) then
1945 begin
1946 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1947 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
1948 end
1949 else
1950 begin
1951 // F3 at titlepic will show game loading dialog
1952 if e_KeyPressed(IK_F3) then
1953 begin
1954 g_Menu_Show_LoadMenu(true);
1955 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
1956 end;
1957 end;
1958 end;
1960 if gState = STATE_FOLD then
1961 begin
1962 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
1963 end;
1965 if gState = STATE_INTERCUSTOM then
1966 begin
1967 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
1968 begin
1969 back := 'TEXTURE_endpic';
1970 if not g_Texture_Get(back, ID) then
1971 back := _lc[I_TEXTURE_ENDPIC];
1972 end
1973 else
1974 back := 'INTER';
1976 r_Game_DrawMenuBackground(back);
1978 DrawCustomStat();
1980 if g_ActiveWindow <> nil then
1981 begin
1982 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1983 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
1984 end;
1985 end;
1987 if gState = STATE_INTERSINGLE then
1988 begin
1989 if EndingGameCounter > 0 then
1990 begin
1991 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
1992 end
1993 else
1994 begin
1995 back := 'INTER';
1997 r_Game_DrawMenuBackground(back);
1999 DrawSingleStat();
2001 if g_ActiveWindow <> nil then
2002 begin
2003 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2004 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2005 end;
2006 end;
2007 end;
2009 if gState = STATE_ENDPIC then
2010 begin
2011 ID := DWORD(-1);
2012 if g_Texture_Get('TEXTURE_endpic', ID) then r_Game_DrawMenuBackground('TEXTURE_endpic')
2013 else r_Game_DrawMenuBackground(_lc[I_TEXTURE_ENDPIC]);
2015 if g_ActiveWindow <> nil then
2016 begin
2017 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2018 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2019 end;
2020 end;
2022 if gState = STATE_SLIST then
2023 begin
2024 // if g_Texture_Get('MENU_BACKGROUND', ID) then
2025 // begin
2026 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
2027 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2028 // end;
2029 r_Game_DrawMenuBackground('MENU_BACKGROUND');
2030 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2031 r_Serverlist_Draw(slCurrent, slTable);
2032 end;
2033 end;
2035 if g_ActiveWindow <> nil then
2036 begin
2037 if gGameOn then
2038 begin
2039 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2040 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2041 end;
2042 g_ActiveWindow.Draw();
2043 end;
2045 {$IFNDEF HEADLESS}
2046 r_Console_Draw();
2047 {$ENDIF}
2049 if g_debug_Sounds and gGameOn then
2050 begin
2051 for w := 0 to High(e_SoundsArray) do
2052 for h := 0 to e_SoundsArray[w].nRefs do
2053 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
2054 end;
2056 if gShowFPS then
2057 begin
2058 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
2059 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
2060 end;
2062 if gGameOn and gShowTime then
2063 drawTime(gScreenWidth-72, gScreenHeight-16);
2065 if gGameOn then drawProfilers();
2067 // TODO: draw this after the FBO and remap mouse click coordinates
2069 {$IFDEF ENABLE_HOLMES}
2070 g_Holmes_DrawUI();
2071 {$ENDIF}
2073 // blit framebuffer to screen
2075 e_SetRendertarget(False);
2076 e_SetViewPort(0, 0, gWinSizeX, gWinSizeY);
2077 e_BlitFramebuffer(gWinSizeX, gWinSizeY);
2079 // draw the overlay stuff on top of it
2081 g_Touch_Draw;
2082 end;
2084 end.