DEADSOFTWARE

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