DEADSOFTWARE

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