1 (* Copyright (C) Doom 2D: Forever Developers
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.
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.
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/>.
15 {$INCLUDE ../../shared/a_modes.inc}
20 procedure r_Game_Draw
;
21 procedure r_Game_DrawLoadingStat
;
22 procedure r_Game_DrawMenuBackground (tex
: AnsiString);
27 {$INCLUDE ../nogl/noGLuses.inc}
28 SysUtils
, Classes
, Math
,
31 MAPDEF
, xprofiler
, utils
, wadreader
,
32 g_textures
, e_input
, e_sound
,
33 g_language
, g_console
, g_menu
, g_triggers
, g_player
, g_options
, g_monsters
, g_map
, g_panel
, g_window
,
34 g_items
, g_weapons
, g_gfx
, g_phys
, g_net
, g_gui
, g_netmaster
,
35 g_game
, r_console
, r_gfx
, r_items
, r_map
, r_panel
, r_monsters
39 profileFrameDraw
: TProfiler
= nil;
45 function GetActivePlayer_ByID(ID
: Integer): TPlayer
;
52 if gPlayers
= nil then
54 for a
:= Low(gPlayers
) to High(gPlayers
) do
55 if IsActivePlayer(gPlayers
[a
]) then
57 if gPlayers
[a
].UID
<> ID
then
59 Result
:= gPlayers
[a
];
64 function calcProfilesHeight (prof
: TProfiler
): Integer;
67 if (prof
= nil) then exit
;
68 if (length(prof
.bars
) = 0) then exit
;
69 result
:= length(prof
.bars
)*(16+2);
73 function drawProfiles (x
, y
: Integer; prof
: TProfiler
): Integer;
80 if (prof
= nil) then exit
;
82 if (length(prof
.bars
) = 0) then exit
;
84 hgt
:= calcProfilesHeight(prof
);
85 if (x
< 0) then x
:= gScreenWidth
-(wdt
-1)+x
;
86 if (y
< 0) then y
:= gScreenHeight
-(hgt
-1)+y
;
88 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND);
89 //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE);
90 e_DarkenQuadWH(x
, y
, wdt
, hgt
, 150);
93 for ii
:= 0 to High(prof
.bars
) do
95 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);
101 procedure drawTime(X
, Y
: Integer); inline;
103 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
);
106 procedure DrawStat();
108 pc
, x
, y
, w
, h
: Integer;
109 w1
, w2
, w3
, w4
: Integer;
111 cw
, ch
, r
, g
, b
, rr
, gg
, bb
: Byte;
114 stat
: TPlayerStatArray
;
122 pc
:= g_Player_GetCount
;
123 e_TextureFontGetSize(gStdFont
, cw
, ch
);
125 w
:= gScreenWidth
-(gScreenWidth
div 5);
126 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
129 h
:= 40+ch
*5+(ch
+8)*pc
;
130 x
:= (gScreenWidth
div 2)-(w
div 2);
131 y
:= (gScreenHeight
div 2)-(h
div 2);
133 e_DrawFillQuad(x
, y
, x
+w
-1, y
+h
-1, 64, 64, 64, 32);
134 e_DrawQuad(x
, y
, x
+w
-1, y
+h
-1, 255, 127, 0);
136 drawTime(x
+w
-78, y
+8);
138 wad
:= g_ExtractWadNameNoPath(gMapInfo
.Map
);
139 map
:= g_ExtractFileName(gMapInfo
.Map
);
140 mapstr
:= wad
+ ':\' + map
+ ' - ' + gMapInfo
.Name
;
142 case gGameSettings
.GameMode
of
145 if gGameSettings
.MaxLives
= 0 then
148 s1
:= _lc
[I_GAME_LMS
];
149 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
150 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
155 if gGameSettings
.MaxLives
= 0 then
156 s1
:= _lc
[I_GAME_TDM
]
158 s1
:= _lc
[I_GAME_TLMS
];
159 s2
:= Format(_lc
[I_GAME_FRAG_LIMIT
], [gGameSettings
.ScoreLimit
]);
160 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
165 s1
:= _lc
[I_GAME_CTF
];
166 s2
:= Format(_lc
[I_GAME_SCORE_LIMIT
], [gGameSettings
.ScoreLimit
]);
167 s3
:= Format(_lc
[I_GAME_TIME_LIMIT
], [gGameSettings
.TimeLimit
div 3600, (gGameSettings
.TimeLimit
div 60) mod 60, gGameSettings
.TimeLimit
mod 60]);
172 if gGameSettings
.MaxLives
= 0 then
173 s1
:= _lc
[I_GAME_COOP
]
175 s1
:= _lc
[I_GAME_SURV
];
176 s2
:= _lc
[I_GAME_MONSTERS
] + ' ' + IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
);
177 s3
:= _lc
[I_GAME_SECRETS
] + ' ' + IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
);
187 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*cw
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
189 e_TextureFontPrintEx(x
+(w
div 2)-(Length(mapstr
)*cw
div 2), _y
, mapstr
, gStdFont
, 200, 200, 200, 1);
191 e_TextureFontPrintEx(x
+16, _y
, s2
, gStdFont
, 200, 200, 200, 1);
193 e_TextureFontPrintEx(x
+w
-16-(Length(s3
))*cw
, _y
, s3
,
194 gStdFont
, 200, 200, 200, 1);
196 if NetMode
= NET_SERVER
then
197 e_TextureFontPrintEx(x
+8, y
+ 8, _lc
[I_NET_SERVER
], gStdFont
, 255, 255, 255, 1)
199 if NetMode
= NET_CLIENT
then
200 e_TextureFontPrintEx(x
+8, y
+ 8,
201 NetClientIP
+ ':' + IntToStr(NetClientPort
), gStdFont
, 255, 255, 255, 1);
205 stat
:= g_Player_GetStats();
208 w2
:= (w
-16) div 6 + 48; // ширина 2 столбца
209 w3
:= (w
-16) div 6; // ширина 3 и 4 столбцов
211 w1
:= w
-16-w2
-w3
-w4
; // оставшееся пространство - для цвета и имени игрока
213 if gGameSettings
.GameMode
in [GM_TDM
, GM_CTF
] then
217 for a
:= TEAM_RED
to TEAM_BLUE
do
221 s1
:= _lc
[I_GAME_TEAM_RED
];
228 s1
:= _lc
[I_GAME_TEAM_BLUE
];
234 e_TextureFontPrintEx(x
+16, _y
, s1
, gStdFont
, r
, g
, b
, 1);
235 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(gTeamStat
[a
].Score
),
236 gStdFont
, r
, g
, b
, 1);
238 _y
:= _y
+ch
+(ch
div 4);
239 e_DrawLine(1, x
+16, _y
, x
+w
-16, _y
, r
, g
, b
);
242 for aa
:= 0 to High(stat
) do
243 if stat
[aa
].Team
= a
then
259 namestr
:= Format('[%5d] %s', [UID
, Name
])
263 e_TextureFontPrintEx(x
+16, _y
, namestr
, gStdFont
, rr
, gg
, bb
, 1);
265 e_TextureFontPrintEx(x
+w1
+16, _y
, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, rr
, gg
, bb
, 1);
267 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
269 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
276 else if gGameSettings
.GameMode
in [GM_DM
, GM_COOP
] then
279 e_TextureFontPrintEx(x
+16, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
280 e_TextureFontPrintEx(x
+16+w1
, _y
, _lc
[I_GAME_PING
], gStdFont
, 255, 127, 0, 1);
281 e_TextureFontPrintEx(x
+16+w1
+w2
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
282 e_TextureFontPrintEx(x
+16+w1
+w2
+w3
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
285 for aa
:= 0 to High(stat
) do
299 namestr
:= Format('[%5d] %s', [UID
, Name
])
303 e_DrawFillQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
304 e_DrawQuad(x
+16, _y
+4, x
+32-1, _y
+16+4-1, 192, 192, 192);
306 e_TextureFontPrintEx(x
+16+16+8, _y
+4, namestr
, gStdFont
, r
, g
, 0, 1);
308 e_TextureFontPrintEx(x
+w1
+16, _y
+4, Format(_lc
[I_GAME_PING_MS
], [Ping
, Loss
]), gStdFont
, r
, g
, 0, 1);
310 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
+4, IntToStr(Frags
), gStdFont
, r
, g
, 0, 1);
312 e_TextureFontPrintEx(x
+w1
+w2
+w3
+16, _y
+4, IntToStr(Deaths
), gStdFont
, r
, g
, 0, 1);
318 procedure DrawCustomStat();
324 ww2
, hh2
, r
, g
, b
, rr
, gg
, bb
: Byte;
325 s1
, s2
, topstr
: String;
327 e_TextureFontGetSize(gStdFont
, ww2
, hh2
);
331 if g_Console_Action(ACTION_SCORES
) then
333 if not gStatsPressed
then
335 gStatsOff
:= not gStatsOff
;
336 gStatsPressed
:= True;
340 gStatsPressed
:= False;
344 s1
:= _lc
[I_MENU_INTER_NOTICE_TAB
];
345 w
:= (Length(s1
) * ww2
) div 2;
346 x
:= gScreenWidth
div 2 - w
;
348 e_TextureFontPrint(x
, y
, s1
, gStdFont
);
352 if (gGameSettings
.GameMode
= GM_COOP
) then
354 if gMissionFailed
then
355 topstr
:= _lc
[I_MENU_INTER_MISSION_FAIL
]
357 topstr
:= _lc
[I_MENU_INTER_LEVEL_COMPLETE
];
360 topstr
:= _lc
[I_MENU_INTER_ROUND_OVER
];
362 e_CharFont_GetSize(gMenuFont
, topstr
, ww1
, hh1
);
363 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww1
div 2), 16, topstr
);
367 topstr
:= Format(_lc
[I_MENU_INTER_NOTICE_TIME
], [gServInterTime
]);
368 if not gChatShow
then
369 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
370 gScreenHeight
-(hh2
+4)*2, topstr
, gStdFont
, 255, 255, 255, 1);
373 if g_Game_IsClient
then
374 topstr
:= _lc
[I_MENU_INTER_NOTICE_MAP
]
376 topstr
:= _lc
[I_MENU_INTER_NOTICE_SPACE
];
377 if not gChatShow
then
378 e_TextureFontPrintEx((gScreenWidth
div 2)-(Length(topstr
)*ww2
div 2),
379 gScreenHeight
-(hh2
+4), topstr
, gStdFont
, 255, 255, 255, 1);
384 w
:= gScreenWidth
-x
*2;
390 e_DrawFillQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 64, 64, 64, 32);
391 e_DrawQuad(x
, y
, gScreenWidth
-x
-1, gScreenHeight
-y
-1, 255, 127, 0);
393 m
:= Max(Length(_lc
[I_MENU_MAP
])+1, Length(_lc
[I_GAME_GAME_TIME
])+1)*ww2
;
395 case CustomStat
.GameMode
of
398 if gGameSettings
.MaxLives
= 0 then
401 s1
:= _lc
[I_GAME_LMS
];
405 if gGameSettings
.MaxLives
= 0 then
406 s1
:= _lc
[I_GAME_TDM
]
408 s1
:= _lc
[I_GAME_TLMS
];
410 GM_CTF
: s1
:= _lc
[I_GAME_CTF
];
413 if gGameSettings
.MaxLives
= 0 then
414 s1
:= _lc
[I_GAME_COOP
]
416 s1
:= _lc
[I_GAME_SURV
];
422 e_TextureFontPrintEx(x
+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
426 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_MENU_MAP
], gStdFont
, 255, 127, 0, 1);
427 e_TextureFontPrint(x
+8+m
, _y
, Format('%s - %s', [CustomStat
.Map
, CustomStat
.MapName
]), gStdFont
);
430 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_GAME_TIME
], gStdFont
, 255, 127, 0, 1);
431 e_TextureFontPrint(x
+8+m
, _y
, Format('%d:%.2d:%.2d', [CustomStat
.GameTime
div 1000 div 3600,
432 (CustomStat
.GameTime
div 1000 div 60) mod 60,
433 CustomStat
.GameTime
div 1000 mod 60]), gStdFont
);
435 pc
:= Length(CustomStat
.PlayerStat
);
438 if CustomStat
.GameMode
= GM_COOP
then
440 m
:= Max(Length(_lc
[I_GAME_MONSTERS
])+1, Length(_lc
[I_GAME_SECRETS
])+1)*ww2
;
442 s2
:= _lc
[I_GAME_MONSTERS
];
443 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
444 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopMonstersKilled
) + '/' + IntToStr(gTotalMonsters
), gStdFont
, 255, 255, 255, 1);
446 s2
:= _lc
[I_GAME_SECRETS
];
447 e_TextureFontPrintEx(x
+8, _y
, s2
, gStdFont
, 255, 127, 0, 1);
448 e_TextureFontPrintEx(x
+8+m
, _y
, IntToStr(gCoopSecretsFound
) + '/' + IntToStr(gSecretsCount
), gStdFont
, 255, 255, 255, 1);
451 m
:= Max(Length(_lc
[I_GAME_MONSTERS_TOTAL
])+1, Length(_lc
[I_GAME_SECRETS_TOTAL
])+1)*ww2
;
453 s2
:= _lc
[I_GAME_MONSTERS_TOTAL
];
454 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
455 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalMonstersKilled
) + '/' + IntToStr(gCoopTotalMonsters
), gStdFont
, 255, 255, 255, 1);
457 s2
:= _lc
[I_GAME_SECRETS_TOTAL
];
458 e_TextureFontPrintEx(x
+250, _y
, s2
, gStdFont
, 255, 127, 0, 1);
459 e_TextureFontPrintEx(x
+250+m
, _y
, IntToStr(gCoopTotalSecretsFound
) + '/' + IntToStr(gCoopTotalSecrets
), gStdFont
, 255, 255, 255, 1);
463 if CustomStat
.GameMode
in [GM_TDM
, GM_CTF
] then
468 if TeamStat
[TEAM_RED
].Score
> TeamStat
[TEAM_BLUE
].Score
then s1
:= _lc
[I_GAME_WIN_RED
]
469 else if TeamStat
[TEAM_BLUE
].Score
> TeamStat
[TEAM_RED
].Score
then s1
:= _lc
[I_GAME_WIN_BLUE
]
470 else s1
:= _lc
[I_GAME_WIN_DRAW
];
472 e_TextureFontPrintEx(x
+8+(w
div 2)-(Length(s1
)*ww2
div 2), _y
, s1
, gStdFont
, 255, 255, 255, 1);
475 for t
:= TEAM_RED
to TEAM_BLUE
do
479 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_RED
],
480 gStdFont
, 255, 0, 0, 1);
481 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_RED
].Score
),
482 gStdFont
, 255, 0, 0, 1);
489 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_TEAM_BLUE
],
490 gStdFont
, 0, 0, 255, 1);
491 e_TextureFontPrintEx(x
+w1
+8, _y
, IntToStr(CustomStat
.TeamStat
[TEAM_BLUE
].Score
),
492 gStdFont
, 0, 0, 255, 1);
498 e_DrawLine(1, x
+8, _y
+20, x
-8+w
, _y
+20, r
, g
, b
);
501 for p
:= 0 to High(CustomStat
.PlayerStat
) do
502 if CustomStat
.PlayerStat
[p
].Team
= t
then
503 with CustomStat
.PlayerStat
[p
] do
517 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
518 e_TextureFontPrintEx(x
+16, _y
, Name
+ ' *', gStdFont
, rr
, gg
, bb
, 1)
520 e_TextureFontPrintEx(x
+16, _y
, Name
, gStdFont
, rr
, gg
, bb
, 1);
521 e_TextureFontPrintEx(x
+w1
+16, _y
, IntToStr(Frags
), gStdFont
, rr
, gg
, bb
, 1);
522 e_TextureFontPrintEx(x
+w1
+w2
+16, _y
, IntToStr(Deaths
), gStdFont
, rr
, gg
, bb
, 1);
529 else if CustomStat
.GameMode
in [GM_DM
, GM_COOP
] then
532 e_TextureFontPrintEx(x
+8, _y
, _lc
[I_GAME_PLAYER_NAME
], gStdFont
, 255, 127, 0, 1);
533 e_TextureFontPrintEx(x
+8+w1
, _y
, _lc
[I_GAME_FRAGS
], gStdFont
, 255, 127, 0, 1);
534 e_TextureFontPrintEx(x
+8+w1
+w2
, _y
, _lc
[I_GAME_DEATHS
], gStdFont
, 255, 127, 0, 1);
537 for p
:= 0 to High(CustomStat
.PlayerStat
) do
538 with CustomStat
.PlayerStat
[p
] do
540 e_DrawFillQuad(x
+8, _y
+4, x
+24-1, _y
+16+4-1, Color
.R
, Color
.G
, Color
.B
, 0);
547 if (gPlayers
[Num
] <> nil) and (gPlayers
[Num
].FReady
) then
548 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
+ ' *', gStdFont
, r
, r
, r
, 1, True)
550 e_TextureFontPrintEx(x
+8+16+8, _y
+4, Name
, gStdFont
, r
, r
, r
, 1, True);
551 e_TextureFontPrintEx(x
+w1
+8+16+8, _y
+4, IntToStr(Frags
), gStdFont
, r
, r
, r
, 1, True);
552 e_TextureFontPrintEx(x
+w1
+w2
+8+16+8, _y
+4, IntToStr(Deaths
), gStdFont
, r
, r
, r
, 1, True);
557 // HACK: take stats screenshot immediately after the first frame of the stats showing
558 if gScreenshotStats
and (not StatShotDone
) and (Length(CustomStat
.PlayerStat
) > 1) then
560 g_TakeScreenShot('stats/' + StatFilename
);
561 StatShotDone
:= True;
565 procedure DrawSingleStat();
567 tm
, key_x
, val_x
, y
: Integer;
571 procedure player_stat(n
: Integer);
577 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Kills
]);
578 s2
:= Format(' %d', [gTotalMonsters
]);
580 e_CharFont_Print(gMenuFont
, key_x
, y
, _lc
[I_MENU_INTER_KILLS
]);
581 e_CharFont_PrintEx(gMenuFont
, val_x
, y
, s1
, _RGB(255, 0, 0));
582 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
583 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
, '/');
585 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
586 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
, s2
, _RGB(255, 0, 0));
588 // "Kills-per-minute: ##.#":
589 s1
:= _lc
[I_MENU_INTER_KPM
];
591 kpm
:= (SingleStat
.PlayerStat
[n
].Kills
/ tm
) * 60
593 kpm
:= SingleStat
.PlayerStat
[n
].Kills
;
594 s2
:= Format(' %.1f', [kpm
]);
596 e_CharFont_Print(gMenuFont
, key_x
, y
+32, s1
);
597 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+32, s2
, _RGB(255, 0, 0));
599 // "Secrets found: # / #":
600 s1
:= Format(' %d ', [SingleStat
.PlayerStat
[n
].Secrets
]);
601 s2
:= Format(' %d', [SingleStat
.TotalSecrets
]);
603 e_CharFont_Print(gMenuFont
, key_x
, y
+64, _lc
[I_MENU_INTER_SECRETS
]);
604 e_CharFont_PrintEx(gMenuFont
, val_x
, y
+64, s1
, _RGB(255, 0, 0));
605 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
606 e_CharFont_Print(gMenuFont
, val_x
+w1
, y
+64, '/');
608 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
609 e_CharFont_PrintEx(gMenuFont
, val_x
+w1
, y
+64, s2
, _RGB(255, 0, 0));
614 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_INTER_LEVEL_COMPLETE
], w1
, h
);
615 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 32, _lc
[I_MENU_INTER_LEVEL_COMPLETE
]);
617 // Определяем координаты выравнивания по самой длинной строке:
618 s1
:= _lc
[I_MENU_INTER_KPM
];
619 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
622 e_CharFont_GetSize(gMenuFont
, s1
, w2
, h
);
624 key_x
:= (gScreenWidth
-w1
-w2
) div 2;
628 tm
:= SingleStat
.GameTime
div 1000;
629 s1
:= _lc
[I_MENU_INTER_TIME
];
630 s2
:= Format(' %d:%.2d:%.2d', [tm
div (60*60), (tm
mod (60*60)) div 60, tm
mod 60]);
632 e_CharFont_Print(gMenuFont
, key_x
, 80, s1
);
633 e_CharFont_PrintEx(gMenuFont
, val_x
, 80, s2
, _RGB(255, 0, 0));
635 if SingleStat
.TwoPlayers
then
638 s1
:= _lc
[I_MENU_PLAYER_1
];
639 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
640 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 128, s1
);
642 // Статистика первого игрока:
647 s1
:= _lc
[I_MENU_PLAYER_2
];
648 e_CharFont_GetSize(gMenuFont
, s1
, w1
, h
);
649 e_CharFont_Print(gMenuFont
, (gScreenWidth
-w1
) div 2, 288, s1
);
651 // Статистика второго игрока:
657 // Статистика первого игрока:
663 procedure r_Game_DrawLoadingStat
;
664 procedure drawRect (x
, y
, w
, h
: Integer);
666 if (w
< 1) or (h
< 1) then exit
;
668 glVertex2f(x
+0.375, y
+0.375);
669 glVertex2f(x
+w
+0.375, y
+0.375);
670 glVertex2f(x
+w
+0.375, y
+h
+0.375);
671 glVertex2f(x
+0.375, y
+h
+0.375);
675 function drawPBar (cur
, total
: Integer; washere
: Boolean): Boolean;
677 rectW
, rectH
: Integer;
684 idl
, idr
, idb
, idm
: LongWord;
688 if (total
< 1) then exit
;
689 if (cur
< 1) then exit
; // don't blink
690 if (not washere
) and (cur
>= total
) then exit
; // don't blink
691 //if (cur < 0) then cur := 0;
692 //if (cur > total) then cur := total;
697 g_Texture_Get('UI_GFX_PBAR_LEFT', idl
);
698 g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl
, hl
);
699 g_Texture_Get('UI_GFX_PBAR_RIGHT', idr
);
700 g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr
, hr
);
701 g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb
);
702 g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb
, hb
);
703 g_Texture_Get('UI_GFX_PBAR_MARKER', idm
);
704 g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm
, hm
);
706 //rectW := gScreenWidth-360;
707 rectW
:= trunc(624.0*gScreenWidth
/1024.0);
710 x0
:= (gScreenWidth
-rectW
) div 2;
711 y0
:= gScreenHeight
-rectH
-64;
712 if (y0
< 2) then y0
:= 2;
714 glEnable(GL_SCISSOR_TEST
);
717 glScissor(x0
, gScreenHeight
-y0
-rectH
, rectW
, rectH
);
718 e_DrawSize(idl
, x0
, y0
, 0, true, false, wl
, hl
);
719 e_DrawSize(idr
, x0
+rectW
-wr
, y0
, 0, true, false, wr
, hr
);
722 glScissor(x0
+wl
, gScreenHeight
-y0
-rectH
, rectW
-wl
-wr
, rectH
);
724 while (f
< x0
+rectW
) do
726 e_DrawSize(idb
, f
, y0
, 0, true, false, wb
, hb
);
731 wdt
:= (rectW
-wl
-wr
)*cur
div total
;
732 if (wdt
> rectW
-wl
-wr
) then wdt
:= rectW
-wr
-wr
;
735 my
:= y0
; // don't be so smart, ketmar: +(rectH-wm) div 2;
736 glScissor(x0
+wl
, gScreenHeight
-my
-rectH
, wdt
, hm
);
740 e_DrawSize(idm
, f
, y0
, 0, true, false, wm
, hm
);
746 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
750 rectW
:= gScreenWidth
-64;
753 x0
:= (gScreenWidth
-rectW
) div 2;
754 y0
:= gScreenHeight
-rectH
-64;
755 if (y0
< 2) then y0
:= 2;
758 glDisable(GL_TEXTURE_2D
);
760 //glClearColor(0, 0, 0, 0);
761 //glClear(GL_COLOR_BUFFER_BIT);
763 glColor4ub(127, 127, 127, 255);
764 drawRect(x0
-2, y0
-2, rectW
+4, rectH
+4);
766 glColor4ub(0, 0, 0, 255);
767 drawRect(x0
-1, y0
-1, rectW
+2, rectH
+2);
769 glColor4ub(127, 127, 127, 255);
770 wdt
:= rectW
*cur
div total
;
771 if (wdt
> rectW
) then wdt
:= rectW
;
772 drawRect(x0
, y0
, wdt
, rectH
);
781 if (Length(LoadingStat
.Msgs
) = 0) then exit
;
783 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_LOADING
], ww
, hh
);
784 yy
:= (gScreenHeight
div 3);
785 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(ww
div 2), yy
-2*hh
, _lc
[I_MENU_LOADING
]);
786 xx
:= (gScreenWidth
div 3);
790 for i
:= 0 to NextMsg
-1 do
792 if (i
= (NextMsg
-1)) and (MaxValue
> 0) then
793 s
:= Format('%s: %d/%d', [Msgs
[i
], CurValue
, MaxValue
])
797 e_CharFont_PrintEx(gMenuSmallFont
, xx
, yy
, s
, _RGB(255, 0, 0));
798 yy
:= yy
+ LOADING_INTERLINE
;
799 PBarWasHere
:= drawPBar(CurValue
, MaxValue
, PBarWasHere
);
804 procedure r_Game_DrawMenuBackground (tex
: AnsiString);
810 if g_Texture_Get(tex
, ID
) then
812 e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
813 e_GetTextureSize(ID
, @w
, @h
);
815 w
:= round(w
* 1.333 * (gScreenHeight
/ h
))
817 w
:= trunc(w
* (gScreenHeight
/ h
));
818 e_DrawSize(ID
, (gScreenWidth
- w
) div 2, 0, 0, False, False, w
, gScreenHeight
);
820 else e_Clear(GL_COLOR_BUFFER_BIT
, 0, 0, 0);
823 procedure DrawMinimap(p
: TPlayer
; RenderRect
: e_graphics
.TRect
);
825 a
, aX
, aY
, aX2
, aY2
, Scale
, ScaleSz
: Integer;
827 function monDraw (mon
: TMonster
): Boolean;
829 result
:= false; // don't stop
834 // Левый верхний угол
835 aX
:= Obj
.X
div ScaleSz
+ 1;
836 aY
:= Obj
.Y
div ScaleSz
+ 1;
838 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
839 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
840 // Правый нижний угол
843 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 255, 0, 0);
849 if (gMapInfo
.Width
> RenderRect
.Right
- RenderRect
.Left
) or
850 (gMapInfo
.Height
> RenderRect
.Bottom
- RenderRect
.Top
) then
853 // Сколько пикселов карты в 1 пикселе мини-карты:
854 ScaleSz
:= 16 div Scale
;
855 // Размеры мини-карты:
856 aX
:= max(gMapInfo
.Width
div ScaleSz
, 1);
857 aY
:= max(gMapInfo
.Height
div ScaleSz
, 1);
859 e_DrawFillQuad(0, 0, aX
-1, aY
-1, 0, 0, 0, 0);
861 if gWalls
<> nil then
864 for a
:= 0 to High(gWalls
) do
866 if PanelType
<> 0 then
868 // Левый верхний угол:
872 aX2
:= max(Width
div ScaleSz
, 1);
873 aY2
:= max(Height
div ScaleSz
, 1);
874 // Правый нижний угол:
879 PANEL_WALL
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 208, 208, 208, 0);
880 PANEL_OPENDOOR
, PANEL_CLOSEDOOR
:
881 if Enabled
then e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 160, 160, 160, 0);
885 if gSteps
<> nil then
888 for a
:= 0 to High(gSteps
) do
890 if PanelType
<> 0 then
892 // Левый верхний угол:
896 aX2
:= max(Width
div ScaleSz
, 1);
897 aY2
:= max(Height
div ScaleSz
, 1);
898 // Правый нижний угол:
902 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 128, 128, 128, 0);
905 if gLifts
<> nil then
908 for a
:= 0 to High(gLifts
) do
910 if PanelType
<> 0 then
912 // Левый верхний угол:
916 aX2
:= max(Width
div ScaleSz
, 1);
917 aY2
:= max(Height
div ScaleSz
, 1);
918 // Правый нижний угол:
923 LIFTTYPE_UP
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 72, 36, 0);
924 LIFTTYPE_DOWN
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 116, 124, 96, 0);
925 LIFTTYPE_LEFT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 200, 80, 4, 0);
926 LIFTTYPE_RIGHT
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 252, 140, 56, 0);
930 if gWater
<> nil then
933 for a
:= 0 to High(gWater
) do
935 if PanelType
<> 0 then
937 // Левый верхний угол:
941 aX2
:= max(Width
div ScaleSz
, 1);
942 aY2
:= max(Height
div ScaleSz
, 1);
943 // Правый нижний угол:
947 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 192, 0);
950 if gAcid1
<> nil then
953 for a
:= 0 to High(gAcid1
) do
955 if PanelType
<> 0 then
957 // Левый верхний угол:
961 aX2
:= max(Width
div ScaleSz
, 1);
962 aY2
:= max(Height
div ScaleSz
, 1);
963 // Правый нижний угол:
967 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 176, 0, 0);
970 if gAcid2
<> nil then
973 for a
:= 0 to High(gAcid2
) do
975 if PanelType
<> 0 then
977 // Левый верхний угол:
981 aX2
:= max(Width
div ScaleSz
, 1);
982 aY2
:= max(Height
div ScaleSz
, 1);
983 // Правый нижний угол:
987 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 176, 0, 0, 0);
990 if gPlayers
<> nil then
993 for a
:= 0 to High(gPlayers
) do
994 if gPlayers
[a
] <> nil then with gPlayers
[a
] do
996 // Левый верхний угол:
997 aX
:= Obj
.X
div ScaleSz
+ 1;
998 aY
:= Obj
.Y
div ScaleSz
+ 1;
1000 aX2
:= max(Obj
.Rect
.Width
div ScaleSz
, 1);
1001 aY2
:= max(Obj
.Rect
.Height
div ScaleSz
, 1);
1002 // Правый нижний угол:
1003 aX2
:= aX
+ aX2
- 1;
1004 aY2
:= aY
+ aY2
- 1;
1006 if gPlayers
[a
] = p
then
1007 e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 255, 0, 0)
1010 TEAM_RED
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 0, 0, 0);
1011 TEAM_BLUE
: e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 0, 0, 255, 0);
1012 else e_DrawFillQuad(aX
, aY
, aX2
, aY2
, 255, 128, 0, 0);
1017 g_Mons_ForEach(monDraw
);
1022 procedure renderAmbientQuad (hasAmbient
: Boolean; constref ambColor
: TDFColor
);
1024 if not hasAmbient
then exit
;
1025 e_AmbientQuad(sX
, sY
, sWidth
, sHeight
, ambColor
.r
, ambColor
.g
, ambColor
.b
, ambColor
.a
);
1029 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
1030 //FIXME: broken for splitscreen mode
1031 procedure renderDynLightsInternal ();
1033 //hasAmbient: Boolean;
1034 //ambColor: TDFColor;
1036 lx
, ly
, lrad
: Integer;
1037 scxywh
: array[0..3] of GLint
;
1040 if e_NoGraphics
then exit
;
1042 //TODO: lights should be in separate grid, i think
1043 // but on the other side: grid may be slower for dynlights, as their lifetime is short
1044 if (not gwin_k8_enable_light_experiments
) or (not gwin_has_stencil
) or (g_dynLightCount
< 1) then exit
;
1047 //ambColor := gCurrentMap['light_ambient'].rgba;
1048 //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack);
1050 { // this will multiply incoming color to alpha from framebuffer
1052 glBlendFunc(GL_DST_ALPHA, GL_ONE);
1056 * light rendering: (INVALID!)
1057 * glStencilFunc(GL_EQUAL, 0, $ff);
1059 * glClear(GL_STENCIL_BUFFER_BIT);
1060 * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
1061 * draw shadow volume into stencil buffer
1062 * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1063 * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer
1065 * draw color-less quad with light alpha (WARNING! don't touch color!)
1066 * glEnable(GL_BLEND);
1067 * glBlendFunc(GL_DST_ALPHA, GL_ONE);
1068 * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting
1070 wassc
:= (glIsEnabled(GL_SCISSOR_TEST
) <> 0);
1071 if wassc
then glGetIntegerv(GL_SCISSOR_BOX
, @scxywh
[0]) else glGetIntegerv(GL_VIEWPORT
, @scxywh
[0]);
1073 // setup OpenGL parameters
1074 glStencilMask($FFFFFFFF);
1075 glStencilFunc(GL_ALWAYS
, 0, $FFFFFFFF);
1076 glEnable(GL_STENCIL_TEST
);
1077 glEnable(GL_SCISSOR_TEST
);
1078 glClear(GL_STENCIL_BUFFER_BIT
);
1079 glStencilFunc(GL_EQUAL
, 0, $ff);
1081 for lln
:= 0 to g_dynLightCount
-1 do
1083 lx
:= g_dynLights
[lln
].x
;
1084 ly
:= g_dynLights
[lln
].y
;
1085 lrad
:= g_dynLights
[lln
].radius
;
1086 if (lrad
< 3) then continue
;
1088 if (lx
-sX
+lrad
< 0) then continue
;
1089 if (ly
-sY
+lrad
< 0) then continue
;
1090 if (lx
-sX
-lrad
>= gPlayerScreenSize
.X
) then continue
;
1091 if (ly
-sY
-lrad
>= gPlayerScreenSize
.Y
) then continue
;
1093 // set scissor to optimize drawing
1094 if (g_dbg_scale
= 1.0) then
1096 glScissor((lx
-sX
)-lrad
+2, gPlayerScreenSize
.Y
-(ly
-sY
)-lrad
-1+2, lrad
*2-4, lrad
*2-4);
1100 glScissor(0, 0, gScreenWidth
, gScreenHeight
);
1102 // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale
1103 if (g_dbg_scale
<> 1.0) then glClear(GL_STENCIL_BUFFER_BIT
);
1104 glStencilOp(GL_KEEP
, GL_KEEP
, GL_INCR
);
1105 // draw extruded panels
1106 glDisable(GL_TEXTURE_2D
);
1107 glDisable(GL_BLEND
);
1108 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
); // no need to modify color buffer
1109 if (lrad
> 4) then r_Map_DrawPanelShadowVolumes(lx
, ly
, lrad
);
1110 // render light texture
1111 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
); // modify color buffer
1112 glStencilOp(GL_ZERO
, GL_ZERO
, GL_ZERO
); // draw light, and clear stencil buffer
1115 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1116 glEnable(GL_TEXTURE_2D
);
1117 // color and opacity
1118 glColor4f(g_dynLights
[lln
].r
, g_dynLights
[lln
].g
, g_dynLights
[lln
].b
, g_dynLights
[lln
].a
);
1119 glBindTexture(GL_TEXTURE_2D
, g_Texture_Light());
1121 glTexCoord2f(0.0, 0.0); glVertex2i(lx
-lrad
, ly
-lrad
); // top-left
1122 glTexCoord2f(1.0, 0.0); glVertex2i(lx
+lrad
, ly
-lrad
); // top-right
1123 glTexCoord2f(1.0, 1.0); glVertex2i(lx
+lrad
, ly
+lrad
); // bottom-right
1124 glTexCoord2f(0.0, 1.0); glVertex2i(lx
-lrad
, ly
+lrad
); // bottom-left
1129 glDisable(GL_STENCIL_TEST
);
1130 glDisable(GL_BLEND
);
1131 glDisable(GL_SCISSOR_TEST
);
1132 //glScissor(0, 0, sWidth, sHeight);
1134 glScissor(scxywh
[0], scxywh
[1], scxywh
[2], scxywh
[3]);
1135 if wassc
then glEnable(GL_SCISSOR_TEST
) else glDisable(GL_SCISSOR_TEST
);
1139 function fixViewportForScale (): Boolean;
1141 nx0
, ny0
, nw
, nh
: Integer;
1144 if (g_dbg_scale
<> 1.0) then
1147 nx0
:= round(sX
-(gPlayerScreenSize
.X
-(sWidth
*g_dbg_scale
))/2/g_dbg_scale
);
1148 ny0
:= round(sY
-(gPlayerScreenSize
.Y
-(sHeight
*g_dbg_scale
))/2/g_dbg_scale
);
1149 nw
:= round(sWidth
/g_dbg_scale
);
1150 nh
:= round(sHeight
/g_dbg_scale
);
1159 // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this!
1160 // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices!
1161 procedure renderMapInternal (backXOfs
, backYOfs
: Integer; setTransMatrix
: Boolean);
1163 TDrawCB
= procedure ();
1166 hasAmbient
: Boolean;
1168 doAmbient
: Boolean = false;
1170 procedure drawPanelType (profname
: AnsiString; panType
: DWord
; doDraw
: Boolean);
1175 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
1176 if gdbg_map_use_accel_render
then
1178 tagmask
:= panelTypeToTag(panType
);
1179 while (gDrawPanelList
.count
> 0) do
1181 pan
:= TPanel(gDrawPanelList
.front());
1182 if ((pan
.tag
and tagmask
) = 0) then break
;
1183 if doDraw
then r_Panel_Draw(pan
, doAmbient
, ambColor
);
1184 gDrawPanelList
.popFront();
1189 if doDraw
then r_Map_DrawPanels(panType
, hasAmbient
, ambColor
);
1191 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
1194 procedure drawOther (profname
: AnsiString; cb
: TDrawCB
);
1196 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin(profname
);
1197 if assigned(cb
) then cb();
1198 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
1202 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('total');
1204 // our accelerated renderer will collect all panels to gDrawPanelList
1205 // we can use panel tag to render level parts (see GridTagXXX in g_map.pas)
1206 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('collect');
1207 if gdbg_map_use_accel_render
then
1209 r_Map_CollectDrawPanels(sX
, sY
, sWidth
, sHeight
);
1211 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
1213 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionBegin('skyback');
1214 r_Map_DrawBack(backXOfs
, backYOfs
);
1215 if (profileFrameDraw
<> nil) then profileFrameDraw
.sectionEnd();
1217 if setTransMatrix
then
1219 //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0);
1220 glScalef(g_dbg_scale
, g_dbg_scale
, 1.0);
1221 glTranslatef(-sX
, -sY
, 0);
1225 ambColor
:= gCurrentMap
['light_ambient'].rgba
;
1226 hasAmbient
:= (not ambColor
.isOpaque
) or (not ambColor
.isBlack
);
1231 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
1232 glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a);
1233 glClear(GL_COLOR_BUFFER_BIT);
1236 //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')');
1239 drawPanelType('*back', PANEL_BACK
, g_rlayer_back
);
1240 drawPanelType('*step', PANEL_STEP
, g_rlayer_step
);
1241 drawOther('items', @r_Items_Draw
);
1242 drawOther('weapons', @g_Weapon_Draw
);
1243 drawOther('shells', @g_Player_DrawShells
);
1244 drawOther('drawall', @g_Player_DrawAll
);
1245 drawOther('corpses', @g_Player_DrawCorpses
);
1246 drawPanelType('*wall', PANEL_WALL
, g_rlayer_wall
);
1247 drawOther('monsters', @r_Monsters_Draw
);
1248 drawOther('itemdrop', @r_Items_DrawDrop
);
1249 drawPanelType('*door', PANEL_CLOSEDOOR
, g_rlayer_door
);
1250 drawOther('gfx', @r_GFX_Draw
);
1251 drawOther('flags', @r_Map_DrawFlags
);
1252 drawPanelType('*acid1', PANEL_ACID1
, g_rlayer_acid1
);
1253 drawPanelType('*acid2', PANEL_ACID2
, g_rlayer_acid2
);
1254 drawPanelType('*water', PANEL_WATER
, g_rlayer_water
);
1255 drawOther('dynlights', @renderDynLightsInternal
);
1257 if hasAmbient
{and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then
1259 renderAmbientQuad(hasAmbient
, ambColor
);
1263 drawPanelType('*fore', PANEL_FORE
, g_rlayer_fore
);
1266 if g_debug_HealthBar
then
1268 r_Monsters_DrawHealth();
1269 g_Player_DrawHealth();
1272 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainEnd(); // map rendering
1276 procedure DrawMapView(x
, y
, w
, h
: Integer);
1283 bx
:= Round(x
/(gMapInfo
.Width
- w
)*(gBackSize
.X
- w
));
1284 by
:= Round(y
/(gMapInfo
.Height
- h
)*(gBackSize
.Y
- h
));
1291 fixViewportForScale();
1292 renderMapInternal(-bx
, -by
, true);
1298 procedure DrawPlayer(p
: TPlayer
);
1300 px
, py
, a
, b
, c
, d
, i
, fX
, fY
: Integer;
1304 if (p
= nil) or (p
.FDummy
) then
1307 r_Map_DrawBack(0, 0);
1312 if (profileFrameDraw
= nil) then profileFrameDraw
:= TProfiler
.Create('RENDER', g_profile_history_size
);
1313 if (profileFrameDraw
<> nil) then profileFrameDraw
.mainBegin(g_profile_frame_draw
);
1319 camObj
:= p
.getCameraObj();
1320 camObj
.lerp(gLerpFactor
, fX
, fY
);
1321 px
:= fX
+ PLAYER_RECT_CX
;
1322 py
:= fY
+ PLAYER_RECT_CY
+nlerp(p
.SlopeOld
, camObj
.slopeUpLeft
, gLerpFactor
);
1324 if (g_dbg_scale
= 1.0) and (not g_dbg_ignore_bounds
) then
1326 if (px
> (gPlayerScreenSize
.X
div 2)) then a
:= -px
+(gPlayerScreenSize
.X
div 2) else a
:= 0;
1327 if (py
> (gPlayerScreenSize
.Y
div 2)) then b
:= -py
+(gPlayerScreenSize
.Y
div 2) else b
:= 0;
1329 if (px
> gMapInfo
.Width
-(gPlayerScreenSize
.X
div 2)) then a
:= -gMapInfo
.Width
+gPlayerScreenSize
.X
;
1330 if (py
> gMapInfo
.Height
-(gPlayerScreenSize
.Y
div 2)) then b
:= -gMapInfo
.Height
+gPlayerScreenSize
.Y
;
1332 if (gMapInfo
.Width
= gPlayerScreenSize
.X
) then a
:= 0
1333 else if (gMapInfo
.Width
< gPlayerScreenSize
.X
) then
1336 a
:= (gPlayerScreenSize
.X
-gMapInfo
.Width
) div 2;
1339 if (gMapInfo
.Height
= gPlayerScreenSize
.Y
) then b
:= 0
1340 else if (gMapInfo
.Height
< gPlayerScreenSize
.Y
) then
1343 b
:= (gPlayerScreenSize
.Y
-gMapInfo
.Height
) div 2;
1348 // scaled, ignore level bounds
1349 a
:= -px
+(gPlayerScreenSize
.X
div 2);
1350 b
:= -py
+(gPlayerScreenSize
.Y
div 2);
1355 sWidth
:= gPlayerScreenSize
.X
;
1356 sHeight
:= gPlayerScreenSize
.Y
;
1357 fixViewportForScale();
1359 i
:= py
- (sY
+ sHeight
div 2);
1360 if (p
.IncCam
> 0) then
1362 // clamp to level bounds
1363 if (sY
- p
.IncCam
< 0) then
1364 p
.IncCam
:= nclamp(sY
, 0, 120);
1365 // clamp around player position
1367 p
.IncCam
:= nclamp(p
.IncCam
, 0, max(0, 120 - i
));
1369 else if (p
.IncCam
< 0) then
1371 // clamp to level bounds
1372 if (sY
+ sHeight
- p
.IncCam
> gMapInfo
.Height
) then
1373 p
.IncCam
:= nclamp(sY
+ sHeight
- gMapInfo
.Height
, -120, 0);
1374 // clamp around player position
1376 p
.IncCam
:= nclamp(p
.IncCam
, min(0, -120 - i
), 0);
1379 sY
:= sY
- nlerp(p
.IncCamOld
, p
.IncCam
, gLerpFactor
);
1381 if (not g_dbg_ignore_bounds
) then
1383 if (sX
+sWidth
> gMapInfo
.Width
) then sX
:= gMapInfo
.Width
-sWidth
;
1384 if (sY
+sHeight
> gMapInfo
.Height
) then sY
:= gMapInfo
.Height
-sHeight
;
1385 if (sX
< 0) then sX
:= 0;
1386 if (sY
< 0) then sY
:= 0;
1389 if (gBackSize
.X
<= gPlayerScreenSize
.X
) or (gMapInfo
.Width
<= sWidth
) then c
:= 0 else c
:= trunc((gBackSize
.X
-gPlayerScreenSize
.X
)*sX
/(gMapInfo
.Width
-sWidth
));
1390 if (gBackSize
.Y
<= gPlayerScreenSize
.Y
) or (gMapInfo
.Height
<= sHeight
) then d
:= 0 else d
:= trunc((gBackSize
.Y
-gPlayerScreenSize
.Y
)*sY
/(gMapInfo
.Height
-sHeight
));
1392 //r_smallmap_h: 0: left; 1: center; 2: right
1393 //r_smallmap_v: 0: top; 1: center; 2: bottom
1395 if (gMapInfo
.Width
= sWidth
) then
1399 else if (gMapInfo
.Width
< sWidth
) then
1401 case r_smallmap_h
of
1402 1: sX
:= -((sWidth
-gMapInfo
.Width
) div 2); // center
1403 2: sX
:= -(sWidth
-gMapInfo
.Width
); // right
1404 else sX
:= 0; // left
1408 if (gMapInfo
.Height
= sHeight
) then
1412 else if (gMapInfo
.Height
< sHeight
) then
1414 case r_smallmap_v
of
1415 1: sY
:= -((sHeight
-gMapInfo
.Height
) div 2); // center
1416 2: sY
:= -(sHeight
-gMapInfo
.Height
); // bottom
1417 else sY
:= 0; // top
1423 p
.viewPortW
:= sWidth
;
1424 p
.viewPortH
:= sHeight
;
1426 {$IFDEF ENABLE_HOLMES}
1427 if (p
= gPlayer1
) then
1429 g_Holmes_plrViewPos(sX
, sY
);
1430 g_Holmes_plrViewSize(sWidth
, sHeight
);
1434 renderMapInternal(-c
, -d
, true);
1436 if (gGameSettings
.GameMode
<> GM_SINGLE
) and (gPlayerIndicator
> 0) then
1437 case gPlayerIndicator
of
1439 p
.DrawIndicator(_RGB(255, 255, 255));
1442 for i
:= 0 to High(gPlayers
) do
1443 if gPlayers
[i
] <> nil then
1444 if gPlayers
[i
] = p
then p
.DrawIndicator(_RGB(255, 255, 255))
1445 else if (gPlayers
[i
].Team
= p
.Team
) and (gPlayers
[i
].Team
<> TEAM_NONE
) then
1446 if gPlayerIndicatorStyle
= 1 then
1447 gPlayers
[i
].DrawIndicator(_RGB(192, 192, 192))
1448 else gPlayers
[i
].DrawIndicator(gPlayers
[i
].GetColor
);
1452 for a := 0 to High(gCollideMap) do
1453 for b := 0 to High(gCollideMap[a]) do
1456 if ByteBool(gCollideMap[a, b] and MARK_WALL) then
1458 if ByteBool(gCollideMap[a, b] and MARK_DOOR) then
1462 1: e_DrawPoint(1, b, a, 200, 200, 200);
1463 2: e_DrawPoint(1, b, a, 64, 64, 255);
1464 3: e_DrawPoint(1, b, a, 255, 0, 255);
1474 if gShowMap
then DrawMinimap(p
, _TRect(0, 0, 128, 128));
1475 if g_Debug_Player
then
1476 g_Player_DrawDebug(p
);
1480 procedure drawProfilers ();
1485 if g_profile_frame_draw
and (profileFrameDraw
<> nil) then px
:= px
-drawProfiles(px
, py
, profileFrameDraw
);
1486 if g_profile_collision
and (profMapCollision
<> nil) then begin px
:= px
-drawProfiles(px
, py
, profMapCollision
); py
-= calcProfilesHeight(profMonsLOS
); end;
1487 if g_profile_los
and (profMonsLOS
<> nil) then begin px
:= px
-drawProfiles(px
, py
, profMonsLOS
); py
-= calcProfilesHeight(profMonsLOS
); end;
1490 procedure r_Game_Draw();
1497 plView1
, plView2
: TPlayer
;
1500 if gExit
= EXIT_QUIT
then Exit
;
1502 Time
:= sys_GetTicks() {div 1000};
1503 FPSCounter
:= FPSCounter
+1;
1504 if Time
- FPSTime
>= 1000 then
1511 e_SetRendertarget(True);
1512 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
1514 if gGameOn
or (gState
= STATE_FOLD
) then
1516 if (gPlayer1
<> nil) and (gPlayer2
<> nil) then
1518 gSpectMode
:= SPECT_NONE
;
1519 if not gRevertPlayers
then
1521 plView1
:= gPlayer1
;
1522 plView2
:= gPlayer2
;
1526 plView1
:= gPlayer2
;
1527 plView2
:= gPlayer1
;
1531 if (gPlayer1
<> nil) or (gPlayer2
<> nil) then
1533 gSpectMode
:= SPECT_NONE
;
1534 if gPlayer2
= nil then
1537 plView1
:= gPlayer2
;
1546 if (plView1
= nil) and (plView2
= nil) and (gSpectMode
= SPECT_NONE
) then
1547 gSpectMode
:= SPECT_STATS
;
1549 if gSpectMode
= SPECT_PLAYERS
then
1550 if gPlayers
<> nil then
1552 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
1553 if plView1
= nil then
1555 gSpectPID1
:= GetActivePlayerID_Next();
1556 plView1
:= GetActivePlayer_ByID(gSpectPID1
);
1558 if gSpectViewTwo
then
1560 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
1561 if plView2
= nil then
1563 gSpectPID2
:= GetActivePlayerID_Next();
1564 plView2
:= GetActivePlayer_ByID(gSpectPID2
);
1569 if gSpectMode
= SPECT_MAPVIEW
then
1571 // Режим просмотра карты
1573 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
1574 DrawMapView(gSpectX
, gSpectY
, gScreenWidth
, gScreenHeight
);
1575 gHearPoint1
.Active
:= True;
1576 gHearPoint1
.Coords
.X
:= gScreenWidth
div 2 + gSpectX
;
1577 gHearPoint1
.Coords
.Y
:= gScreenHeight
div 2 + gSpectY
;
1578 gHearPoint2
.Active
:= False;
1582 Split
:= (plView1
<> nil) and (plView2
<> nil);
1584 // Точки слуха игроков
1585 if plView1
<> nil then
1587 gHearPoint1
.Active
:= True;
1588 gHearPoint1
.Coords
.X
:= plView1
.GameX
+ PLAYER_RECT
.Width
;
1589 gHearPoint1
.Coords
.Y
:= plView1
.GameY
+ PLAYER_RECT
.Height
DIV 2;
1591 gHearPoint1
.Active
:= False;
1592 if plView2
<> nil then
1594 gHearPoint2
.Active
:= True;
1595 gHearPoint2
.Coords
.X
:= plView2
.GameX
+ PLAYER_RECT
.Width
;
1596 gHearPoint2
.Coords
.Y
:= plView2
.GameY
+ PLAYER_RECT
.Height
DIV 2;
1598 gHearPoint2
.Active
:= False;
1600 // Размер экранов игроков:
1601 gPlayerScreenSize
.X
:= gScreenWidth
-196;
1604 gPlayerScreenSize
.Y
:= gScreenHeight
div 2;
1605 if gScreenHeight
mod 2 = 0 then
1606 Dec(gPlayerScreenSize
.Y
);
1609 gPlayerScreenSize
.Y
:= gScreenHeight
;
1612 if gScreenHeight
mod 2 = 0 then
1613 e_SetViewPort(0, gPlayerScreenSize
.Y
+2, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
)
1615 e_SetViewPort(0, gPlayerScreenSize
.Y
+1, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
1617 DrawPlayer(plView1
);
1618 gPlayer1ScreenCoord
.X
:= sX
;
1619 gPlayer1ScreenCoord
.Y
:= sY
;
1623 e_SetViewPort(0, 0, gPlayerScreenSize
.X
+196, gPlayerScreenSize
.Y
);
1625 DrawPlayer(plView2
);
1626 gPlayer2ScreenCoord
.X
:= sX
;
1627 gPlayer2ScreenCoord
.Y
:= sY
;
1630 e_SetViewPort(0, 0, gScreenWidth
, gScreenHeight
);
1633 e_DrawLine(2, 0, gScreenHeight
div 2, gScreenWidth
, gScreenHeight
div 2, 0, 0, 0);
1636 {$IFDEF ENABLE_HOLMES}
1638 if (g_holmes_enabled
) then g_Holmes_Draw();
1641 if MessageText
<> '' then
1645 e_CharFont_GetSizeFmt(gMenuFont
, MessageText
, w
, h
);
1647 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
1648 (gScreenHeight
div 2)-(h
div 2), MessageText
)
1650 e_CharFont_PrintFmt(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
1651 Round(gScreenHeight
/ 2.75)-(h
div 2), MessageText
);
1654 if IsDrawStat
or (gSpectMode
= SPECT_STATS
) then
1657 if gSpectHUD
and (not gChatShow
) and (gSpectMode
<> SPECT_NONE
) and (not gSpectAuto
) then
1659 // Draw spectator GUI
1662 e_TextureFontGetSize(gStdFont
, ww
, hh
);
1665 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Stats', gStdFont
, 255, 255, 255, 1);
1667 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Observe Map', gStdFont
, 255, 255, 255, 1);
1669 e_TextureFontPrintEx(0, gScreenHeight
- (hh
+2)*2, 'MODE: Watch Players', gStdFont
, 255, 255, 255, 1);
1671 e_TextureFontPrintEx(2*ww
, gScreenHeight
- (hh
+2), '< jump >', gStdFont
, 255, 255, 255, 1);
1672 if gSpectMode
= SPECT_STATS
then
1674 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2)*2, 'Autoview', gStdFont
, 255, 255, 255, 1);
1675 e_TextureFontPrintEx(16*ww
, gScreenHeight
- (hh
+2), '< fire >', gStdFont
, 255, 255, 255, 1);
1677 if gSpectMode
= SPECT_MAPVIEW
then
1679 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, '[-]', gStdFont
, 255, 255, 255, 1);
1680 e_TextureFontPrintEx(26*ww
, gScreenHeight
- (hh
+2)*2, 'Step ' + IntToStr(gSpectStep
), gStdFont
, 255, 255, 255, 1);
1681 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2)*2, '[+]', gStdFont
, 255, 255, 255, 1);
1682 e_TextureFontPrintEx(18*ww
, gScreenHeight
- (hh
+2), '<prev weap>', gStdFont
, 255, 255, 255, 1);
1683 e_TextureFontPrintEx(30*ww
, gScreenHeight
- (hh
+2), '<next weap>', gStdFont
, 255, 255, 255, 1);
1685 if gSpectMode
= SPECT_PLAYERS
then
1687 e_TextureFontPrintEx(22*ww
, gScreenHeight
- (hh
+2)*2, 'Player 1', gStdFont
, 255, 255, 255, 1);
1688 e_TextureFontPrintEx(20*ww
, gScreenHeight
- (hh
+2), '<left/right>', gStdFont
, 255, 255, 255, 1);
1689 if gSpectViewTwo
then
1691 e_TextureFontPrintEx(37*ww
, gScreenHeight
- (hh
+2)*2, 'Player 2', gStdFont
, 255, 255, 255, 1);
1692 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<prev w/next w>', gStdFont
, 255, 255, 255, 1);
1693 e_TextureFontPrintEx(52*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
1694 e_TextureFontPrintEx(51*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
1698 e_TextureFontPrintEx(35*ww
, gScreenHeight
- (hh
+2)*2, '2x View', gStdFont
, 255, 255, 255, 1);
1699 e_TextureFontPrintEx(34*ww
, gScreenHeight
- (hh
+2), '<up/down>', gStdFont
, 255, 255, 255, 1);
1705 if gPauseMain
and gGameOn
and (g_ActiveWindow
= nil) then
1707 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1708 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1710 e_CharFont_GetSize(gMenuFont
, _lc
[I_MENU_PAUSE
], w
, h
);
1711 e_CharFont_Print(gMenuFont
, (gScreenWidth
div 2)-(w
div 2),
1712 (gScreenHeight
div 2)-(h
div 2), _lc
[I_MENU_PAUSE
]);
1717 if (gState
= STATE_MENU
) then
1719 if (g_ActiveWindow
= nil) or (g_ActiveWindow
.BackTexture
= '') then r_Game_DrawMenuBackground('MENU_BACKGROUND');
1720 // F3 at menu will show game loading dialog
1721 if e_KeyPressed(IK_F3
) then g_Menu_Show_LoadMenu(true);
1722 if (g_ActiveWindow
<> nil) then
1724 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1725 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1729 // F3 at titlepic will show game loading dialog
1730 if e_KeyPressed(IK_F3
) then
1732 g_Menu_Show_LoadMenu(true);
1733 if (g_ActiveWindow
<> nil) then e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1738 if gState
= STATE_FOLD
then
1740 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
1743 if gState
= STATE_INTERCUSTOM
then
1745 if gLastMap
and (gGameSettings
.GameMode
= GM_COOP
) then
1747 back
:= 'TEXTURE_endpic';
1748 if not g_Texture_Get(back
, ID
) then
1749 back
:= _lc
[I_TEXTURE_ENDPIC
];
1754 r_Game_DrawMenuBackground(back
);
1758 if g_ActiveWindow
<> nil then
1760 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1761 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1765 if gState
= STATE_INTERSINGLE
then
1767 if EndingGameCounter
> 0 then
1769 e_DrawFillQuad(0, 0, gScreenWidth
-1, gScreenHeight
-1, 0, 0, 0, EndingGameCounter
);
1775 r_Game_DrawMenuBackground(back
);
1779 if g_ActiveWindow
<> nil then
1781 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1782 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1787 if gState
= STATE_ENDPIC
then
1790 if g_Texture_Get('TEXTURE_endpic', ID
) then r_Game_DrawMenuBackground('TEXTURE_endpic')
1791 else r_Game_DrawMenuBackground(_lc
[I_TEXTURE_ENDPIC
]);
1793 if g_ActiveWindow
<> nil then
1795 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1796 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1800 if gState
= STATE_SLIST
then
1802 // if g_Texture_Get('MENU_BACKGROUND', ID) then
1804 // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight);
1805 // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1807 r_Game_DrawMenuBackground('MENU_BACKGROUND');
1808 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1809 g_Serverlist_Draw(slCurrent
, slTable
);
1813 if g_ActiveWindow
<> nil then
1817 //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180);
1818 e_DarkenQuadWH(0, 0, gScreenWidth
, gScreenHeight
, 150);
1820 g_ActiveWindow
.Draw();
1827 if g_debug_Sounds
and gGameOn
then
1829 for w
:= 0 to High(e_SoundsArray
) do
1830 for h
:= 0 to e_SoundsArray
[w
].nRefs
do
1831 e_DrawPoint(1, w
+100, h
+100, 255, 0, 0);
1836 e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS
]), gStdFont
);
1837 e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS
]), gStdFont
);
1840 if gGameOn
and gShowTime
then
1841 drawTime(gScreenWidth
-72, gScreenHeight
-16);
1843 if gGameOn
then drawProfilers();
1845 // TODO: draw this after the FBO and remap mouse click coordinates
1847 {$IFDEF ENABLE_HOLMES}
1851 // blit framebuffer to screen
1853 e_SetRendertarget(False);
1854 e_SetViewPort(0, 0, gWinSizeX
, gWinSizeY
);
1855 e_BlitFramebuffer(gWinSizeX
, gWinSizeY
);
1857 // draw the overlay stuff on top of it