DEADSOFTWARE

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