DEADSOFTWARE

6b25a4f69aa81c63b34fb3e30cc5402270810d81
[d2df-sdl.git] / src / game / opengl / r_game.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../../shared/a_modes.inc}
16 unit r_game;
18 interface
20 procedure r_Game_Load;
21 procedure r_Game_Free;
23 procedure r_Game_LoadTextures;
24 procedure r_Game_FreeTextures;
26 procedure r_Game_Draw;
27 procedure r_Game_DrawLoadingStat;
28 procedure r_Game_DrawMenuBackground (tex: AnsiString);
30 procedure r_Game_SetupScreenSize;
32 var
33 gStdFont: DWORD;
34 gMenuFont: DWORD;
35 gMenuSmallFont: DWORD;
37 implementation
39 uses
40 {$INCLUDE ../nogl/noGLuses.inc}
41 {$IFDEF ENABLE_HOLMES}
42 g_holmes,
43 {$ENDIF}
44 {$IFDEF ENABLE_MENU}
45 g_gui, g_menu,
46 {$ENDIF}
47 {$IFDEF ENABLE_GFX}
48 g_gfx, r_gfx,
49 {$ENDIF}
50 {$IFDEF ENABLE_CORPSES}
51 g_corpses,
52 {$ENDIF}
53 {$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 g_Texture_Delete('NOTEXTURE');
216 g_Texture_Delete('TEXTURE_PLAYER_HUD');
217 g_Texture_Delete('TEXTURE_PLAYER_HUDBG');
218 g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD');
219 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG');
220 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S');
221 g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D');
222 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG');
223 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S');
224 g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D');
225 g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE');
226 g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA');
227 end;
229 procedure r_Game_SetupScreenSize;
230 const
231 RES_FACTOR = 4.0 / 3.0;
232 var
233 s: Single;
234 rf: Single;
235 bw, bh: Word;
236 begin
237 // Размер экранов игроков:
238 gPlayerScreenSize.X := gScreenWidth-196;
239 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
240 gPlayerScreenSize.Y := gScreenHeight div 2
241 else
242 gPlayerScreenSize.Y := gScreenHeight;
244 // Размер заднего плана:
245 if BackID <> DWORD(-1) then
246 begin
247 s := SKY_STRETCH;
248 if (gScreenWidth*s > gMapInfo.Width) or
249 (gScreenHeight*s > gMapInfo.Height) then
250 begin
251 gBackSize.X := gScreenWidth;
252 gBackSize.Y := gScreenHeight;
253 end
254 else
255 begin
256 e_GetTextureSize(BackID, @bw, @bh);
257 rf := Single(bw) / Single(bh);
258 if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR)
259 else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR);
260 s := Max(gScreenWidth / bw, gScreenHeight / bh);
261 if (s < 1.0) then s := 1.0;
262 gBackSize.X := Round(bw*s);
263 gBackSize.Y := Round(bh*s);
264 end;
265 end;
266 end;
268 procedure r_Game_LoadTextures;
269 begin
270 g_Texture_CreateWADEx('TEXTURE_endpic', EndPicPath, gTextureFilter);
271 if gMapInfo.SkyFullName <> '' then
272 g_Texture_CreateWAD(BackID, gMapInfo.SkyFullName, gTextureFilter);
273 r_Game_SetupScreenSize;
274 end;
276 procedure r_Game_FreeTextures;
277 begin
278 g_Texture_Delete('TEXTURE_endpic');
279 if BackID <> DWORD(-1) then
280 begin
281 gBackSize.X := 0;
282 gBackSize.Y := 0;
283 e_DeleteTexture(BackID);
284 BackID := DWORD(-1);
285 end
286 end;
288 procedure r_Map_DrawBack(dx, dy: Integer);
289 begin
290 if gDrawBackGround and (BackID <> DWORD(-1)) then
291 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
292 else
293 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
294 end;
296 function GetActivePlayer_ByID(ID: Integer): TPlayer;
297 var
298 a: Integer;
299 begin
300 Result := nil;
301 if ID < 0 then
302 Exit;
303 if gPlayers = nil then
304 Exit;
305 for a := Low(gPlayers) to High(gPlayers) do
306 if IsActivePlayer(gPlayers[a]) then
307 begin
308 if gPlayers[a].UID <> ID then
309 continue;
310 Result := gPlayers[a];
311 break;
312 end;
313 end;
315 function calcProfilesHeight (prof: TProfiler): Integer;
316 begin
317 result := 0;
318 if (prof = nil) then exit;
319 if (length(prof.bars) = 0) then exit;
320 result := length(prof.bars)*(16+2);
321 end;
323 // returns width
324 function drawProfiles (x, y: Integer; prof: TProfiler): Integer;
325 var
326 wdt, hgt: Integer;
327 yy: Integer;
328 ii: Integer;
329 begin
330 result := 0;
331 if (prof = nil) then exit;
332 // gScreenWidth
333 if (length(prof.bars) = 0) then exit;
334 wdt := 192;
335 hgt := calcProfilesHeight(prof);
336 if (x < 0) then x := gScreenWidth-(wdt-1)+x;
337 if (y < 0) then y := gScreenHeight-(hgt-1)+y;
338 // background
339 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
340 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
341 e_DarkenQuadWH(x, y, wdt, hgt, 150);
342 // title
343 yy := y+2;
344 for ii := 0 to High(prof.bars) do
345 begin
346 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);
347 Inc(yy, 16+2);
348 end;
349 result := wdt;
350 end;
352 procedure drawTime(X, Y: Integer); inline;
353 begin
354 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);
355 end;
357 procedure DrawStat();
358 var
359 pc, x, y, w, h: Integer;
360 w1, w2, w3, w4: Integer;
361 a, aa: Integer;
362 cw, ch, r, g, b, rr, gg, bb: Byte;
363 s1, s2, s3: String;
364 _y: Integer;
365 stat: TPlayerStatArray;
366 wad, map: string;
367 mapstr: string;
368 namestr: string;
369 begin
370 s1 := '';
371 s2 := '';
372 s3 := '';
373 pc := g_Player_GetCount;
374 e_TextureFontGetSize(gStdFont, cw, ch);
376 w := gScreenWidth-(gScreenWidth div 5);
377 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
378 h := 32+ch*(11+pc)
379 else
380 h := 40+ch*5+(ch+8)*pc;
381 x := (gScreenWidth div 2)-(w div 2);
382 y := (gScreenHeight div 2)-(h div 2);
384 e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32);
385 e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0);
387 drawTime(x+w-78, y+8);
389 wad := g_ExtractWadNameNoPath(gMapInfo.Map);
390 map := g_ExtractFileName(gMapInfo.Map);
391 mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name;
393 case gGameSettings.GameMode of
394 GM_DM:
395 begin
396 if gGameSettings.MaxLives = 0 then
397 s1 := _lc[I_GAME_DM]
398 else
399 s1 := _lc[I_GAME_LMS];
400 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
401 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
402 end;
404 GM_TDM:
405 begin
406 if gGameSettings.MaxLives = 0 then
407 s1 := _lc[I_GAME_TDM]
408 else
409 s1 := _lc[I_GAME_TLMS];
410 s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]);
411 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
412 end;
414 GM_CTF:
415 begin
416 s1 := _lc[I_GAME_CTF];
417 s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]);
418 s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]);
419 end;
421 GM_COOP:
422 begin
423 if gGameSettings.MaxLives = 0 then
424 s1 := _lc[I_GAME_COOP]
425 else
426 s1 := _lc[I_GAME_SURV];
427 s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters);
428 s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount);
429 end;
431 else
432 begin
433 s1 := '';
434 s2 := '';
435 end;
436 end;
437 _y := y+8;
438 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1);
439 _y := _y+ch+8;
440 e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1);
441 _y := _y+ch+8;
442 e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1);
444 e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3,
445 gStdFont, 200, 200, 200, 1);
447 if NetMode = NET_SERVER then
448 e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1)
449 else
450 if NetMode = NET_CLIENT then
451 e_TextureFontPrintEx(x+8, y + 8,
452 NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1);
454 if pc = 0 then
455 Exit;
456 stat := g_Player_GetStats();
457 SortGameStat(stat);
459 w2 := (w-16) div 6 + 48; // ширина 2 столбца
460 w3 := (w-16) div 6; // ширина 3 и 4 столбцов
461 w4 := w3;
462 w1 := w-16-w2-w3-w4; // оставшееся пространство - для цвета и имени игрока
464 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
465 begin
466 _y := _y+ch+ch;
468 for a := TEAM_RED to TEAM_BLUE do
469 begin
470 if a = TEAM_RED then
471 begin
472 s1 := _lc[I_GAME_TEAM_RED];
473 r := 255;
474 g := 0;
475 b := 0;
476 end
477 else
478 begin
479 s1 := _lc[I_GAME_TEAM_BLUE];
480 r := 0;
481 g := 0;
482 b := 255;
483 end;
485 e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1);
486 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals),
487 gStdFont, r, g, b, 1);
489 _y := _y+ch+(ch div 4);
490 e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b);
491 _y := _y+(ch div 4);
493 for aa := 0 to High(stat) do
494 if stat[aa].Team = a then
495 with stat[aa] do
496 begin
497 if Spectator then
498 begin
499 rr := r div 2;
500 gg := g div 2;
501 bb := b div 2;
502 end
503 else
504 begin
505 rr := r;
506 gg := g;
507 bb := b;
508 end;
509 if gShowPIDs then
510 namestr := Format('[%5d] %s', [UID, Name])
511 else
512 namestr := Name;
513 // Имя
514 e_TextureFontPrintEx(x+16, _y, namestr, gStdFont, rr, gg, bb, 1);
515 // Пинг/потери
516 e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1);
517 // Фраги
518 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
519 // Смерти
520 e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
521 _y := _y+ch;
522 end;
524 _y := _y+ch;
525 end;
526 end
527 else if gGameSettings.GameMode in [GM_DM, GM_COOP] then
528 begin
529 _y := _y+ch+ch;
530 e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
531 e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1);
532 e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
533 e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
535 _y := _y+ch+8;
536 for aa := 0 to High(stat) do
537 with stat[aa] do
538 begin
539 if Spectator then
540 begin
541 r := 127;
542 g := 64;
543 end
544 else
545 begin
546 r := 255;
547 g := 127;
548 end;
549 if gShowPIDs then
550 namestr := Format('[%5d] %s', [UID, Name])
551 else
552 namestr := Name;
553 // Цвет игрока
554 e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
555 e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192);
556 // Имя
557 e_TextureFontPrintEx(x+16+16+8, _y+4, namestr, gStdFont, r, g, 0, 1);
558 // Пинг/потери
559 e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1);
560 // Фраги
561 e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1);
562 // Смерти
563 e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1);
564 _y := _y+ch+8;
565 end;
566 end
567 end;
569 procedure DrawCustomStat();
570 var
571 pc, x, y, w, _y,
572 w1, w2, w3,
573 t, p, m: Integer;
574 ww1, hh1: Word;
575 ww2, hh2, r, g, b, rr, gg, bb: Byte;
576 s1, s2, topstr: String;
577 begin
578 e_TextureFontGetSize(gStdFont, ww2, hh2);
580 {$IFDEF ENABLE_SYSTEM}
581 sys_HandleInput;
582 {$ENDIF}
584 if g_Console_Action(ACTION_SCORES) then
585 begin
586 if not gStatsPressed then
587 begin
588 gStatsOff := not gStatsOff;
589 gStatsPressed := True;
590 end;
591 end
592 else
593 gStatsPressed := False;
595 if gStatsOff then
596 begin
597 s1 := _lc[I_MENU_INTER_NOTICE_TAB];
598 w := (Length(s1) * ww2) div 2;
599 x := gScreenWidth div 2 - w;
600 y := 8;
601 e_TextureFontPrint(x, y, s1, gStdFont);
602 Exit;
603 end;
605 if (gGameSettings.GameMode = GM_COOP) then
606 begin
607 if gMissionFailed then
608 topstr := _lc[I_MENU_INTER_MISSION_FAIL]
609 else
610 topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE];
611 end
612 else
613 topstr := _lc[I_MENU_INTER_ROUND_OVER];
615 e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1);
616 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr);
618 if g_Game_IsNet then
619 begin
620 topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]);
621 if not gChatShow then
622 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
623 gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1);
624 end;
626 if g_Game_IsClient then
627 topstr := _lc[I_MENU_INTER_NOTICE_MAP]
628 else
629 topstr := _lc[I_MENU_INTER_NOTICE_SPACE];
630 if not gChatShow then
631 e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2),
632 gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1);
634 x := 32;
635 y := 16+hh1+16;
637 w := gScreenWidth-x*2;
639 w2 := (w-16) div 6;
640 w3 := w2;
641 w1 := w-16-w2-w3;
643 e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32);
644 e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0);
646 m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2;
648 case CustomStat.GameMode of
649 GM_DM:
650 begin
651 if gGameSettings.MaxLives = 0 then
652 s1 := _lc[I_GAME_DM]
653 else
654 s1 := _lc[I_GAME_LMS];
655 end;
656 GM_TDM:
657 begin
658 if gGameSettings.MaxLives = 0 then
659 s1 := _lc[I_GAME_TDM]
660 else
661 s1 := _lc[I_GAME_TLMS];
662 end;
663 GM_CTF: s1 := _lc[I_GAME_CTF];
664 GM_COOP:
665 begin
666 if gGameSettings.MaxLives = 0 then
667 s1 := _lc[I_GAME_COOP]
668 else
669 s1 := _lc[I_GAME_SURV];
670 end;
671 else s1 := '';
672 end;
674 _y := y+16;
675 e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
676 _y := _y+8;
678 _y := _y+16;
679 e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1);
680 e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont);
682 _y := _y+16;
683 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1);
684 e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600,
685 (CustomStat.GameTime div 1000 div 60) mod 60,
686 CustomStat.GameTime div 1000 mod 60]), gStdFont);
688 pc := Length(CustomStat.PlayerStat);
689 if pc = 0 then Exit;
691 if CustomStat.GameMode = GM_COOP then
692 begin
693 m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2;
694 _y := _y+32;
695 s2 := _lc[I_GAME_MONSTERS];
696 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
697 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1);
698 _y := _y+16;
699 s2 := _lc[I_GAME_SECRETS];
700 e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1);
701 e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1);
702 if gLastMap then
703 begin
704 m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2;
705 _y := _y-16;
706 s2 := _lc[I_GAME_MONSTERS_TOTAL];
707 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
708 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1);
709 _y := _y+16;
710 s2 := _lc[I_GAME_SECRETS_TOTAL];
711 e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1);
712 e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1);
713 end;
714 end;
716 if CustomStat.GameMode in [GM_TDM, GM_CTF] then
717 begin
718 _y := _y+16+16;
720 with CustomStat do
721 if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED]
722 else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE]
723 else s1 := _lc[I_GAME_WIN_DRAW];
725 e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1);
726 _y := _y+40;
728 for t := TEAM_RED to TEAM_BLUE do
729 begin
730 if t = TEAM_RED then
731 begin
732 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED],
733 gStdFont, 255, 0, 0, 1);
734 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals),
735 gStdFont, 255, 0, 0, 1);
736 r := 255;
737 g := 0;
738 b := 0;
739 end
740 else
741 begin
742 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE],
743 gStdFont, 0, 0, 255, 1);
744 e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals),
745 gStdFont, 0, 0, 255, 1);
746 r := 0;
747 g := 0;
748 b := 255;
749 end;
751 e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b);
752 _y := _y+24;
754 for p := 0 to High(CustomStat.PlayerStat) do
755 if CustomStat.PlayerStat[p].Team = t then
756 with CustomStat.PlayerStat[p] do
757 begin
758 if Spectator then
759 begin
760 rr := r div 2;
761 gg := g div 2;
762 bb := b div 2;
763 end
764 else
765 begin
766 rr := r;
767 gg := g;
768 bb := b;
769 end;
770 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
771 e_TextureFontPrintEx(x+16, _y, Name + ' *', gStdFont, rr, gg, bb, 1)
772 else
773 e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1);
774 e_TextureFontPrintEx(x+w1+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1);
775 e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1);
776 _y := _y+24;
777 end;
779 _y := _y+16+16;
780 end;
781 end
782 else if CustomStat.GameMode in [GM_DM, GM_COOP] then
783 begin
784 _y := _y+40;
785 e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1);
786 e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1);
787 e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1);
789 _y := _y+24;
790 for p := 0 to High(CustomStat.PlayerStat) do
791 with CustomStat.PlayerStat[p] do
792 begin
793 e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0);
795 if Spectator then
796 r := 127
797 else
798 r := 255;
800 if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then
801 e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True)
802 else
803 e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True);
804 e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True);
805 e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True);
806 _y := _y+24;
807 end;
808 end;
810 // HACK: take stats screenshot immediately after the first frame of the stats showing
811 if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then
812 begin
813 g_TakeScreenShot('stats/' + StatFilename);
814 StatShotDone := True;
815 end;
816 end;
818 procedure DrawSingleStat();
819 var
820 tm, key_x, val_x, y: Integer;
821 w1, w2, h: Word;
822 s1, s2: String;
824 procedure player_stat(n: Integer);
825 var
826 kpm: Real;
828 begin
829 // "Kills: # / #":
830 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]);
831 s2 := Format(' %d', [gTotalMonsters]);
833 e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]);
834 e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0));
835 e_CharFont_GetSize(gMenuFont, s1, w1, h);
836 e_CharFont_Print(gMenuFont, val_x+w1, y, '/');
837 s1 := s1 + '/';
838 e_CharFont_GetSize(gMenuFont, s1, w1, h);
839 e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0));
841 // "Kills-per-minute: ##.#":
842 s1 := _lc[I_MENU_INTER_KPM];
843 if tm > 0 then
844 kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60
845 else
846 kpm := SingleStat.PlayerStat[n].Kills;
847 s2 := Format(' %.1f', [kpm]);
849 e_CharFont_Print(gMenuFont, key_x, y+32, s1);
850 e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0));
852 // "Secrets found: # / #":
853 s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]);
854 s2 := Format(' %d', [SingleStat.TotalSecrets]);
856 e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]);
857 e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0));
858 e_CharFont_GetSize(gMenuFont, s1, w1, h);
859 e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/');
860 s1 := s1 + '/';
861 e_CharFont_GetSize(gMenuFont, s1, w1, h);
862 e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0));
863 end;
865 begin
866 // "Level Complete":
867 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h);
868 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]);
870 // Определяем координаты выравнивания по самой длинной строке:
871 s1 := _lc[I_MENU_INTER_KPM];
872 e_CharFont_GetSize(gMenuFont, s1, w1, h);
873 Inc(w1, 16);
874 s1 := ' 9999.9';
875 e_CharFont_GetSize(gMenuFont, s1, w2, h);
877 key_x := (gScreenWidth-w1-w2) div 2;
878 val_x := key_x + w1;
880 // "Time: #:##:##":
881 tm := SingleStat.GameTime div 1000;
882 s1 := _lc[I_MENU_INTER_TIME];
883 s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]);
885 e_CharFont_Print(gMenuFont, key_x, 80, s1);
886 e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0));
888 if SingleStat.TwoPlayers then
889 begin
890 // "Player 1":
891 s1 := _lc[I_MENU_PLAYER_1];
892 e_CharFont_GetSize(gMenuFont, s1, w1, h);
893 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1);
895 // Статистика первого игрока:
896 y := 176;
897 player_stat(0);
899 // "Player 2":
900 s1 := _lc[I_MENU_PLAYER_2];
901 e_CharFont_GetSize(gMenuFont, s1, w1, h);
902 e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1);
904 // Статистика второго игрока:
905 y := 336;
906 player_stat(1);
907 end
908 else
909 begin
910 // Статистика первого игрока:
911 y := 128;
912 player_stat(0);
913 end;
914 end;
916 procedure r_Game_DrawLoadingStat;
917 procedure drawRect (x, y, w, h: Integer);
918 begin
919 if (w < 1) or (h < 1) then exit;
920 glBegin(GL_QUADS);
921 glVertex2f(x+0.375, y+0.375);
922 glVertex2f(x+w+0.375, y+0.375);
923 glVertex2f(x+w+0.375, y+h+0.375);
924 glVertex2f(x+0.375, y+h+0.375);
925 glEnd();
926 end;
928 function drawPBar (cur, total: Integer; washere: Boolean): Boolean;
929 var
930 rectW, rectH: Integer;
931 x0, y0: Integer;
932 wdt: Integer;
933 wl, hl: Integer;
934 wr, hr: Integer;
935 wb, hb: Integer;
936 wm, hm: Integer;
937 idl, idr, idb, idm: LongWord;
938 f, my: Integer;
939 begin
940 result := false;
941 if (total < 1) then exit;
942 if (cur < 1) then exit; // don't blink
943 if (not washere) and (cur >= total) then exit; // don't blink
944 //if (cur < 0) then cur := 0;
945 //if (cur > total) then cur := total;
946 result := true;
948 if (hasPBarGfx) then
949 begin
950 g_Texture_Get('UI_GFX_PBAR_LEFT', idl);
951 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl);
952 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr);
953 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr);
954 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb);
955 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb);
956 g_Texture_Get('UI_GFX_PBAR_MARKER', idm);
957 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm);
959 //rectW := gScreenWidth-360;
960 rectW := trunc(624.0*gScreenWidth/1024.0);
961 rectH := hl;
963 x0 := (gScreenWidth-rectW) div 2;
964 y0 := gScreenHeight-rectH-64;
965 if (y0 < 2) then y0 := 2;
967 glEnable(GL_SCISSOR_TEST);
969 // left and right
970 glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH);
971 e_DrawSize(idl, x0, y0, 0, true, false, wl, hl);
972 e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr);
974 // body
975 glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH);
976 f := x0+wl;
977 while (f < x0+rectW) do
978 begin
979 e_DrawSize(idb, f, y0, 0, true, false, wb, hb);
980 f += wb;
981 end;
983 // filled part
984 wdt := (rectW-wl-wr)*cur div total;
985 if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr;
986 if (wdt > 0) then
987 begin
988 my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2;
989 glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm);
990 f := x0+wl;
991 while (wdt > 0) do
992 begin
993 e_DrawSize(idm, f, y0, 0, true, false, wm, hm);
994 f += wm;
995 wdt -= wm;
996 end;
997 end;
999 glScissor(0, 0, gScreenWidth, gScreenHeight);
1000 end
1001 else
1002 begin
1003 rectW := gScreenWidth-64;
1004 rectH := 16;
1006 x0 := (gScreenWidth-rectW) div 2;
1007 y0 := gScreenHeight-rectH-64;
1008 if (y0 < 2) then y0 := 2;
1010 glDisable(GL_BLEND);
1011 glDisable(GL_TEXTURE_2D);
1013 //glClearColor(0, 0, 0, 0);
1014 //glClear(GL_COLOR_BUFFER_BIT);
1016 glColor4ub(127, 127, 127, 255);
1017 drawRect(x0-2, y0-2, rectW+4, rectH+4);
1019 glColor4ub(0, 0, 0, 255);
1020 drawRect(x0-1, y0-1, rectW+2, rectH+2);
1022 glColor4ub(127, 127, 127, 255);
1023 wdt := rectW*cur div total;
1024 if (wdt > rectW) then wdt := rectW;
1025 drawRect(x0, y0, wdt, rectH);
1026 end;
1027 end;
1029 var
1030 ww, hh: Word;
1031 xx, yy, i: Integer;
1032 s: String;
1033 begin
1034 if (Length(LoadingStat.Msgs) = 0) then exit;
1036 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh);
1037 yy := (gScreenHeight div 3);
1038 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]);
1039 xx := (gScreenWidth div 3);
1041 with LoadingStat do
1042 begin
1043 for i := 0 to NextMsg-1 do
1044 begin
1045 if (i = (NextMsg-1)) and (MaxValue > 0) then
1046 s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue])
1047 else
1048 s := Msgs[i];
1050 e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0));
1051 yy := yy + LOADING_INTERLINE;
1052 PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere);
1053 end;
1054 end;
1055 end;
1057 procedure r_Game_DrawMenuBackground (tex: AnsiString);
1058 var
1059 w, h: Word;
1060 ID: DWord;
1062 begin
1063 if g_Texture_Get(tex, ID) then
1064 begin
1065 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
1066 e_GetTextureSize(ID, @w, @h);
1067 if w = h then
1068 w := round(w * 1.333 * (gScreenHeight / h))
1069 else
1070 w := trunc(w * (gScreenHeight / h));
1071 e_DrawSize(ID, (gScreenWidth - w) div 2, 0, 0, False, False, w, gScreenHeight);
1072 end
1073 else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
1074 end;
1076 procedure DrawMinimap(p: TPlayer; RenderRect: TRect);
1077 var
1078 a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer;
1080 function monDraw (mon: TMonster): Boolean;
1081 begin
1082 result := false; // don't stop
1083 with mon do
1084 begin
1085 if alive then
1086 begin
1087 // Левый верхний угол
1088 aX := Obj.X div ScaleSz + 1;
1089 aY := Obj.Y div ScaleSz + 1;
1090 // Размеры
1091 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
1092 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
1093 // Правый нижний угол
1094 aX2 := aX + aX2 - 1;
1095 aY2 := aY + aY2 - 1;
1096 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0);
1097 end;
1098 end;
1099 end;
1101 begin
1102 if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or
1103 (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then
1104 begin
1105 Scale := 1;
1106 // Сколько пикселов карты в 1 пикселе мини-карты:
1107 ScaleSz := 16 div Scale;
1108 // Размеры мини-карты:
1109 aX := max(gMapInfo.Width div ScaleSz, 1);
1110 aY := max(gMapInfo.Height div ScaleSz, 1);
1111 // Рамка карты:
1112 e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0);
1114 if gWalls <> nil then
1115 begin
1116 // Рисуем стены:
1117 for a := 0 to High(gWalls) do
1118 with gWalls[a] do
1119 if PanelType <> 0 then
1120 begin
1121 // Левый верхний угол:
1122 aX := X div ScaleSz;
1123 aY := Y div ScaleSz;
1124 // Размеры:
1125 aX2 := max(Width div ScaleSz, 1);
1126 aY2 := max(Height div ScaleSz, 1);
1127 // Правый нижний угол:
1128 aX2 := aX + aX2 - 1;
1129 aY2 := aY + aY2 - 1;
1131 case PanelType of
1132 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
1133 PANEL_OPENDOOR, PANEL_CLOSEDOOR:
1134 if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0);
1135 end;
1136 end;
1137 end;
1138 if gSteps <> nil then
1139 begin
1140 // Рисуем ступени:
1141 for a := 0 to High(gSteps) do
1142 with gSteps[a] do
1143 if PanelType <> 0 then
1144 begin
1145 // Левый верхний угол:
1146 aX := X div ScaleSz;
1147 aY := Y div ScaleSz;
1148 // Размеры:
1149 aX2 := max(Width div ScaleSz, 1);
1150 aY2 := max(Height div ScaleSz, 1);
1151 // Правый нижний угол:
1152 aX2 := aX + aX2 - 1;
1153 aY2 := aY + aY2 - 1;
1155 e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
1156 end;
1157 end;
1158 if gLifts <> nil then
1159 begin
1160 // Рисуем лифты:
1161 for a := 0 to High(gLifts) do
1162 with gLifts[a] do
1163 if PanelType <> 0 then
1164 begin
1165 // Левый верхний угол:
1166 aX := X div ScaleSz;
1167 aY := Y div ScaleSz;
1168 // Размеры:
1169 aX2 := max(Width div ScaleSz, 1);
1170 aY2 := max(Height div ScaleSz, 1);
1171 // Правый нижний угол:
1172 aX2 := aX + aX2 - 1;
1173 aY2 := aY + aY2 - 1;
1175 case LiftType of
1176 LIFTTYPE_UP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
1177 LIFTTYPE_DOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
1178 LIFTTYPE_LEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
1179 LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
1180 end;
1181 end;
1182 end;
1183 if gWater <> nil then
1184 begin
1185 // Рисуем воду:
1186 for a := 0 to High(gWater) do
1187 with gWater[a] do
1188 if PanelType <> 0 then
1189 begin
1190 // Левый верхний угол:
1191 aX := X div ScaleSz;
1192 aY := Y div ScaleSz;
1193 // Размеры:
1194 aX2 := max(Width div ScaleSz, 1);
1195 aY2 := max(Height div ScaleSz, 1);
1196 // Правый нижний угол:
1197 aX2 := aX + aX2 - 1;
1198 aY2 := aY + aY2 - 1;
1200 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
1201 end;
1202 end;
1203 if gAcid1 <> nil then
1204 begin
1205 // Рисуем кислоту 1:
1206 for a := 0 to High(gAcid1) do
1207 with gAcid1[a] do
1208 if PanelType <> 0 then
1209 begin
1210 // Левый верхний угол:
1211 aX := X div ScaleSz;
1212 aY := Y div ScaleSz;
1213 // Размеры:
1214 aX2 := max(Width div ScaleSz, 1);
1215 aY2 := max(Height div ScaleSz, 1);
1216 // Правый нижний угол:
1217 aX2 := aX + aX2 - 1;
1218 aY2 := aY + aY2 - 1;
1220 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
1221 end;
1222 end;
1223 if gAcid2 <> nil then
1224 begin
1225 // Рисуем кислоту 2:
1226 for a := 0 to High(gAcid2) do
1227 with gAcid2[a] do
1228 if PanelType <> 0 then
1229 begin
1230 // Левый верхний угол:
1231 aX := X div ScaleSz;
1232 aY := Y div ScaleSz;
1233 // Размеры:
1234 aX2 := max(Width div ScaleSz, 1);
1235 aY2 := max(Height div ScaleSz, 1);
1236 // Правый нижний угол:
1237 aX2 := aX + aX2 - 1;
1238 aY2 := aY + aY2 - 1;
1240 e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
1241 end;
1242 end;
1243 if gPlayers <> nil then
1244 begin
1245 // Рисуем игроков:
1246 for a := 0 to High(gPlayers) do
1247 if gPlayers[a] <> nil then with gPlayers[a] do
1248 if alive then begin
1249 // Левый верхний угол:
1250 aX := Obj.X div ScaleSz + 1;
1251 aY := Obj.Y div ScaleSz + 1;
1252 // Размеры:
1253 aX2 := max(Obj.Rect.Width div ScaleSz, 1);
1254 aY2 := max(Obj.Rect.Height div ScaleSz, 1);
1255 // Правый нижний угол:
1256 aX2 := aX + aX2 - 1;
1257 aY2 := aY + aY2 - 1;
1259 if gPlayers[a] = p then
1260 e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0)
1261 else
1262 case Team of
1263 TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0);
1264 TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0);
1265 else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0);
1266 end;
1267 end;
1268 end;
1269 // Рисуем монстров
1270 g_Mons_ForEach(monDraw);
1271 end;
1272 end;
1275 procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor);
1276 begin
1277 if not hasAmbient then exit;
1278 e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a);
1279 end;
1281 // ////////////////////////////////////////////////////////////////////////// //
1282 var
1283 ltexid: GLuint = 0;
1285 function g_Texture_Light (): Integer;
1286 const
1287 Radius: Integer = 128;
1288 var
1289 tex, tpp: PByte;
1290 x, y, a: Integer;
1291 dist: Double;
1292 begin
1293 if ltexid = 0 then
1294 begin
1295 GetMem(tex, (Radius*2)*(Radius*2)*4);
1296 tpp := tex;
1297 for y := 0 to Radius*2-1 do
1298 begin
1299 for x := 0 to Radius*2-1 do
1300 begin
1301 dist := 1.0-sqrt((x-Radius)*(x-Radius)+(y-Radius)*(y-Radius))/Radius;
1302 if (dist < 0) then
1303 begin
1304 tpp^ := 0; Inc(tpp);
1305 tpp^ := 0; Inc(tpp);
1306 tpp^ := 0; Inc(tpp);
1307 tpp^ := 0; Inc(tpp);
1308 end
1309 else
1310 begin
1311 //tc.setPixel(x, y, Color(cast(int)(dist*255), cast(int)(dist*255), cast(int)(dist*255)));
1312 if (dist > 0.5) then dist := 0.5;
1313 a := round(dist*255);
1314 if (a < 0) then a := 0 else if (a > 255) then a := 255;
1315 tpp^ := 255; Inc(tpp);
1316 tpp^ := 255; Inc(tpp);
1317 tpp^ := 255; Inc(tpp);
1318 tpp^ := Byte(a); Inc(tpp);
1319 end;
1320 end;
1321 end;
1323 glGenTextures(1, @ltexid);
1324 //if (tid == 0) assert(0, "VGL: can't create screen texture");
1326 glBindTexture(GL_TEXTURE_2D, ltexid);
1327 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1328 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1329 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1330 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1332 //GLfloat[4] bclr = 0.0;
1333 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
1335 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Radius*2, Radius*2, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
1336 end;
1338 result := ltexid;
1339 end;
1341 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
1342 //FIXME: broken for splitscreen mode
1343 procedure renderDynLightsInternal ();
1344 var
1345 //hasAmbient: Boolean;
1346 //ambColor: TDFColor;
1347 lln: Integer;
1348 lx, ly, lrad: Integer;
1349 scxywh: array[0..3] of GLint;
1350 wassc: Boolean;
1351 begin
1352 if e_NoGraphics then exit;
1354 //TODO: lights should be in separate grid, i think
1355 // but on the other side: grid may be slower for dynlights, as their lifetime is short
1356 if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit;
1358 // rendering mode
1359 //ambColor := gCurrentMap['light_ambient'].rgba;
1360 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
1362 { // this will multiply incoming color to alpha from framebuffer
1363 glEnable(GL_BLEND);
1364 glBlendFunc(GL_DST_ALPHA, GL_ONE);
1367 (*
1368 * light rendering: (INVALID!)
1369 * glStencilFunc(GL_EQUAL, 0, $ff);
1370 * for each light:
1371 * glClear(GL_STENCIL_BUFFER_BIT);
1372 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
1373 * draw shadow volume into stencil buffer
1374 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1375 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
1376 * turn off blending
1377 * draw color-less quad with light alpha (WARNING! don't touch color!)
1378 * glEnable(GL_BLEND);
1379 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
1380 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
1381 *)
1382 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
1383 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
1385 // setup OpenGL parameters
1386 glStencilMask($FFFFFFFF);
1387 glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF);
1388 glEnable(GL_STENCIL_TEST);
1389 glEnable(GL_SCISSOR_TEST);
1390 glClear(GL_STENCIL_BUFFER_BIT);
1391 glStencilFunc(GL_EQUAL, 0, $ff);
1393 for lln := 0 to g_dynLightCount-1 do
1394 begin
1395 lx := g_dynLights[lln].x;
1396 ly := g_dynLights[lln].y;
1397 lrad := g_dynLights[lln].radius;
1398 if (lrad < 3) then continue;
1400 if (lx-sX+lrad < 0) then continue;
1401 if (ly-sY+lrad < 0) then continue;
1402 if (lx-sX-lrad >= gPlayerScreenSize.X) then continue;
1403 if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue;
1405 // set scissor to optimize drawing
1406 if (g_dbg_scale = 1.0) then
1407 begin
1408 glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4);
1409 end
1410 else
1411 begin
1412 glScissor(0, 0, gScreenWidth, gScreenHeight);
1413 end;
1414 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
1415 if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT);
1416 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
1417 // draw extruded panels
1418 glDisable(GL_TEXTURE_2D);
1419 glDisable(GL_BLEND);
1420 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer
1421 if (lrad > 4) then r_Map_DrawPanelShadowVolumes(lx, ly, lrad);
1422 // render light texture
1423 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1424 glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer
1425 // blend it
1426 glEnable(GL_BLEND);
1427 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1428 glEnable(GL_TEXTURE_2D);
1429 // color and opacity
1430 glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a);
1431 glBindTexture(GL_TEXTURE_2D, g_Texture_Light());
1432 glBegin(GL_QUADS);
1433 glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left
1434 glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right
1435 glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right
1436 glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left
1437 glEnd();
1438 end;
1440 // done
1441 glDisable(GL_STENCIL_TEST);
1442 glDisable(GL_BLEND);
1443 glDisable(GL_SCISSOR_TEST);
1444 //glScissor(0, 0, sWidth, sHeight);
1446 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
1447 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
1448 end;
1451 function fixViewportForScale (): Boolean;
1452 var
1453 nx0, ny0, nw, nh: Integer;
1454 begin
1455 result := false;
1456 if (g_dbg_scale <> 1.0) then
1457 begin
1458 result := true;
1459 nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale);
1460 ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale);
1461 nw := round(sWidth/g_dbg_scale);
1462 nh := round(sHeight/g_dbg_scale);
1463 sX := nx0;
1464 sY := ny0;
1465 sWidth := nw;
1466 sHeight := nh;
1467 end;
1468 end;
1471 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
1472 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
1473 procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean);
1474 type
1475 TDrawCB = procedure ();
1477 var
1478 hasAmbient: Boolean;
1479 ambColor: TDFColor;
1480 doAmbient: Boolean = false;
1482 procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean);
1483 var
1484 tagmask: Integer;
1485 pan: TPanel;
1486 begin
1487 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
1488 if gdbg_map_use_accel_render then
1489 begin
1490 tagmask := panelTypeToTag(panType);
1491 while (gDrawPanelList.count > 0) do
1492 begin
1493 pan := TPanel(gDrawPanelList.front());
1494 if ((pan.tag and tagmask) = 0) then break;
1495 if doDraw then r_Panel_Draw(pan, doAmbient, ambColor);
1496 gDrawPanelList.popFront();
1497 end;
1498 end
1499 else
1500 begin
1501 if doDraw then r_Map_DrawPanels(panType, hasAmbient, ambColor);
1502 end;
1503 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1504 end;
1506 procedure drawOther (profname: AnsiString; cb: TDrawCB);
1507 begin
1508 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname);
1509 if assigned(cb) then cb();
1510 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1511 end;
1513 begin
1514 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total');
1516 // our accelerated renderer will collect all panels to gDrawPanelList
1517 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
1518 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect');
1519 if gdbg_map_use_accel_render then
1520 begin
1521 r_Map_CollectDrawPanels(sX, sY, sWidth, sHeight);
1522 end;
1523 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1525 if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback');
1526 r_Map_DrawBack(backXOfs, backYOfs);
1527 if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd();
1529 if setTransMatrix then
1530 begin
1531 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
1532 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
1533 glTranslatef(-sX, -sY, 0);
1534 end;
1536 // rendering mode
1537 ambColor := gCurrentMap['light_ambient'].rgba;
1538 hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
1541 if hasAmbient then
1542 begin
1543 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
1544 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
1545 glClear(GL_COLOR_BUFFER_BIT);
1546 end;
1548 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
1551 drawPanelType('*back', PANEL_BACK, g_rlayer_back);
1552 drawPanelType('*step', PANEL_STEP, g_rlayer_step);
1553 drawOther('items', @r_Items_Draw);
1554 drawOther('weapons', @r_Weapon_Draw);
1555 {$IFDEF ENABLE_SHELLS}
1556 drawOther('shells', @r_Player_DrawShells);
1557 {$ENDIF}
1558 drawOther('drawall', @r_Player_DrawAll);
1559 {$IFDEF ENABLE_GIBS}
1560 drawOther('gibs', @r_PlayerModel_DrawGibs);
1561 {$ENDIF}
1562 {$IFDEF ENABLE_CORPSES}
1563 drawOther('corpses', @r_Player_DrawCorpses);
1564 {$ENDIF}
1565 drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
1566 drawOther('monsters', @r_Monsters_Draw);
1567 drawOther('itemdrop', @r_Items_DrawDrop);
1568 drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door);
1569 {$IFDEF ENABLE_GFX}
1570 drawOther('gfx', @r_GFX_Draw);
1571 {$ENDIF}
1572 drawOther('flags', @r_Map_DrawFlags);
1573 drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1);
1574 drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2);
1575 drawPanelType('*water', PANEL_WATER, g_rlayer_water);
1576 drawOther('dynlights', @renderDynLightsInternal);
1578 if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
1579 begin
1580 renderAmbientQuad(hasAmbient, ambColor);
1581 end;
1583 doAmbient := true;
1584 drawPanelType('*fore', PANEL_FORE, g_rlayer_fore);
1587 if g_debug_HealthBar then
1588 begin
1589 r_Monsters_DrawHealth();
1590 r_Player_DrawHealth();
1591 end;
1593 if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering
1594 end;
1597 procedure DrawMapView(x, y, w, h: Integer);
1599 var
1600 bx, by: Integer;
1601 begin
1602 glPushMatrix();
1604 bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w));
1605 by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h));
1607 sX := x;
1608 sY := y;
1609 sWidth := w;
1610 sHeight := h;
1612 fixViewportForScale();
1613 renderMapInternal(-bx, -by, true);
1615 glPopMatrix();
1616 end;
1619 procedure DrawPlayer(p: TPlayer);
1620 var
1621 px, py, a, b, c, d, i, fX, fY: Integer;
1622 camObj: TObj;
1623 //R: TRect;
1624 begin
1625 if (p = nil) or (p.FDummy) then
1626 begin
1627 glPushMatrix();
1628 r_Map_DrawBack(0, 0);
1629 glPopMatrix();
1630 Exit;
1631 end;
1633 if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size);
1634 if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw);
1636 gPlayerDrawn := p;
1638 glPushMatrix();
1640 {$IFDEF ENABLE_CORPSES}
1641 camObj := g_Corpses_GetCameraObj(p);
1642 {$ELSE}
1643 camObj := p.Obj;
1644 {$ENDIF}
1646 camObj.lerp(gLerpFactor, fX, fY);
1647 px := fX + PLAYER_RECT_CX;
1648 py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor);
1650 if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then
1651 begin
1652 if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0;
1653 if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0;
1655 if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X;
1656 if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y;
1658 if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0
1659 else if (gMapInfo.Width < gPlayerScreenSize.X) then
1660 begin
1661 // hcenter
1662 a := (gPlayerScreenSize.X-gMapInfo.Width) div 2;
1663 end;
1665 if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0
1666 else if (gMapInfo.Height < gPlayerScreenSize.Y) then
1667 begin
1668 // vcenter
1669 b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2;
1670 end;
1671 end
1672 else
1673 begin
1674 // scaled, ignore level bounds
1675 a := -px+(gPlayerScreenSize.X div 2);
1676 b := -py+(gPlayerScreenSize.Y div 2);
1677 end;
1679 sX := -a;
1680 sY := -b;
1681 sWidth := gPlayerScreenSize.X;
1682 sHeight := gPlayerScreenSize.Y;
1683 fixViewportForScale();
1685 i := py - (sY + sHeight div 2);
1686 if (p.IncCam > 0) then
1687 begin
1688 // clamp to level bounds
1689 if (sY - p.IncCam < 0) then
1690 p.IncCam := nclamp(sY, 0, 120);
1691 // clamp around player position
1692 if (i > 0) then
1693 p.IncCam := nclamp(p.IncCam, 0, max(0, 120 - i));
1694 end
1695 else if (p.IncCam < 0) then
1696 begin
1697 // clamp to level bounds
1698 if (sY + sHeight - p.IncCam > gMapInfo.Height) then
1699 p.IncCam := nclamp(sY + sHeight - gMapInfo.Height, -120, 0);
1700 // clamp around player position
1701 if (i < 0) then
1702 p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0);
1703 end;
1705 sY := sY - nlerp(p.IncCamOld, p.IncCam, gLerpFactor);
1707 if (not g_dbg_ignore_bounds) then
1708 begin
1709 if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth;
1710 if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight;
1711 if (sX < 0) then sX := 0;
1712 if (sY < 0) then sY := 0;
1713 end;
1715 if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth));
1716 if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight));
1718 //r_smallmap_h: 0: left; 1: center; 2: right
1719 //r_smallmap_v: 0: top; 1: center; 2: bottom
1720 // horiz small map?
1721 if (gMapInfo.Width = sWidth) then
1722 begin
1723 sX := 0;
1724 end
1725 else if (gMapInfo.Width < sWidth) then
1726 begin
1727 case r_smallmap_h of
1728 1: sX := -((sWidth-gMapInfo.Width) div 2); // center
1729 2: sX := -(sWidth-gMapInfo.Width); // right
1730 else sX := 0; // left
1731 end;
1732 end;
1733 // vert small map?
1734 if (gMapInfo.Height = sHeight) then
1735 begin
1736 sY := 0;
1737 end
1738 else if (gMapInfo.Height < sHeight) then
1739 begin
1740 case r_smallmap_v of
1741 1: sY := -((sHeight-gMapInfo.Height) div 2); // center
1742 2: sY := -(sHeight-gMapInfo.Height); // bottom
1743 else sY := 0; // top
1744 end;
1745 end;
1747 p.viewPortX := sX;
1748 p.viewPortY := sY;
1749 p.viewPortW := sWidth;
1750 p.viewPortH := sHeight;
1752 {$IFDEF ENABLE_HOLMES}
1753 if (p = gPlayer1) then
1754 begin
1755 g_Holmes_plrViewPos(sX, sY);
1756 g_Holmes_plrViewSize(sWidth, sHeight);
1757 end;
1758 {$ENDIF}
1760 renderMapInternal(-c, -d, true);
1762 if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then
1763 case gPlayerIndicator of
1764 1:
1765 r_Player_DrawIndicator(p, _RGB(255, 255, 255));
1767 2:
1768 for i := 0 to High(gPlayers) do
1769 if gPlayers[i] <> nil then
1770 if gPlayers[i] = p then
1771 r_Player_DrawIndicator(p, _RGB(255, 255, 255))
1772 else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then
1773 if gPlayerIndicatorStyle = 1 then
1774 r_Player_DrawIndicator(gPlayers[i], _RGB(192, 192, 192))
1775 else
1776 r_Player_DrawIndicator(gPlayers[i], gPlayers[i].GetColor);
1777 end;
1780 for a := 0 to High(gCollideMap) do
1781 for b := 0 to High(gCollideMap[a]) do
1782 begin
1783 d := 0;
1784 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
1785 d := d + 1;
1786 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
1787 d := d + 2;
1789 case d of
1790 1: e_DrawPoint(1, b, a, 200, 200, 200);
1791 2: e_DrawPoint(1, b, a, 64, 64, 255);
1792 3: e_DrawPoint(1, b, a, 255, 0, 255);
1793 end;
1794 end;
1797 glPopMatrix();
1799 r_Player_DrawPain(p);
1800 r_Player_DrawPickup(p);
1801 r_Player_DrawRulez(p);
1802 if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128));
1803 if g_Debug_Player then
1804 r_Player_DrawDebug(p);
1805 r_Player_DrawGUI(p);
1806 end;
1808 procedure drawProfilers ();
1809 var
1810 px: Integer = -1;
1811 py: Integer = -1;
1812 begin
1813 if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw);
1814 if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end;
1815 if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end;
1816 end;
1818 procedure r_Game_Draw();
1819 var
1820 ID: DWORD;
1821 w, h: Word;
1822 ww, hh: Byte;
1823 Time: Int64;
1824 back: string;
1825 plView1, plView2: TPlayer;
1826 Split: Boolean;
1827 MsgLineLength: Integer;
1828 MsgText: String;
1829 begin
1830 if gExit = EXIT_QUIT then Exit;
1832 Time := GetTickCount64() {div 1000};
1833 FPSCounter := FPSCounter+1;
1834 if Time - FPSTime >= 1000 then
1835 begin
1836 FPS := FPSCounter;
1837 FPSCounter := 0;
1838 FPSTime := Time;
1839 end;
1841 e_SetRendertarget(True);
1842 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
1844 if gGameOn or (gState = STATE_FOLD) then
1845 begin
1846 if (gPlayer1 <> nil) and (gPlayer2 <> nil) then
1847 begin
1848 gSpectMode := SPECT_NONE;
1849 if not gRevertPlayers then
1850 begin
1851 plView1 := gPlayer1;
1852 plView2 := gPlayer2;
1853 end
1854 else
1855 begin
1856 plView1 := gPlayer2;
1857 plView2 := gPlayer1;
1858 end;
1859 end
1860 else
1861 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
1862 begin
1863 gSpectMode := SPECT_NONE;
1864 if gPlayer2 = nil then
1865 plView1 := gPlayer1
1866 else
1867 plView1 := gPlayer2;
1868 plView2 := nil;
1869 end
1870 else
1871 begin
1872 plView1 := nil;
1873 plView2 := nil;
1874 end;
1876 if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then
1877 gSpectMode := SPECT_STATS;
1879 if gSpectMode = SPECT_PLAYERS then
1880 if gPlayers <> nil then
1881 begin
1882 plView1 := GetActivePlayer_ByID(gSpectPID1);
1883 if plView1 = nil then
1884 begin
1885 gSpectPID1 := GetActivePlayerID_Next();
1886 plView1 := GetActivePlayer_ByID(gSpectPID1);
1887 end;
1888 if gSpectViewTwo then
1889 begin
1890 plView2 := GetActivePlayer_ByID(gSpectPID2);
1891 if plView2 = nil then
1892 begin
1893 gSpectPID2 := GetActivePlayerID_Next();
1894 plView2 := GetActivePlayer_ByID(gSpectPID2);
1895 end;
1896 end;
1897 end;
1899 if gSpectMode = SPECT_MAPVIEW then
1900 begin
1901 // Режим просмотра карты
1902 Split := False;
1903 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
1904 DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight);
1905 gHearPoint1.Active := True;
1906 gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX;
1907 gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY;
1908 gHearPoint2.Active := False;
1909 end
1910 else
1911 begin
1912 Split := (plView1 <> nil) and (plView2 <> nil);
1914 // Точки слуха игроков
1915 if plView1 <> nil then
1916 begin
1917 gHearPoint1.Active := True;
1918 gHearPoint1.Coords.X := plView1.GameX + PLAYER_RECT.Width;
1919 gHearPoint1.Coords.Y := plView1.GameY + PLAYER_RECT.Height DIV 2;
1920 end else
1921 gHearPoint1.Active := False;
1922 if plView2 <> nil then
1923 begin
1924 gHearPoint2.Active := True;
1925 gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width;
1926 gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2;
1927 end else
1928 gHearPoint2.Active := False;
1930 // Размер экранов игроков:
1931 gPlayerScreenSize.X := gScreenWidth-196;
1932 if Split then
1933 begin
1934 gPlayerScreenSize.Y := gScreenHeight div 2;
1935 if gScreenHeight mod 2 = 0 then
1936 Dec(gPlayerScreenSize.Y);
1937 end
1938 else
1939 gPlayerScreenSize.Y := gScreenHeight;
1941 if Split then
1942 if gScreenHeight mod 2 = 0 then
1943 e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y)
1944 else
1945 e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
1947 DrawPlayer(plView1);
1948 gPlayer1ScreenCoord.X := sX;
1949 gPlayer1ScreenCoord.Y := sY;
1951 if Split then
1952 begin
1953 e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y);
1955 DrawPlayer(plView2);
1956 gPlayer2ScreenCoord.X := sX;
1957 gPlayer2ScreenCoord.Y := sY;
1958 end;
1960 e_SetViewPort(0, 0, gScreenWidth, gScreenHeight);
1962 if Split then
1963 e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0);
1964 end;
1966 {$IFDEF ENABLE_HOLMES}
1967 // draw inspector
1968 if (g_holmes_enabled) then g_Holmes_Draw();
1969 {$ENDIF}
1971 if MessageText <> '' then
1972 begin
1973 w := 0;
1974 h := 0;
1975 e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h);
1976 MsgLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont);
1977 MsgText := b_Text_Wrap(b_Text_Format(MessageText), MsgLineLength);
1978 if Split then
1979 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
1980 (gScreenHeight div 2)-(h div 2), MsgText)
1981 else
1982 e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2),
1983 Round(gScreenHeight / 2.75)-(h div 2), MsgText);
1984 end;
1986 if IsDrawStat or (gSpectMode = SPECT_STATS) then
1987 DrawStat();
1989 if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then
1990 begin
1991 // Draw spectator GUI
1992 ww := 0;
1993 hh := 0;
1994 e_TextureFontGetSize(gStdFont, ww, hh);
1995 case gSpectMode of
1996 SPECT_STATS:
1997 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1);
1998 SPECT_MAPVIEW:
1999 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1);
2000 SPECT_PLAYERS:
2001 e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1);
2002 end;
2003 e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1);
2004 if gSpectMode = SPECT_STATS then
2005 begin
2006 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1);
2007 e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1);
2008 end;
2009 if gSpectMode = SPECT_MAPVIEW then
2010 begin
2011 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1);
2012 e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1);
2013 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1);
2014 e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '<prev weap>', gStdFont, 255, 255, 255, 1);
2015 e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '<next weap>', gStdFont, 255, 255, 255, 1);
2016 end;
2017 if gSpectMode = SPECT_PLAYERS then
2018 begin
2019 e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1);
2020 e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '<left/right>', gStdFont, 255, 255, 255, 1);
2021 if gSpectViewTwo then
2022 begin
2023 e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1);
2024 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<prev w/next w>', gStdFont, 255, 255, 255, 1);
2025 e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
2026 e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
2027 end
2028 else
2029 begin
2030 e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1);
2031 e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '<up/down>', gStdFont, 255, 255, 255, 1);
2032 end;
2033 end;
2034 end;
2035 end;
2037 {$IFDEF ENABLE_MENU}
2038 if gPauseMain and gGameOn and (g_ActiveWindow = nil) then
2039 {$ELSE}
2040 if gPauseMain and gGameOn then
2041 {$ENDIF}
2042 begin
2043 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2044 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2046 e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h);
2047 e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2),
2048 (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]);
2049 end;
2051 if not gGameOn then
2052 begin
2053 {$IFDEF ENABLE_MENU}
2054 if (gState = STATE_MENU) then
2055 begin
2056 if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then r_Game_DrawMenuBackground('MENU_BACKGROUND');
2057 // F3 at menu will show game loading dialog
2058 if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true);
2059 if (g_ActiveWindow <> nil) then
2060 begin
2061 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2062 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2063 end
2064 else
2065 begin
2066 // F3 at titlepic will show game loading dialog
2067 if e_KeyPressed(IK_F3) then
2068 begin
2069 g_Menu_Show_LoadMenu(true);
2070 if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2071 end;
2072 end;
2073 end;
2074 {$ELSE}
2075 r_Game_DrawMenuBackground('MENU_BACKGROUND');
2076 {$ENDIF}
2078 if gState = STATE_FOLD then
2079 begin
2080 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
2081 end;
2083 if gState = STATE_INTERCUSTOM then
2084 begin
2085 if gLastMap and (gGameSettings.GameMode = GM_COOP) then
2086 begin
2087 back := 'TEXTURE_endpic';
2088 if not g_Texture_Get(back, ID) then
2089 back := _lc[I_TEXTURE_ENDPIC];
2090 end
2091 else
2092 back := 'INTER';
2094 r_Game_DrawMenuBackground(back);
2096 DrawCustomStat();
2098 {$IFDEF ENABLE_MENU}
2099 if g_ActiveWindow <> nil then
2100 begin
2101 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2102 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2103 end;
2104 {$ENDIF}
2105 end;
2107 if gState = STATE_INTERSINGLE then
2108 begin
2109 if EndingGameCounter > 0 then
2110 begin
2111 e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter);
2112 end
2113 else
2114 begin
2115 back := 'INTER';
2117 r_Game_DrawMenuBackground(back);
2119 DrawSingleStat();
2121 {$IFDEF ENABLE_MENU}
2122 if g_ActiveWindow <> nil then
2123 begin
2124 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2125 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2126 end;
2127 {$ENDIF}
2128 end;
2129 end;
2131 if gState = STATE_ENDPIC then
2132 begin
2133 ID := DWORD(-1);
2134 if g_Texture_Get('TEXTURE_endpic', ID) then r_Game_DrawMenuBackground('TEXTURE_endpic')
2135 else r_Game_DrawMenuBackground(_lc[I_TEXTURE_ENDPIC]);
2137 {$IFDEF ENABLE_MENU}
2138 if g_ActiveWindow <> nil then
2139 begin
2140 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2141 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2142 end;
2143 {$ENDIF}
2144 end;
2146 if gState = STATE_SLIST then
2147 begin
2148 // if g_Texture_Get('MENU_BACKGROUND', ID) then
2149 // begin
2150 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
2151 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2152 // end;
2153 r_Game_DrawMenuBackground('MENU_BACKGROUND');
2154 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2155 r_Serverlist_Draw(slCurrent, slTable);
2156 end;
2157 end;
2159 {$IFDEF ENABLE_MENU}
2160 if g_ActiveWindow <> nil then
2161 begin
2162 if gGameOn then
2163 begin
2164 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
2165 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
2166 end;
2167 g_ActiveWindow.Draw();
2168 end;
2169 {$ENDIF}
2171 r_Console_Draw();
2173 if g_debug_Sounds and gGameOn then
2174 begin
2175 for w := 0 to High(e_SoundsArray) do
2176 for h := 0 to e_SoundsArray[w].nRefs do
2177 e_DrawPoint(1, w+100, h+100, 255, 0, 0);
2178 end;
2180 if gShowFPS then
2181 begin
2182 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont);
2183 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont);
2184 end;
2186 if gGameOn and gShowTime then
2187 drawTime(gScreenWidth-72, gScreenHeight-16);
2189 if gGameOn then drawProfilers();
2191 // TODO: draw this after the FBO and remap mouse click coordinates
2193 {$IFDEF ENABLE_HOLMES}
2194 g_Holmes_DrawUI();
2195 {$ENDIF}
2197 // blit framebuffer to screen
2199 e_SetRendertarget(False);
2200 e_SetViewPort(0, 0, gWinSizeX, gWinSizeY);
2201 e_BlitFramebuffer(gWinSizeX, gWinSizeY);
2202 end;
2204 end.