DEADSOFTWARE

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