(* Copyright (C) Doom 2D: Forever Developers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License ONLY. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) {$INCLUDE ../../shared/a_modes.inc} unit r_game; interface procedure r_Game_Draw; procedure DrawLoadingStat; procedure DrawMenuBackground (tex: AnsiString); implementation uses {$INCLUDE ../nogl/noGLuses.inc} SysUtils, Classes, Math, e_graphics, g_system, g_touch, MAPDEF, xprofiler, utils, wadreader, g_textures, e_input, e_sound, g_language, g_console, g_menu, g_triggers, g_player, g_options, g_monsters, g_map, g_panel, g_window, g_items, g_weapons, g_gfx, g_phys, g_net, g_gui, g_netmaster, g_game, r_console, r_gfx, r_items ; var profileFrameDraw: TProfiler = nil; FPS: Word; FPSCounter: Word; FPSTime: LongWord; function GetActivePlayer_ByID(ID: Integer): TPlayer; var a: Integer; begin Result := nil; if ID < 0 then Exit; if gPlayers = nil then Exit; for a := Low(gPlayers) to High(gPlayers) do if IsActivePlayer(gPlayers[a]) then begin if gPlayers[a].UID <> ID then continue; Result := gPlayers[a]; break; end; end; function calcProfilesHeight (prof: TProfiler): Integer; begin result := 0; if (prof = nil) then exit; if (length(prof.bars) = 0) then exit; result := length(prof.bars)*(16+2); end; // returns width function drawProfiles (x, y: Integer; prof: TProfiler): Integer; var wdt, hgt: Integer; yy: Integer; ii: Integer; begin result := 0; if (prof = nil) then exit; // gScreenWidth if (length(prof.bars) = 0) then exit; wdt := 192; hgt := calcProfilesHeight(prof); if (x < 0) then x := gScreenWidth-(wdt-1)+x; if (y < 0) then y := gScreenHeight-(hgt-1)+y; // background //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 255, 255, 255, 200, B_BLEND); //e_DrawFillQuad(x, y, x+wdt-1, y+hgt-1, 20, 20, 20, 0, B_NONE); e_DarkenQuadWH(x, y, wdt, hgt, 150); // title yy := y+2; for ii := 0 to High(prof.bars) do begin 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); Inc(yy, 16+2); end; result := wdt; end; procedure drawTime(X, Y: Integer); inline; begin 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); end; procedure DrawStat(); var pc, x, y, w, h: Integer; w1, w2, w3, w4: Integer; a, aa: Integer; cw, ch, r, g, b, rr, gg, bb: Byte; s1, s2, s3: String; _y: Integer; stat: TPlayerStatArray; wad, map: string; mapstr: string; namestr: string; begin s1 := ''; s2 := ''; s3 := ''; pc := g_Player_GetCount; e_TextureFontGetSize(gStdFont, cw, ch); w := gScreenWidth-(gScreenWidth div 5); if gGameSettings.GameMode in [GM_TDM, GM_CTF] then h := 32+ch*(11+pc) else h := 40+ch*5+(ch+8)*pc; x := (gScreenWidth div 2)-(w div 2); y := (gScreenHeight div 2)-(h div 2); e_DrawFillQuad(x, y, x+w-1, y+h-1, 64, 64, 64, 32); e_DrawQuad(x, y, x+w-1, y+h-1, 255, 127, 0); drawTime(x+w-78, y+8); wad := g_ExtractWadNameNoPath(gMapInfo.Map); map := g_ExtractFileName(gMapInfo.Map); mapstr := wad + ':\' + map + ' - ' + gMapInfo.Name; case gGameSettings.GameMode of GM_DM: begin if gGameSettings.MaxLives = 0 then s1 := _lc[I_GAME_DM] else s1 := _lc[I_GAME_LMS]; s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]); s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]); end; GM_TDM: begin if gGameSettings.MaxLives = 0 then s1 := _lc[I_GAME_TDM] else s1 := _lc[I_GAME_TLMS]; s2 := Format(_lc[I_GAME_FRAG_LIMIT], [gGameSettings.GoalLimit]); s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]); end; GM_CTF: begin s1 := _lc[I_GAME_CTF]; s2 := Format(_lc[I_GAME_SCORE_LIMIT], [gGameSettings.GoalLimit]); s3 := Format(_lc[I_GAME_TIME_LIMIT], [gGameSettings.TimeLimit div 3600, (gGameSettings.TimeLimit div 60) mod 60, gGameSettings.TimeLimit mod 60]); end; GM_COOP: begin if gGameSettings.MaxLives = 0 then s1 := _lc[I_GAME_COOP] else s1 := _lc[I_GAME_SURV]; s2 := _lc[I_GAME_MONSTERS] + ' ' + IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters); s3 := _lc[I_GAME_SECRETS] + ' ' + IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount); end; else begin s1 := ''; s2 := ''; end; end; _y := y+8; e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*cw div 2), _y, s1, gStdFont, 255, 255, 255, 1); _y := _y+ch+8; e_TextureFontPrintEx(x+(w div 2)-(Length(mapstr)*cw div 2), _y, mapstr, gStdFont, 200, 200, 200, 1); _y := _y+ch+8; e_TextureFontPrintEx(x+16, _y, s2, gStdFont, 200, 200, 200, 1); e_TextureFontPrintEx(x+w-16-(Length(s3))*cw, _y, s3, gStdFont, 200, 200, 200, 1); if NetMode = NET_SERVER then e_TextureFontPrintEx(x+8, y + 8, _lc[I_NET_SERVER], gStdFont, 255, 255, 255, 1) else if NetMode = NET_CLIENT then e_TextureFontPrintEx(x+8, y + 8, NetClientIP + ':' + IntToStr(NetClientPort), gStdFont, 255, 255, 255, 1); if pc = 0 then Exit; stat := g_Player_GetStats(); SortGameStat(stat); w2 := (w-16) div 6 + 48; // ширина 2 столбца w3 := (w-16) div 6; // ширина 3 и 4 столбцов w4 := w3; w1 := w-16-w2-w3-w4; // оставшееся пространство - для цвета и имени игрока if gGameSettings.GameMode in [GM_TDM, GM_CTF] then begin _y := _y+ch+ch; for a := TEAM_RED to TEAM_BLUE do begin if a = TEAM_RED then begin s1 := _lc[I_GAME_TEAM_RED]; r := 255; g := 0; b := 0; end else begin s1 := _lc[I_GAME_TEAM_BLUE]; r := 0; g := 0; b := 255; end; e_TextureFontPrintEx(x+16, _y, s1, gStdFont, r, g, b, 1); e_TextureFontPrintEx(x+w1+16, _y, IntToStr(gTeamStat[a].Goals), gStdFont, r, g, b, 1); _y := _y+ch+(ch div 4); e_DrawLine(1, x+16, _y, x+w-16, _y, r, g, b); _y := _y+(ch div 4); for aa := 0 to High(stat) do if stat[aa].Team = a then with stat[aa] do begin if Spectator then begin rr := r div 2; gg := g div 2; bb := b div 2; end else begin rr := r; gg := g; bb := b; end; if gShowPIDs then namestr := Format('[%5d] %s', [UID, Name]) else namestr := Name; // Имя e_TextureFontPrintEx(x+16, _y, namestr, gStdFont, rr, gg, bb, 1); // Пинг/потери e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1); // Фраги e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1); // Смерти e_TextureFontPrintEx(x+w1+w2+w3+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1); _y := _y+ch; end; _y := _y+ch; end; end else if gGameSettings.GameMode in [GM_DM, GM_COOP] then begin _y := _y+ch+ch; e_TextureFontPrintEx(x+16, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+16+w1, _y, _lc[I_GAME_PING], gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+16+w1+w2, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+16+w1+w2+w3, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1); _y := _y+ch+8; for aa := 0 to High(stat) do with stat[aa] do begin if Spectator then begin r := 127; g := 64; end else begin r := 255; g := 127; end; if gShowPIDs then namestr := Format('[%5d] %s', [UID, Name]) else namestr := Name; // Цвет игрока e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0); e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192); // Имя e_TextureFontPrintEx(x+16+16+8, _y+4, namestr, gStdFont, r, g, 0, 1); // Пинг/потери e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1); // Фраги e_TextureFontPrintEx(x+w1+w2+16, _y+4, IntToStr(Frags), gStdFont, r, g, 0, 1); // Смерти e_TextureFontPrintEx(x+w1+w2+w3+16, _y+4, IntToStr(Deaths), gStdFont, r, g, 0, 1); _y := _y+ch+8; end; end end; procedure DrawCustomStat(); var pc, x, y, w, _y, w1, w2, w3, t, p, m: Integer; ww1, hh1: Word; ww2, hh2, r, g, b, rr, gg, bb: Byte; s1, s2, topstr: String; begin e_TextureFontGetSize(gStdFont, ww2, hh2); sys_HandleInput; if g_Console_Action(ACTION_SCORES) then begin if not gStatsPressed then begin gStatsOff := not gStatsOff; gStatsPressed := True; end; end else gStatsPressed := False; if gStatsOff then begin s1 := _lc[I_MENU_INTER_NOTICE_TAB]; w := (Length(s1) * ww2) div 2; x := gScreenWidth div 2 - w; y := 8; e_TextureFontPrint(x, y, s1, gStdFont); Exit; end; if (gGameSettings.GameMode = GM_COOP) then begin if gMissionFailed then topstr := _lc[I_MENU_INTER_MISSION_FAIL] else topstr := _lc[I_MENU_INTER_LEVEL_COMPLETE]; end else topstr := _lc[I_MENU_INTER_ROUND_OVER]; e_CharFont_GetSize(gMenuFont, topstr, ww1, hh1); e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww1 div 2), 16, topstr); if g_Game_IsNet then begin topstr := Format(_lc[I_MENU_INTER_NOTICE_TIME], [gServInterTime]); if not gChatShow then e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2), gScreenHeight-(hh2+4)*2, topstr, gStdFont, 255, 255, 255, 1); end; if g_Game_IsClient then topstr := _lc[I_MENU_INTER_NOTICE_MAP] else topstr := _lc[I_MENU_INTER_NOTICE_SPACE]; if not gChatShow then e_TextureFontPrintEx((gScreenWidth div 2)-(Length(topstr)*ww2 div 2), gScreenHeight-(hh2+4), topstr, gStdFont, 255, 255, 255, 1); x := 32; y := 16+hh1+16; w := gScreenWidth-x*2; w2 := (w-16) div 6; w3 := w2; w1 := w-16-w2-w3; e_DrawFillQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 64, 64, 64, 32); e_DrawQuad(x, y, gScreenWidth-x-1, gScreenHeight-y-1, 255, 127, 0); m := Max(Length(_lc[I_MENU_MAP])+1, Length(_lc[I_GAME_GAME_TIME])+1)*ww2; case CustomStat.GameMode of GM_DM: begin if gGameSettings.MaxLives = 0 then s1 := _lc[I_GAME_DM] else s1 := _lc[I_GAME_LMS]; end; GM_TDM: begin if gGameSettings.MaxLives = 0 then s1 := _lc[I_GAME_TDM] else s1 := _lc[I_GAME_TLMS]; end; GM_CTF: s1 := _lc[I_GAME_CTF]; GM_COOP: begin if gGameSettings.MaxLives = 0 then s1 := _lc[I_GAME_COOP] else s1 := _lc[I_GAME_SURV]; end; else s1 := ''; end; _y := y+16; e_TextureFontPrintEx(x+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1); _y := _y+8; _y := _y+16; e_TextureFontPrintEx(x+8, _y, _lc[I_MENU_MAP], gStdFont, 255, 127, 0, 1); e_TextureFontPrint(x+8+m, _y, Format('%s - %s', [CustomStat.Map, CustomStat.MapName]), gStdFont); _y := _y+16; e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_GAME_TIME], gStdFont, 255, 127, 0, 1); e_TextureFontPrint(x+8+m, _y, Format('%d:%.2d:%.2d', [CustomStat.GameTime div 1000 div 3600, (CustomStat.GameTime div 1000 div 60) mod 60, CustomStat.GameTime div 1000 mod 60]), gStdFont); pc := Length(CustomStat.PlayerStat); if pc = 0 then Exit; if CustomStat.GameMode = GM_COOP then begin m := Max(Length(_lc[I_GAME_MONSTERS])+1, Length(_lc[I_GAME_SECRETS])+1)*ww2; _y := _y+32; s2 := _lc[I_GAME_MONSTERS]; e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopMonstersKilled) + '/' + IntToStr(gTotalMonsters), gStdFont, 255, 255, 255, 1); _y := _y+16; s2 := _lc[I_GAME_SECRETS]; e_TextureFontPrintEx(x+8, _y, s2, gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+8+m, _y, IntToStr(gCoopSecretsFound) + '/' + IntToStr(gSecretsCount), gStdFont, 255, 255, 255, 1); if gLastMap then begin m := Max(Length(_lc[I_GAME_MONSTERS_TOTAL])+1, Length(_lc[I_GAME_SECRETS_TOTAL])+1)*ww2; _y := _y-16; s2 := _lc[I_GAME_MONSTERS_TOTAL]; e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalMonstersKilled) + '/' + IntToStr(gCoopTotalMonsters), gStdFont, 255, 255, 255, 1); _y := _y+16; s2 := _lc[I_GAME_SECRETS_TOTAL]; e_TextureFontPrintEx(x+250, _y, s2, gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+250+m, _y, IntToStr(gCoopTotalSecretsFound) + '/' + IntToStr(gCoopTotalSecrets), gStdFont, 255, 255, 255, 1); end; end; if CustomStat.GameMode in [GM_TDM, GM_CTF] then begin _y := _y+16+16; with CustomStat do if TeamStat[TEAM_RED].Goals > TeamStat[TEAM_BLUE].Goals then s1 := _lc[I_GAME_WIN_RED] else if TeamStat[TEAM_BLUE].Goals > TeamStat[TEAM_RED].Goals then s1 := _lc[I_GAME_WIN_BLUE] else s1 := _lc[I_GAME_WIN_DRAW]; e_TextureFontPrintEx(x+8+(w div 2)-(Length(s1)*ww2 div 2), _y, s1, gStdFont, 255, 255, 255, 1); _y := _y+40; for t := TEAM_RED to TEAM_BLUE do begin if t = TEAM_RED then begin e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_RED], gStdFont, 255, 0, 0, 1); e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_RED].Goals), gStdFont, 255, 0, 0, 1); r := 255; g := 0; b := 0; end else begin e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_TEAM_BLUE], gStdFont, 0, 0, 255, 1); e_TextureFontPrintEx(x+w1+8, _y, IntToStr(CustomStat.TeamStat[TEAM_BLUE].Goals), gStdFont, 0, 0, 255, 1); r := 0; g := 0; b := 255; end; e_DrawLine(1, x+8, _y+20, x-8+w, _y+20, r, g, b); _y := _y+24; for p := 0 to High(CustomStat.PlayerStat) do if CustomStat.PlayerStat[p].Team = t then with CustomStat.PlayerStat[p] do begin if Spectator then begin rr := r div 2; gg := g div 2; bb := b div 2; end else begin rr := r; gg := g; bb := b; end; if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then e_TextureFontPrintEx(x+16, _y, Name + ' *', gStdFont, rr, gg, bb, 1) else e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1); e_TextureFontPrintEx(x+w1+16, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1); e_TextureFontPrintEx(x+w1+w2+16, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1); _y := _y+24; end; _y := _y+16+16; end; end else if CustomStat.GameMode in [GM_DM, GM_COOP] then begin _y := _y+40; e_TextureFontPrintEx(x+8, _y, _lc[I_GAME_PLAYER_NAME], gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+8+w1, _y, _lc[I_GAME_FRAGS], gStdFont, 255, 127, 0, 1); e_TextureFontPrintEx(x+8+w1+w2, _y, _lc[I_GAME_DEATHS], gStdFont, 255, 127, 0, 1); _y := _y+24; for p := 0 to High(CustomStat.PlayerStat) do with CustomStat.PlayerStat[p] do begin e_DrawFillQuad(x+8, _y+4, x+24-1, _y+16+4-1, Color.R, Color.G, Color.B, 0); if Spectator then r := 127 else r := 255; if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True) else e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True); e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True); e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True); _y := _y+24; end; end; // HACK: take stats screenshot immediately after the first frame of the stats showing if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then begin g_TakeScreenShot('stats/' + StatFilename); StatShotDone := True; end; end; procedure DrawSingleStat(); var tm, key_x, val_x, y: Integer; w1, w2, h: Word; s1, s2: String; procedure player_stat(n: Integer); var kpm: Real; begin // "Kills: # / #": s1 := Format(' %d ', [SingleStat.PlayerStat[n].Kills]); s2 := Format(' %d', [gTotalMonsters]); e_CharFont_Print(gMenuFont, key_x, y, _lc[I_MENU_INTER_KILLS]); e_CharFont_PrintEx(gMenuFont, val_x, y, s1, _RGB(255, 0, 0)); e_CharFont_GetSize(gMenuFont, s1, w1, h); e_CharFont_Print(gMenuFont, val_x+w1, y, '/'); s1 := s1 + '/'; e_CharFont_GetSize(gMenuFont, s1, w1, h); e_CharFont_PrintEx(gMenuFont, val_x+w1, y, s2, _RGB(255, 0, 0)); // "Kills-per-minute: ##.#": s1 := _lc[I_MENU_INTER_KPM]; if tm > 0 then kpm := (SingleStat.PlayerStat[n].Kills / tm) * 60 else kpm := SingleStat.PlayerStat[n].Kills; s2 := Format(' %.1f', [kpm]); e_CharFont_Print(gMenuFont, key_x, y+32, s1); e_CharFont_PrintEx(gMenuFont, val_x, y+32, s2, _RGB(255, 0, 0)); // "Secrets found: # / #": s1 := Format(' %d ', [SingleStat.PlayerStat[n].Secrets]); s2 := Format(' %d', [SingleStat.TotalSecrets]); e_CharFont_Print(gMenuFont, key_x, y+64, _lc[I_MENU_INTER_SECRETS]); e_CharFont_PrintEx(gMenuFont, val_x, y+64, s1, _RGB(255, 0, 0)); e_CharFont_GetSize(gMenuFont, s1, w1, h); e_CharFont_Print(gMenuFont, val_x+w1, y+64, '/'); s1 := s1 + '/'; e_CharFont_GetSize(gMenuFont, s1, w1, h); e_CharFont_PrintEx(gMenuFont, val_x+w1, y+64, s2, _RGB(255, 0, 0)); end; begin // "Level Complete": e_CharFont_GetSize(gMenuFont, _lc[I_MENU_INTER_LEVEL_COMPLETE], w1, h); e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 32, _lc[I_MENU_INTER_LEVEL_COMPLETE]); // Определяем координаты выравнивания по самой длинной строке: s1 := _lc[I_MENU_INTER_KPM]; e_CharFont_GetSize(gMenuFont, s1, w1, h); Inc(w1, 16); s1 := ' 9999.9'; e_CharFont_GetSize(gMenuFont, s1, w2, h); key_x := (gScreenWidth-w1-w2) div 2; val_x := key_x + w1; // "Time: #:##:##": tm := SingleStat.GameTime div 1000; s1 := _lc[I_MENU_INTER_TIME]; s2 := Format(' %d:%.2d:%.2d', [tm div (60*60), (tm mod (60*60)) div 60, tm mod 60]); e_CharFont_Print(gMenuFont, key_x, 80, s1); e_CharFont_PrintEx(gMenuFont, val_x, 80, s2, _RGB(255, 0, 0)); if SingleStat.TwoPlayers then begin // "Player 1": s1 := _lc[I_MENU_PLAYER_1]; e_CharFont_GetSize(gMenuFont, s1, w1, h); e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 128, s1); // Статистика первого игрока: y := 176; player_stat(0); // "Player 2": s1 := _lc[I_MENU_PLAYER_2]; e_CharFont_GetSize(gMenuFont, s1, w1, h); e_CharFont_Print(gMenuFont, (gScreenWidth-w1) div 2, 288, s1); // Статистика второго игрока: y := 336; player_stat(1); end else begin // Статистика первого игрока: y := 128; player_stat(0); end; end; procedure DrawLoadingStat(); procedure drawRect (x, y, w, h: Integer); begin if (w < 1) or (h < 1) then exit; glBegin(GL_QUADS); glVertex2f(x+0.375, y+0.375); glVertex2f(x+w+0.375, y+0.375); glVertex2f(x+w+0.375, y+h+0.375); glVertex2f(x+0.375, y+h+0.375); glEnd(); end; function drawPBar (cur, total: Integer; washere: Boolean): Boolean; var rectW, rectH: Integer; x0, y0: Integer; wdt: Integer; wl, hl: Integer; wr, hr: Integer; wb, hb: Integer; wm, hm: Integer; idl, idr, idb, idm: LongWord; f, my: Integer; begin result := false; if (total < 1) then exit; if (cur < 1) then exit; // don't blink if (not washere) and (cur >= total) then exit; // don't blink //if (cur < 0) then cur := 0; //if (cur > total) then cur := total; result := true; if (hasPBarGfx) then begin g_Texture_Get('UI_GFX_PBAR_LEFT', idl); g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl); g_Texture_Get('UI_GFX_PBAR_RIGHT', idr); g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr); g_Texture_Get('UI_GFX_PBAR_MIDDLE', idb); g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb); g_Texture_Get('UI_GFX_PBAR_MARKER', idm); g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm); //rectW := gScreenWidth-360; rectW := trunc(624.0*gScreenWidth/1024.0); rectH := hl; x0 := (gScreenWidth-rectW) div 2; y0 := gScreenHeight-rectH-64; if (y0 < 2) then y0 := 2; glEnable(GL_SCISSOR_TEST); // left and right glScissor(x0, gScreenHeight-y0-rectH, rectW, rectH); e_DrawSize(idl, x0, y0, 0, true, false, wl, hl); e_DrawSize(idr, x0+rectW-wr, y0, 0, true, false, wr, hr); // body glScissor(x0+wl, gScreenHeight-y0-rectH, rectW-wl-wr, rectH); f := x0+wl; while (f < x0+rectW) do begin e_DrawSize(idb, f, y0, 0, true, false, wb, hb); f += wb; end; // filled part wdt := (rectW-wl-wr)*cur div total; if (wdt > rectW-wl-wr) then wdt := rectW-wr-wr; if (wdt > 0) then begin my := y0; // don't be so smart, ketmar: +(rectH-wm) div 2; glScissor(x0+wl, gScreenHeight-my-rectH, wdt, hm); f := x0+wl; while (wdt > 0) do begin e_DrawSize(idm, f, y0, 0, true, false, wm, hm); f += wm; wdt -= wm; end; end; glScissor(0, 0, gScreenWidth, gScreenHeight); end else begin rectW := gScreenWidth-64; rectH := 16; x0 := (gScreenWidth-rectW) div 2; y0 := gScreenHeight-rectH-64; if (y0 < 2) then y0 := 2; glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); //glClearColor(0, 0, 0, 0); //glClear(GL_COLOR_BUFFER_BIT); glColor4ub(127, 127, 127, 255); drawRect(x0-2, y0-2, rectW+4, rectH+4); glColor4ub(0, 0, 0, 255); drawRect(x0-1, y0-1, rectW+2, rectH+2); glColor4ub(127, 127, 127, 255); wdt := rectW*cur div total; if (wdt > rectW) then wdt := rectW; drawRect(x0, y0, wdt, rectH); end; end; var ww, hh: Word; xx, yy, i: Integer; s: String; begin if (Length(LoadingStat.Msgs) = 0) then exit; e_CharFont_GetSize(gMenuFont, _lc[I_MENU_LOADING], ww, hh); yy := (gScreenHeight div 3); e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(ww div 2), yy-2*hh, _lc[I_MENU_LOADING]); xx := (gScreenWidth div 3); with LoadingStat do begin for i := 0 to NextMsg-1 do begin if (i = (NextMsg-1)) and (MaxValue > 0) then s := Format('%s: %d/%d', [Msgs[i], CurValue, MaxValue]) else s := Msgs[i]; e_CharFont_PrintEx(gMenuSmallFont, xx, yy, s, _RGB(255, 0, 0)); yy := yy + LOADING_INTERLINE; PBarWasHere := drawPBar(CurValue, MaxValue, PBarWasHere); end; end; end; procedure DrawMenuBackground(tex: AnsiString); var w, h: Word; ID: DWord; begin if g_Texture_Get(tex, ID) then begin e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); e_GetTextureSize(ID, @w, @h); if w = h then w := round(w * 1.333 * (gScreenHeight / h)) else w := trunc(w * (gScreenHeight / h)); e_DrawSize(ID, (gScreenWidth - w) div 2, 0, 0, False, False, w, gScreenHeight); end else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); end; procedure DrawMinimap(p: TPlayer; RenderRect: e_graphics.TRect); var a, aX, aY, aX2, aY2, Scale, ScaleSz: Integer; function monDraw (mon: TMonster): Boolean; begin result := false; // don't stop with mon do begin if alive then begin // Левый верхний угол aX := Obj.X div ScaleSz + 1; aY := Obj.Y div ScaleSz + 1; // Размеры aX2 := max(Obj.Rect.Width div ScaleSz, 1); aY2 := max(Obj.Rect.Height div ScaleSz, 1); // Правый нижний угол aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; e_DrawFillQuad(aX, aY, aX2, aY2, 255, 255, 0, 0); end; end; end; begin if (gMapInfo.Width > RenderRect.Right - RenderRect.Left) or (gMapInfo.Height > RenderRect.Bottom - RenderRect.Top) then begin Scale := 1; // Сколько пикселов карты в 1 пикселе мини-карты: ScaleSz := 16 div Scale; // Размеры мини-карты: aX := max(gMapInfo.Width div ScaleSz, 1); aY := max(gMapInfo.Height div ScaleSz, 1); // Рамка карты: e_DrawFillQuad(0, 0, aX-1, aY-1, 0, 0, 0, 0); if gWalls <> nil then begin // Рисуем стены: for a := 0 to High(gWalls) do with gWalls[a] do if PanelType <> 0 then begin // Левый верхний угол: aX := X div ScaleSz; aY := Y div ScaleSz; // Размеры: aX2 := max(Width div ScaleSz, 1); aY2 := max(Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; case PanelType of PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0); PANEL_OPENDOOR, PANEL_CLOSEDOOR: if Enabled then e_DrawFillQuad(aX, aY, aX2, aY2, 160, 160, 160, 0); end; end; end; if gSteps <> nil then begin // Рисуем ступени: for a := 0 to High(gSteps) do with gSteps[a] do if PanelType <> 0 then begin // Левый верхний угол: aX := X div ScaleSz; aY := Y div ScaleSz; // Размеры: aX2 := max(Width div ScaleSz, 1); aY2 := max(Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0); end; end; if gLifts <> nil then begin // Рисуем лифты: for a := 0 to High(gLifts) do with gLifts[a] do if PanelType <> 0 then begin // Левый верхний угол: aX := X div ScaleSz; aY := Y div ScaleSz; // Размеры: aX2 := max(Width div ScaleSz, 1); aY2 := max(Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; case LiftType of LIFTTYPE_UP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0); LIFTTYPE_DOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0); LIFTTYPE_LEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0); LIFTTYPE_RIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0); end; end; end; if gWater <> nil then begin // Рисуем воду: for a := 0 to High(gWater) do with gWater[a] do if PanelType <> 0 then begin // Левый верхний угол: aX := X div ScaleSz; aY := Y div ScaleSz; // Размеры: aX2 := max(Width div ScaleSz, 1); aY2 := max(Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0); end; end; if gAcid1 <> nil then begin // Рисуем кислоту 1: for a := 0 to High(gAcid1) do with gAcid1[a] do if PanelType <> 0 then begin // Левый верхний угол: aX := X div ScaleSz; aY := Y div ScaleSz; // Размеры: aX2 := max(Width div ScaleSz, 1); aY2 := max(Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0); end; end; if gAcid2 <> nil then begin // Рисуем кислоту 2: for a := 0 to High(gAcid2) do with gAcid2[a] do if PanelType <> 0 then begin // Левый верхний угол: aX := X div ScaleSz; aY := Y div ScaleSz; // Размеры: aX2 := max(Width div ScaleSz, 1); aY2 := max(Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0); end; end; if gPlayers <> nil then begin // Рисуем игроков: for a := 0 to High(gPlayers) do if gPlayers[a] <> nil then with gPlayers[a] do if alive then begin // Левый верхний угол: aX := Obj.X div ScaleSz + 1; aY := Obj.Y div ScaleSz + 1; // Размеры: aX2 := max(Obj.Rect.Width div ScaleSz, 1); aY2 := max(Obj.Rect.Height div ScaleSz, 1); // Правый нижний угол: aX2 := aX + aX2 - 1; aY2 := aY + aY2 - 1; if gPlayers[a] = p then e_DrawFillQuad(aX, aY, aX2, aY2, 0, 255, 0, 0) else case Team of TEAM_RED: e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0); TEAM_BLUE: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 255, 0); else e_DrawFillQuad(aX, aY, aX2, aY2, 255, 128, 0, 0); end; end; end; // Рисуем монстров g_Mons_ForEach(monDraw); end; end; procedure renderAmbientQuad (hasAmbient: Boolean; constref ambColor: TDFColor); begin if not hasAmbient then exit; e_AmbientQuad(sX, sY, sWidth, sHeight, ambColor.r, ambColor.g, ambColor.b, ambColor.a); end; // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this! //FIXME: broken for splitscreen mode procedure renderDynLightsInternal (); var //hasAmbient: Boolean; //ambColor: TDFColor; lln: Integer; lx, ly, lrad: Integer; scxywh: array[0..3] of GLint; wassc: Boolean; begin if e_NoGraphics then exit; //TODO: lights should be in separate grid, i think // but on the other side: grid may be slower for dynlights, as their lifetime is short if (not gwin_k8_enable_light_experiments) or (not gwin_has_stencil) or (g_dynLightCount < 1) then exit; // rendering mode //ambColor := gCurrentMap['light_ambient'].rgba; //hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack); { // this will multiply incoming color to alpha from framebuffer glEnable(GL_BLEND); glBlendFunc(GL_DST_ALPHA, GL_ONE); } (* * light rendering: (INVALID!) * glStencilFunc(GL_EQUAL, 0, $ff); * for each light: * glClear(GL_STENCIL_BUFFER_BIT); * glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); * draw shadow volume into stencil buffer * glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer * glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify stencil buffer * turn off blending * draw color-less quad with light alpha (WARNING! don't touch color!) * glEnable(GL_BLEND); * glBlendFunc(GL_DST_ALPHA, GL_ONE); * draw all geometry up to and including walls (with alpha-testing, probably) -- this does lighting *) wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0); if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]); // setup OpenGL parameters glStencilMask($FFFFFFFF); glStencilFunc(GL_ALWAYS, 0, $FFFFFFFF); glEnable(GL_STENCIL_TEST); glEnable(GL_SCISSOR_TEST); glClear(GL_STENCIL_BUFFER_BIT); glStencilFunc(GL_EQUAL, 0, $ff); for lln := 0 to g_dynLightCount-1 do begin lx := g_dynLights[lln].x; ly := g_dynLights[lln].y; lrad := g_dynLights[lln].radius; if (lrad < 3) then continue; if (lx-sX+lrad < 0) then continue; if (ly-sY+lrad < 0) then continue; if (lx-sX-lrad >= gPlayerScreenSize.X) then continue; if (ly-sY-lrad >= gPlayerScreenSize.Y) then continue; // set scissor to optimize drawing if (g_dbg_scale = 1.0) then begin glScissor((lx-sX)-lrad+2, gPlayerScreenSize.Y-(ly-sY)-lrad-1+2, lrad*2-4, lrad*2-4); end else begin glScissor(0, 0, gScreenWidth, gScreenHeight); end; // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // draw extruded panels glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no need to modify color buffer if (lrad > 4) then g_Map_DrawPanelShadowVolumes(lx, ly, lrad); // render light texture glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // draw light, and clear stencil buffer // blend it glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); // color and opacity glColor4f(g_dynLights[lln].r, g_dynLights[lln].g, g_dynLights[lln].b, g_dynLights[lln].a); glBindTexture(GL_TEXTURE_2D, g_Texture_Light()); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2i(lx-lrad, ly-lrad); // top-left glTexCoord2f(1.0, 0.0); glVertex2i(lx+lrad, ly-lrad); // top-right glTexCoord2f(1.0, 1.0); glVertex2i(lx+lrad, ly+lrad); // bottom-right glTexCoord2f(0.0, 1.0); glVertex2i(lx-lrad, ly+lrad); // bottom-left glEnd(); end; // done glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); glDisable(GL_SCISSOR_TEST); //glScissor(0, 0, sWidth, sHeight); glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]); if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST); end; function fixViewportForScale (): Boolean; var nx0, ny0, nw, nh: Integer; begin result := false; if (g_dbg_scale <> 1.0) then begin result := true; nx0 := round(sX-(gPlayerScreenSize.X-(sWidth*g_dbg_scale))/2/g_dbg_scale); ny0 := round(sY-(gPlayerScreenSize.Y-(sHeight*g_dbg_scale))/2/g_dbg_scale); nw := round(sWidth/g_dbg_scale); nh := round(sHeight/g_dbg_scale); sX := nx0; sY := ny0; sWidth := nw; sHeight := nh; end; end; // setup sX, sY, sWidth, sHeight, and transformation matrix before calling this! // WARNING! this WILL CALL `glTranslatef()`, but won't restore matrices! procedure renderMapInternal (backXOfs, backYOfs: Integer; setTransMatrix: Boolean); type TDrawCB = procedure (); var hasAmbient: Boolean; ambColor: TDFColor; doAmbient: Boolean = false; procedure drawPanelType (profname: AnsiString; panType: DWord; doDraw: Boolean); var tagmask: Integer; pan: TPanel; begin if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname); if gdbg_map_use_accel_render then begin tagmask := panelTypeToTag(panType); while (gDrawPanelList.count > 0) do begin pan := TPanel(gDrawPanelList.front()); if ((pan.tag and tagmask) = 0) then break; if doDraw then pan.Draw(doAmbient, ambColor); gDrawPanelList.popFront(); end; end else begin if doDraw then g_Map_DrawPanels(panType, hasAmbient, ambColor); end; if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd(); end; procedure drawOther (profname: AnsiString; cb: TDrawCB); begin if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin(profname); if assigned(cb) then cb(); if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd(); end; begin if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('total'); // our accelerated renderer will collect all panels to gDrawPanelList // we can use panel tag to render level parts (see GridTagXXX in g_map.pas) if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('collect'); if gdbg_map_use_accel_render then begin g_Map_CollectDrawPanels(sX, sY, sWidth, sHeight); end; if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd(); if (profileFrameDraw <> nil) then profileFrameDraw.sectionBegin('skyback'); g_Map_DrawBack(backXOfs, backYOfs); if (profileFrameDraw <> nil) then profileFrameDraw.sectionEnd(); if setTransMatrix then begin //if (g_dbg_scale <> 1.0) then glTranslatef(0.0, -0.375/2, 0); glScalef(g_dbg_scale, g_dbg_scale, 1.0); glTranslatef(-sX, -sY, 0); end; // rendering mode ambColor := gCurrentMap['light_ambient'].rgba; hasAmbient := (not ambColor.isOpaque) or (not ambColor.isBlack); { if hasAmbient then begin //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')'); glColor4ub(ambColor.r, ambColor.g, ambColor.b, ambColor.a); glClear(GL_COLOR_BUFFER_BIT); end; } //writeln('color: (', ambColor.r, ',', ambColor.g, ',', ambColor.b, ',', ambColor.a, ')'); drawPanelType('*back', PANEL_BACK, g_rlayer_back); drawPanelType('*step', PANEL_STEP, g_rlayer_step); drawOther('items', @r_Items_Draw); drawOther('weapons', @g_Weapon_Draw); drawOther('shells', @g_Player_DrawShells); drawOther('drawall', @g_Player_DrawAll); drawOther('corpses', @g_Player_DrawCorpses); drawPanelType('*wall', PANEL_WALL, g_rlayer_wall); drawOther('monsters', @g_Monsters_Draw); drawOther('itemdrop', @r_Items_DrawDrop); drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door); drawOther('gfx', @r_GFX_Draw); drawOther('flags', @g_Map_DrawFlags); drawPanelType('*acid1', PANEL_ACID1, g_rlayer_acid1); drawPanelType('*acid2', PANEL_ACID2, g_rlayer_acid2); drawPanelType('*water', PANEL_WATER, g_rlayer_water); drawOther('dynlights', @renderDynLightsInternal); if hasAmbient {and ((not g_playerLight) or (not gwin_has_stencil) or (g_dynLightCount < 1))} then begin renderAmbientQuad(hasAmbient, ambColor); end; doAmbient := true; drawPanelType('*fore', PANEL_FORE, g_rlayer_fore); if g_debug_HealthBar then begin g_Monsters_DrawHealth(); g_Player_DrawHealth(); end; if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering end; procedure DrawMapView(x, y, w, h: Integer); var bx, by: Integer; begin glPushMatrix(); bx := Round(x/(gMapInfo.Width - w)*(gBackSize.X - w)); by := Round(y/(gMapInfo.Height - h)*(gBackSize.Y - h)); sX := x; sY := y; sWidth := w; sHeight := h; fixViewportForScale(); renderMapInternal(-bx, -by, true); glPopMatrix(); end; procedure DrawPlayer(p: TPlayer); var px, py, a, b, c, d, i, fX, fY: Integer; camObj: TObj; //R: TRect; begin if (p = nil) or (p.FDummy) then begin glPushMatrix(); g_Map_DrawBack(0, 0); glPopMatrix(); Exit; end; if (profileFrameDraw = nil) then profileFrameDraw := TProfiler.Create('RENDER', g_profile_history_size); if (profileFrameDraw <> nil) then profileFrameDraw.mainBegin(g_profile_frame_draw); gPlayerDrawn := p; glPushMatrix(); camObj := p.getCameraObj(); camObj.lerp(gLerpFactor, fX, fY); px := fX + PLAYER_RECT_CX; py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor); if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then begin if (px > (gPlayerScreenSize.X div 2)) then a := -px+(gPlayerScreenSize.X div 2) else a := 0; if (py > (gPlayerScreenSize.Y div 2)) then b := -py+(gPlayerScreenSize.Y div 2) else b := 0; if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then a := -gMapInfo.Width+gPlayerScreenSize.X; if (py > gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then b := -gMapInfo.Height+gPlayerScreenSize.Y; if (gMapInfo.Width = gPlayerScreenSize.X) then a := 0 else if (gMapInfo.Width < gPlayerScreenSize.X) then begin // hcenter a := (gPlayerScreenSize.X-gMapInfo.Width) div 2; end; if (gMapInfo.Height = gPlayerScreenSize.Y) then b := 0 else if (gMapInfo.Height < gPlayerScreenSize.Y) then begin // vcenter b := (gPlayerScreenSize.Y-gMapInfo.Height) div 2; end; end else begin // scaled, ignore level bounds a := -px+(gPlayerScreenSize.X div 2); b := -py+(gPlayerScreenSize.Y div 2); end; sX := -a; sY := -b; sWidth := gPlayerScreenSize.X; sHeight := gPlayerScreenSize.Y; fixViewportForScale(); i := py - (sY + sHeight div 2); if (p.IncCam > 0) then begin // clamp to level bounds if (sY - p.IncCam < 0) then p.IncCam := nclamp(sY, 0, 120); // clamp around player position if (i > 0) then p.IncCam := nclamp(p.IncCam, 0, max(0, 120 - i)); end else if (p.IncCam < 0) then begin // clamp to level bounds if (sY + sHeight - p.IncCam > gMapInfo.Height) then p.IncCam := nclamp(sY + sHeight - gMapInfo.Height, -120, 0); // clamp around player position if (i < 0) then p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0); end; sY := sY - nlerp(p.IncCamOld, p.IncCam, gLerpFactor); if (not g_dbg_ignore_bounds) then begin if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth; if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight; if (sX < 0) then sX := 0; if (sY < 0) then sY := 0; end; if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth)); if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight)); //r_smallmap_h: 0: left; 1: center; 2: right //r_smallmap_v: 0: top; 1: center; 2: bottom // horiz small map? if (gMapInfo.Width = sWidth) then begin sX := 0; end else if (gMapInfo.Width < sWidth) then begin case r_smallmap_h of 1: sX := -((sWidth-gMapInfo.Width) div 2); // center 2: sX := -(sWidth-gMapInfo.Width); // right else sX := 0; // left end; end; // vert small map? if (gMapInfo.Height = sHeight) then begin sY := 0; end else if (gMapInfo.Height < sHeight) then begin case r_smallmap_v of 1: sY := -((sHeight-gMapInfo.Height) div 2); // center 2: sY := -(sHeight-gMapInfo.Height); // bottom else sY := 0; // top end; end; p.viewPortX := sX; p.viewPortY := sY; p.viewPortW := sWidth; p.viewPortH := sHeight; {$IFDEF ENABLE_HOLMES} if (p = gPlayer1) then begin g_Holmes_plrViewPos(sX, sY); g_Holmes_plrViewSize(sWidth, sHeight); end; {$ENDIF} renderMapInternal(-c, -d, true); if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then case gPlayerIndicator of 1: p.DrawIndicator(_RGB(255, 255, 255)); 2: for i := 0 to High(gPlayers) do if gPlayers[i] <> nil then if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255)) else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then if gPlayerIndicatorStyle = 1 then gPlayers[i].DrawIndicator(_RGB(192, 192, 192)) else gPlayers[i].DrawIndicator(gPlayers[i].GetColor); end; { for a := 0 to High(gCollideMap) do for b := 0 to High(gCollideMap[a]) do begin d := 0; if ByteBool(gCollideMap[a, b] and MARK_WALL) then d := d + 1; if ByteBool(gCollideMap[a, b] and MARK_DOOR) then d := d + 2; case d of 1: e_DrawPoint(1, b, a, 200, 200, 200); 2: e_DrawPoint(1, b, a, 64, 64, 255); 3: e_DrawPoint(1, b, a, 255, 0, 255); end; end; } glPopMatrix(); p.DrawPain(); p.DrawPickup(); p.DrawRulez(); if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128)); if g_Debug_Player then g_Player_DrawDebug(p); p.DrawGUI(); end; procedure drawProfilers (); var px: Integer = -1; py: Integer = -1; begin if g_profile_frame_draw and (profileFrameDraw <> nil) then px := px-drawProfiles(px, py, profileFrameDraw); if g_profile_collision and (profMapCollision <> nil) then begin px := px-drawProfiles(px, py, profMapCollision); py -= calcProfilesHeight(profMonsLOS); end; if g_profile_los and (profMonsLOS <> nil) then begin px := px-drawProfiles(px, py, profMonsLOS); py -= calcProfilesHeight(profMonsLOS); end; end; procedure r_Game_Draw(); var ID: DWORD; w, h: Word; ww, hh: Byte; Time: Int64; back: string; plView1, plView2: TPlayer; Split: Boolean; begin if gExit = EXIT_QUIT then Exit; Time := sys_GetTicks() {div 1000}; FPSCounter := FPSCounter+1; if Time - FPSTime >= 1000 then begin FPS := FPSCounter; FPSCounter := 0; FPSTime := Time; end; e_SetRendertarget(True); e_SetViewPort(0, 0, gScreenWidth, gScreenHeight); if gGameOn or (gState = STATE_FOLD) then begin if (gPlayer1 <> nil) and (gPlayer2 <> nil) then begin gSpectMode := SPECT_NONE; if not gRevertPlayers then begin plView1 := gPlayer1; plView2 := gPlayer2; end else begin plView1 := gPlayer2; plView2 := gPlayer1; end; end else if (gPlayer1 <> nil) or (gPlayer2 <> nil) then begin gSpectMode := SPECT_NONE; if gPlayer2 = nil then plView1 := gPlayer1 else plView1 := gPlayer2; plView2 := nil; end else begin plView1 := nil; plView2 := nil; end; if (plView1 = nil) and (plView2 = nil) and (gSpectMode = SPECT_NONE) then gSpectMode := SPECT_STATS; if gSpectMode = SPECT_PLAYERS then if gPlayers <> nil then begin plView1 := GetActivePlayer_ByID(gSpectPID1); if plView1 = nil then begin gSpectPID1 := GetActivePlayerID_Next(); plView1 := GetActivePlayer_ByID(gSpectPID1); end; if gSpectViewTwo then begin plView2 := GetActivePlayer_ByID(gSpectPID2); if plView2 = nil then begin gSpectPID2 := GetActivePlayerID_Next(); plView2 := GetActivePlayer_ByID(gSpectPID2); end; end; end; if gSpectMode = SPECT_MAPVIEW then begin // Режим просмотра карты Split := False; e_SetViewPort(0, 0, gScreenWidth, gScreenHeight); DrawMapView(gSpectX, gSpectY, gScreenWidth, gScreenHeight); gHearPoint1.Active := True; gHearPoint1.Coords.X := gScreenWidth div 2 + gSpectX; gHearPoint1.Coords.Y := gScreenHeight div 2 + gSpectY; gHearPoint2.Active := False; end else begin Split := (plView1 <> nil) and (plView2 <> nil); // Точки слуха игроков if plView1 <> nil then begin gHearPoint1.Active := True; gHearPoint1.Coords.X := plView1.GameX + PLAYER_RECT.Width; gHearPoint1.Coords.Y := plView1.GameY + PLAYER_RECT.Height DIV 2; end else gHearPoint1.Active := False; if plView2 <> nil then begin gHearPoint2.Active := True; gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width; gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2; end else gHearPoint2.Active := False; // Размер экранов игроков: gPlayerScreenSize.X := gScreenWidth-196; if Split then begin gPlayerScreenSize.Y := gScreenHeight div 2; if gScreenHeight mod 2 = 0 then Dec(gPlayerScreenSize.Y); end else gPlayerScreenSize.Y := gScreenHeight; if Split then if gScreenHeight mod 2 = 0 then e_SetViewPort(0, gPlayerScreenSize.Y+2, gPlayerScreenSize.X+196, gPlayerScreenSize.Y) else e_SetViewPort(0, gPlayerScreenSize.Y+1, gPlayerScreenSize.X+196, gPlayerScreenSize.Y); DrawPlayer(plView1); gPlayer1ScreenCoord.X := sX; gPlayer1ScreenCoord.Y := sY; if Split then begin e_SetViewPort(0, 0, gPlayerScreenSize.X+196, gPlayerScreenSize.Y); DrawPlayer(plView2); gPlayer2ScreenCoord.X := sX; gPlayer2ScreenCoord.Y := sY; end; e_SetViewPort(0, 0, gScreenWidth, gScreenHeight); if Split then e_DrawLine(2, 0, gScreenHeight div 2, gScreenWidth, gScreenHeight div 2, 0, 0, 0); end; {$IFDEF ENABLE_HOLMES} // draw inspector if (g_holmes_enabled) then g_Holmes_Draw(); {$ENDIF} if MessageText <> '' then begin w := 0; h := 0; e_CharFont_GetSizeFmt(gMenuFont, MessageText, w, h); if Split then e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2), (gScreenHeight div 2)-(h div 2), MessageText) else e_CharFont_PrintFmt(gMenuFont, (gScreenWidth div 2)-(w div 2), Round(gScreenHeight / 2.75)-(h div 2), MessageText); end; if IsDrawStat or (gSpectMode = SPECT_STATS) then DrawStat(); if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then begin // Draw spectator GUI ww := 0; hh := 0; e_TextureFontGetSize(gStdFont, ww, hh); case gSpectMode of SPECT_STATS: e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Stats', gStdFont, 255, 255, 255, 1); SPECT_MAPVIEW: e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Observe Map', gStdFont, 255, 255, 255, 1); SPECT_PLAYERS: e_TextureFontPrintEx(0, gScreenHeight - (hh+2)*2, 'MODE: Watch Players', gStdFont, 255, 255, 255, 1); end; e_TextureFontPrintEx(2*ww, gScreenHeight - (hh+2), '< jump >', gStdFont, 255, 255, 255, 1); if gSpectMode = SPECT_STATS then begin e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2)*2, 'Autoview', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(16*ww, gScreenHeight - (hh+2), '< fire >', gStdFont, 255, 255, 255, 1); end; if gSpectMode = SPECT_MAPVIEW then begin e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, '[-]', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(26*ww, gScreenHeight - (hh+2)*2, 'Step ' + IntToStr(gSpectStep), gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2)*2, '[+]', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(18*ww, gScreenHeight - (hh+2), '', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(30*ww, gScreenHeight - (hh+2), '', gStdFont, 255, 255, 255, 1); end; if gSpectMode = SPECT_PLAYERS then begin e_TextureFontPrintEx(22*ww, gScreenHeight - (hh+2)*2, 'Player 1', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(20*ww, gScreenHeight - (hh+2), '', gStdFont, 255, 255, 255, 1); if gSpectViewTwo then begin e_TextureFontPrintEx(37*ww, gScreenHeight - (hh+2)*2, 'Player 2', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(52*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(51*ww, gScreenHeight - (hh+2), '', gStdFont, 255, 255, 255, 1); end else begin e_TextureFontPrintEx(35*ww, gScreenHeight - (hh+2)*2, '2x View', gStdFont, 255, 255, 255, 1); e_TextureFontPrintEx(34*ww, gScreenHeight - (hh+2), '', gStdFont, 255, 255, 255, 1); end; end; end; end; if gPauseMain and gGameOn and (g_ActiveWindow = nil) then begin //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); e_CharFont_GetSize(gMenuFont, _lc[I_MENU_PAUSE], w, h); e_CharFont_Print(gMenuFont, (gScreenWidth div 2)-(w div 2), (gScreenHeight div 2)-(h div 2), _lc[I_MENU_PAUSE]); end; if not gGameOn then begin if (gState = STATE_MENU) then begin if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then DrawMenuBackground('MENU_BACKGROUND'); // F3 at menu will show game loading dialog if e_KeyPressed(IK_F3) then g_Menu_Show_LoadMenu(true); if (g_ActiveWindow <> nil) then begin //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); end else begin // F3 at titlepic will show game loading dialog if e_KeyPressed(IK_F3) then begin g_Menu_Show_LoadMenu(true); if (g_ActiveWindow <> nil) then e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); end; end; end; if gState = STATE_FOLD then begin e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter); end; if gState = STATE_INTERCUSTOM then begin if gLastMap and (gGameSettings.GameMode = GM_COOP) then begin back := 'TEXTURE_endpic'; if not g_Texture_Get(back, ID) then back := _lc[I_TEXTURE_ENDPIC]; end else back := 'INTER'; DrawMenuBackground(back); DrawCustomStat(); if g_ActiveWindow <> nil then begin //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); end; end; if gState = STATE_INTERSINGLE then begin if EndingGameCounter > 0 then begin e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 0, 0, 0, EndingGameCounter); end else begin back := 'INTER'; DrawMenuBackground(back); DrawSingleStat(); if g_ActiveWindow <> nil then begin //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); end; end; end; if gState = STATE_ENDPIC then begin ID := DWORD(-1); if g_Texture_Get('TEXTURE_endpic', ID) then DrawMenuBackground('TEXTURE_endpic') else DrawMenuBackground(_lc[I_TEXTURE_ENDPIC]); if g_ActiveWindow <> nil then begin //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); end; end; if gState = STATE_SLIST then begin // if g_Texture_Get('MENU_BACKGROUND', ID) then // begin // e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight); // //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); // end; DrawMenuBackground('MENU_BACKGROUND'); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); g_Serverlist_Draw(slCurrent, slTable); end; end; if g_ActiveWindow <> nil then begin if gGameOn then begin //e_DrawFillQuad(0, 0, gScreenWidth-1, gScreenHeight-1, 48, 48, 48, 180); e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); end; g_ActiveWindow.Draw(); end; {$IFNDEF HEADLESS} r_Console_Draw(); {$ENDIF} if g_debug_Sounds and gGameOn then begin for w := 0 to High(e_SoundsArray) do for h := 0 to e_SoundsArray[w].nRefs do e_DrawPoint(1, w+100, h+100, 255, 0, 0); end; if gShowFPS then begin e_TextureFontPrint(0, 0, Format('FPS: %d', [FPS]), gStdFont); e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont); end; if gGameOn and gShowTime then drawTime(gScreenWidth-72, gScreenHeight-16); if gGameOn then drawProfilers(); // TODO: draw this after the FBO and remap mouse click coordinates {$IFDEF ENABLE_HOLMES} g_Holmes_DrawUI(); {$ENDIF} // blit framebuffer to screen e_SetRendertarget(False); e_SetViewPort(0, 0, gWinSizeX, gWinSizeY); e_BlitFramebuffer(gWinSizeX, gWinSizeY); // draw the overlay stuff on top of it g_Touch_Draw; end; end.