From fd4788b9dbc9c05fa78c97c5fc4fcb975361fe40 Mon Sep 17 00:00:00 2001 From: DeaDDooMER Date: Mon, 7 Jun 2021 18:56:28 +0300 Subject: [PATCH] render: separate game logic and drawing --- src/game/Doom2DF.lpr | 1 + src/game/g_game.pas | 2268 ++++----------------------------- src/game/g_main.pas | 4 +- src/game/g_menu.pas | 4 +- src/game/g_netmaster.pas | 4 +- src/game/g_window.pas | 2 +- src/game/opengl/r_console.pas | 2 +- src/game/opengl/r_game.pas | 1862 +++++++++++++++++++++++++++ 8 files changed, 2086 insertions(+), 2061 deletions(-) create mode 100644 src/game/opengl/r_game.pas diff --git a/src/game/Doom2DF.lpr b/src/game/Doom2DF.lpr index e928742..3286f29 100644 --- a/src/game/Doom2DF.lpr +++ b/src/game/Doom2DF.lpr @@ -154,6 +154,7 @@ uses {$ENDIF} r_console in 'opengl/r_console.pas', + r_game in 'opengl/r_game.pas', {$IFDEF USE_FMOD} fmod in '../lib/FMOD/fmod.pas', diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 6f70992..e505f8b 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -85,7 +85,6 @@ procedure g_Game_LoadData(); procedure g_Game_FreeData(); procedure g_Game_Update(); procedure g_Game_PreUpdate(); -procedure g_Game_Draw(); procedure g_Game_Quit(); procedure g_Game_SetupScreenSize(); procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean); @@ -144,8 +143,10 @@ procedure g_Game_SetLoadingText(Text: String; Max: Integer; reWrite: Boolean); procedure g_Game_StepLoading(Value: Integer = -1); procedure g_Game_ClearLoading(); procedure g_Game_SetDebugMode(); -procedure DrawLoadingStat(); -procedure DrawMenuBackground(tex: AnsiString); + +function IsActivePlayer(p: TPlayer): Boolean; +function GetActivePlayerID_Next(Skip: Integer = -1): Integer; +procedure SortGameStat(var stat: TPlayerStatArray); { procedure SetWinPause(Enable: Boolean); } @@ -379,6 +380,55 @@ procedure g_DynLightExplosion (x, y, radius: Integer; r, g, b: Single); function conIsCheatsEnabled (): Boolean; inline; function gPause (): Boolean; inline; + type (* private state *) + TEndCustomGameStat = record + PlayerStat: TPlayerStatArray; + TeamStat: TTeamStat; + GameTime: LongWord; + GameMode: Byte; + Map, MapName: String; + end; + + TEndSingleGameStat = record + PlayerStat: Array [0..1] of record + Kills: Integer; + Secrets: Integer; + end; + GameTime: LongWord; + TwoPlayers: Boolean; + TotalSecrets: Integer; + end; + + TLoadingStat = record + CurValue: Integer; + MaxValue: Integer; + ShowCount: Integer; + Msgs: Array of String; + NextMsg: Word; + PBarWasHere: Boolean; // did we draw a progress bar for this message? + end; + + TDynLight = record + x, y, radius: Integer; + r, g, b, a: Single; + exploCount: Integer; + exploRadius: Integer; + end; + + var (* private state *) + CustomStat: TEndCustomGameStat; + StatShotDone: Boolean; + StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv + SingleStat: TEndSingleGameStat; + hasPBarGfx: Boolean; + LoadingStat: TLoadingStat; + MessageText: String; + IsDrawStat: Boolean; + EndingGameCounter: Byte; + UPS: Word; + g_playerLight: Boolean; + g_dynLights: array of TDynLight = nil; + g_dynLightCount: Integer = 0; implementation @@ -396,48 +446,9 @@ uses sfs, wadreader, g_system; -var - hasPBarGfx: Boolean = false; - - // ////////////////////////////////////////////////////////////////////////// // function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end; - -// ////////////////////////////////////////////////////////////////////////// // -function conIsCheatsEnabled (): Boolean; inline; -begin - result := false; - if g_Game_IsNet then exit; - if not gDebugMode then - begin - //if not gCheats then exit; - if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit; - if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then exit; - end; - result := true; -end; - - -// ////////////////////////////////////////////////////////////////////////// // -var - profileFrameDraw: TProfiler = nil; - - -// ////////////////////////////////////////////////////////////////////////// // -type - TDynLight = record - x, y, radius: Integer; - r, g, b, a: Single; - exploCount: Integer; - exploRadius: Integer; - end; - -var - g_dynLights: array of TDynLight = nil; - g_dynLightCount: Integer = 0; - g_playerLight: Boolean = false; - procedure g_ResetDynlights (); var lnum, idx: Integer; @@ -498,75 +509,22 @@ begin Inc(g_dynLightCount); 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; +function conIsCheatsEnabled (): Boolean; inline; 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; + result := false; + if g_Game_IsNet then exit; + if not gDebugMode then + begin + //if not gCheats then exit; + if not (gGameSettings.GameType in [GT_SINGLE, GT_CUSTOM]) then exit; + if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then exit; + end; + result := true; end; - // ////////////////////////////////////////////////////////////////////////// // type - TEndCustomGameStat = record - PlayerStat: TPlayerStatArray; - TeamStat: TTeamStat; - GameTime: LongWord; - GameMode: Byte; - Map, MapName: String; - end; - - TEndSingleGameStat = record - PlayerStat: Array [0..1] of record - Kills: Integer; - Secrets: Integer; - end; - GameTime: LongWord; - TwoPlayers: Boolean; - TotalSecrets: Integer; - end; - - TLoadingStat = record - CurValue: Integer; - MaxValue: Integer; - ShowCount: Integer; - Msgs: Array of String; - NextMsg: Word; - PBarWasHere: Boolean; // did we draw a progress bar for this message? - end; - TParamStrValue = record Name: String; Value: String; @@ -580,23 +538,14 @@ const INTER_ACTION_MUSIC = 3; var - FPS, UPS: Word; - FPSCounter, UPSCounter: Word; - FPSTime, UPSTime: LongWord; + UPSCounter: Word; + UPSTime: LongWord; DataLoaded: Boolean = False; - IsDrawStat: Boolean = False; - CustomStat: TEndCustomGameStat; - SingleStat: TEndSingleGameStat; - LoadingStat: TLoadingStat; - EndingGameCounter: Byte = 0; - MessageText: String; MessageTime: Word; MessageLineLength: Integer = 80; MapList: SSArray = nil; MapIndex: Integer = -1; InterReadyTime: Integer = -1; - StatShotDone: Boolean = False; - StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv StatDate: string = ''; MegaWAD: record info: TMegaWADInfo; @@ -891,7 +840,7 @@ begin g_Texture_CreateWADEx('TEXTURE_endpic', s); TEXTUREFILTER := GL_NEAREST; end; - MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ'); + MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\КОНЕЦ'); if MegaWAD.endmus <> '' then begin s := e_GetResourcePath(WadDirs, MegaWAD.endmus, WAD); @@ -982,7 +931,7 @@ begin if g_Game_IsNet and g_Game_IsServer then MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed)); -// Ñòîï èãðà: +// Стоп игра: gPauseMain := false; gPauseHolmes := false; gGameOn := false; @@ -999,16 +948,16 @@ begin gLMSRespawnTime := 0; case gExit of - EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà + EXIT_SIMPLE: // Выход через меню или конец теста begin g_Game_Free(); if gMapOnce then - begin // Ýòî áûë òåñò + begin // Это был тест g_Game_Quit(); end else - begin // Âûõîä â ãëàâíîå ìåíþ + begin // Выход в главное меню gMusic.SetByName('MUSIC_MENU'); gMusic.Play(); if gState <> STATE_SLIST then @@ -1017,7 +966,7 @@ begin gState := STATE_MENU; end else begin - // Îáíîâëÿåì ñïèñîê ñåðâåðîâ + // Обновляем список серверов slReturnPressed := True; if g_Net_Slist_Fetch(slCurrent) then begin @@ -1033,14 +982,14 @@ begin end; end; - EXIT_RESTART: // Íà÷àòü óðîâåíü ñíà÷àëà + EXIT_RESTART: // Начать уровень сначала begin if not g_Game_IsClient then g_Game_Restart(); end; - EXIT_ENDLEVELCUSTOM: // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå + EXIT_ENDLEVELCUSTOM: // Закончился уровень в Своей игре begin - // Ñòàòèñòèêà Ñâîåé èãðû: + // Статистика Своей игры: FileName := g_ExtractWadName(gMapInfo.Map); CustomStat.GameTime := gTime; @@ -1052,7 +1001,7 @@ begin CustomStat.PlayerStat := nil; - // Ñòàòèñòèêà èãðîêîâ: + // Статистика игроков: if gPlayers <> nil then begin for a := 0 to High(gPlayers) do @@ -1092,7 +1041,7 @@ begin if not g_Game_IsClient then g_Player_ResetReady; gInterReadyCount := 0; - // Çàòóõàþùèé ýêðàí: + // Затухающий экран: EndingGameCounter := 255; gState := STATE_FOLD; gInterTime := 0; @@ -1102,16 +1051,16 @@ begin gInterEndTime := gDefInterTime * 1000; end; - EXIT_ENDLEVELSINGLE: // Çàêîí÷èëñÿ óðîâåíü â Îäèíî÷íîé èãðå + EXIT_ENDLEVELSINGLE: // Закончился уровень в Одиночной игре begin - // Ñòàòèñòèêà Îäèíî÷íîé èãðû: + // Статистика Одиночной игры: SingleStat.GameTime := gTime; SingleStat.TwoPlayers := gPlayer2 <> nil; SingleStat.TotalSecrets := gSecretsCount; - // Ñòàòèñòèêà ïåðâîãî èãðîêà: + // Статистика первого игрока: SingleStat.PlayerStat[0].Kills := gPlayer1.MonsterKills; SingleStat.PlayerStat[0].Secrets := gPlayer1.Secrets; - // Ñòàòèñòèêà âòîðîãî èãðîêà (åñëè åñòü): + // Статистика второго игрока (если есть): if SingleStat.TwoPlayers then begin SingleStat.PlayerStat[1].Kills := gPlayer2.MonsterKills; @@ -1120,7 +1069,7 @@ begin g_Game_ExecuteEvent('onmapend'); - // Åñòü åùå êàðòû: + // Есть еще карты: if gNextMap <> '' then begin gMusic.SetByName('MUSIC_INTERMUS'); @@ -1130,244 +1079,20 @@ begin g_Game_ExecuteEvent('oninter'); end - else // Áîëüøå íåò êàðò + else // Больше нет карт begin - // Çàòóõàþùèé ýêðàí: + // Затухающий экран: EndingGameCounter := 255; gState := STATE_FOLD; end; end; end; -// Îêîí÷àíèå îáðàáîòàíî: +// Окончание обработано: if gExit <> EXIT_QUIT then gExit := 0; 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 g_Game_Init(); var SR: TSearchRec; @@ -1509,25 +1234,6 @@ begin Result := (not p.FDummy) and (not p.FSpectator); end; -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 GetActivePlayerID_Next(Skip: Integer = -1): Integer; var a, idx: Integer; @@ -1645,13 +1351,13 @@ begin MoveButton := MoveButton and $0F; if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then - MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî" + MoveButton := 1 // Нажата только "Влево" else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then - MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî" + MoveButton := 2 // Нажата только "Вправо" else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then - MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî" + MoveButton := 0; // Не нажаты ни "Влево", ни "Вправо" - // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó: + // Сейчас или раньше были нажаты "Влево"/"Вправо" => передаем игроку: if MoveButton = 1 then plr.PressKey(KEY_LEFT, time) else if MoveButton = 2 then @@ -1672,13 +1378,13 @@ begin else begin strafeDir := 0; // not strafing anymore - // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî: + // Раньше была нажата "Вправо", а сейчас "Влево" => бежим вправо, смотрим влево: if (MoveButton = 2) and gPlayerAction[p, ACTION_MOVELEFT] then plr.SetDirection(TDirection.D_LEFT) - // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî: + // Раньше была нажата "Влево", а сейчас "Вправо" => бежим влево, смотрим вправо: else if (MoveButton = 1) and gPlayerAction[p, ACTION_MOVERIGHT] then plr.SetDirection(TDirection.D_RIGHT) - // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì: + // Что-то было нажато и не изменилось => куда бежим, туда и смотрим: else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1)) end; @@ -1686,7 +1392,7 @@ begin // fix movebutton state MoveButton := MoveButton or (strafeDir shl 4); - // Îñòàëüíûå êëàâèøè: + // Остальные клавиши: if gPlayerAction[p, ACTION_JUMP] then plr.PressKey(KEY_JUMP, time); if gPlayerAction[p, ACTION_LOOKUP] then plr.PressKey(KEY_UP, time); if gPlayerAction[p, ACTION_LOOKDOWN] then plr.PressKey(KEY_DOWN, time); @@ -1781,10 +1487,10 @@ begin g_ResetDynlights(); framePool.reset(); -// Ïîðà âûêëþ÷àòü èãðó: +// Пора выключать игру: if gExit = EXIT_QUIT then Exit; -// Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì: +// Игра закончилась - обрабатываем: if gExit <> 0 then begin EndGame(); @@ -1792,10 +1498,10 @@ begin Exit; end; - // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî + // Читаем клавиатуру и джойстик, если окно активно // no need to, as we'll do it in event handler -// Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ): +// Обновляем консоль (движение и сообщения): r_Console_Update; g_Console_Update(); @@ -1810,10 +1516,10 @@ begin g_Net_Slist_Pulse(); case gState of - STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå - STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå - STATE_INTERTEXT, // Òåêñò ìåæäó óðîâíÿìè - STATE_INTERPIC: // Êàðòèíêà ìåæäó óðîâíÿìè + STATE_INTERSINGLE, // Статистка после прохождения уровня в Одиночной игре + STATE_INTERCUSTOM, // Статистка после прохождения уровня в Своей игре + STATE_INTERTEXT, // Текст между уровнями + STATE_INTERPIC: // Картинка между уровнями begin if g_Game_IsNet and g_Game_IsServer then begin @@ -1841,19 +1547,19 @@ begin or (g_Game_IsNet and ((gInterTime > gInterEndTime) or ((gInterReadyCount >= NetClientCount) and (NetClientCount > 0)))) ) then - begin // Íàæàëè /<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè: + begin // Нажали /<Пробел> или прошло достаточно времени: g_Game_StopAllSounds(True); - if gMapOnce then // Ýòî áûë òåñò + if gMapOnce then // Это был тест gExit := EXIT_SIMPLE else - if gNextMap <> '' then // Ïåðåõîäèì íà ñëåäóþùóþ êàðòó + if gNextMap <> '' then // Переходим на следующую карту g_Game_ChangeMap(gNextMap) - else // Ñëåäóþùåé êàðòû íåò + else // Следующей карты нет begin if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER] then begin - // Âûõîä â ãëàâíîå ìåíþ: + // Выход в главное меню: g_Game_Free; g_GUI_ShowWindow('MainMenu'); gMusic.SetByName('MUSIC_MENU'); @@ -1861,7 +1567,7 @@ begin gState := STATE_MENU; end else begin - // Ôèíàëüíàÿ êàðòèíêà: + // Финальная картинка: g_Game_ExecuteEvent('onwadend'); g_Game_Free(); if not gMusic.SetByName('MUSIC_endmus') then @@ -1896,11 +1602,11 @@ begin InterText.counter := InterText.counter - 1; end; - STATE_FOLD: // Çàòóõàíèå ýêðàíà + STATE_FOLD: // Затухание экрана begin if EndingGameCounter = 0 then begin - // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå: + // Закончился уровень в Своей игре: if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then begin InterReadyTime := -1; @@ -1917,7 +1623,7 @@ begin gState := STATE_INTERCUSTOM; e_UnpressAllKeys(); end - else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå + else // Закончилась последняя карта в Одиночной игре begin gMusic.SetByName('MUSIC_INTERMUS'); gMusic.Play(); @@ -1930,9 +1636,9 @@ begin DecMin(EndingGameCounter, 6, 0); end; - STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà + STATE_ENDPIC: // Картинка окончания мегаВада begin - if gMapOnce then // Ýòî áûë òåñò + if gMapOnce then // Это был тест begin gExit := EXIT_SIMPLE; Exit; @@ -1943,17 +1649,17 @@ begin g_Serverlist_Control(slCurrent, slTable); end; -// Ñòàòèñòèêà ïî Tab: +// Статистика по Tab: if gGameOn then IsDrawStat := (not gConsoleShow) and (not gChatShow) and (gGameSettings.GameType <> GT_SINGLE) and g_Console_Action(ACTION_SCORES); -// Èãðà èäåò: +// Игра идет: if gGameOn and not gPause and (gState <> STATE_FOLD) then begin - // Âðåìÿ += 28 ìèëëèñåêóíä: + // Время += 28 миллисекунд: gTime := gTime + GAME_TICK; - // Ñîîáùåíèå ïîñåðåäèíå ýêðàíà: + // Сообщение посередине экрана: if MessageTime = 0 then MessageText := ''; if MessageTime > 0 then @@ -1961,19 +1667,19 @@ begin if (g_Game_IsServer) then begin - // Áûë çàäàí ëèìèò âðåìåíè: + // Был задан лимит времени: if (gGameSettings.TimeLimit > 0) then if (gTime - gGameStartTime) div 1000 >= gGameSettings.TimeLimit then - begin // Îí ïðîøåë => êîíåö óðîâíÿ + begin // Он прошел => конец уровня g_Game_NextLevel(); Exit; end; - // Íàäî ðåñïàâíèòü èãðîêîâ â LMS: + // Надо респавнить игроков в LMS: if (gLMSRespawn > LMS_RESPAWN_NONE) and (gLMSRespawnTime < gTime) then g_Game_RestartRound(gLMSSoftSpawn); - // Ïðîâåðèì ðåçóëüòàò ãîëîñîâàíèÿ, åñëè âðåìÿ ïðîøëî + // Проверим результат голосования, если время прошло if gVoteInProgress and (gVoteTimer < gTime) then g_Game_CheckVote else if gVotePassed and (gVoteCmdTimer < gTime) then @@ -1983,19 +1689,19 @@ begin gVotePassed := False; end; - // Çàìåðÿåì âðåìÿ çàõâàòà ôëàãîâ + // Замеряем время захвата флагов if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then gFlags[FLAG_RED].CaptureTime := gFlags[FLAG_RED].CaptureTime + GAME_TICK; if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then gFlags[FLAG_BLUE].CaptureTime := gFlags[FLAG_BLUE].CaptureTime + GAME_TICK; - // Áûë çàäàí ëèìèò ïîáåä: + // Был задан лимит побед: if (gGameSettings.GoalLimit > 0) then begin b := 0; if gGameSettings.GameMode = GM_DM then - begin //  DM èùåì èãðîêà ñ max ôðàãàìè + begin // В DM ищем игрока с max фрагами for i := 0 to High(gPlayers) do if gPlayers[i] <> nil then if gPlayers[i].Frags > b then @@ -2003,11 +1709,11 @@ begin end else if gGameSettings.GameMode in [GM_TDM, GM_CTF] then - begin //  CTF/TDM âûáèðàåì êîìàíäó ñ íàèáîëüøèì ñ÷åòîì + begin // В CTF/TDM выбираем команду с наибольшим счетом b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals); end; - // Ëèìèò ïîáåä íàáðàí => êîíåö óðîâíÿ: + // Лимит побед набран => конец уровня: if b >= gGameSettings.GoalLimit then begin g_Game_NextLevel(); @@ -2015,7 +1721,7 @@ begin end; end; - // Îáðàáàòûâàåì êëàâèøè èãðîêîâ: + // Обрабатываем клавиши игроков: if gPlayer1 <> nil then gPlayer1.ReleaseKeys(); if gPlayer2 <> nil then gPlayer2.ReleaseKeys(); if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then @@ -2030,7 +1736,7 @@ begin // process weapon switch queue end; // if server - // Íàáëþäàòåëü + // Наблюдатель if (gPlayer1 = nil) and (gPlayer2 = nil) and (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then begin @@ -2189,7 +1895,7 @@ begin end; end; - // Îáíîâëÿåì âñå îñòàëüíîå: + // Обновляем все остальное: g_Map_Update(); g_Items_Update(); g_Triggers_Update(); @@ -2275,7 +1981,7 @@ begin end; end; // if gameOn ... -// Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó: +// Активно окно интерфейса - передаем клавиши ему: if g_ActiveWindow <> nil then begin w := e_GetFirstKeyPressed(); @@ -2287,11 +1993,11 @@ begin g_ActiveWindow.OnMessage(Msg); end; - // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì: + // Если оно от этого не закрылось, то обновляем: if g_ActiveWindow <> nil then g_ActiveWindow.Update(); - // Íóæíî ñìåíèòü ðàçðåøåíèå: + // Нужно сменить разрешение: if gResolutionChange then begin e_WriteLog('Changing resolution', TMsgType.Notify); @@ -2300,7 +2006,7 @@ begin g_ActiveWindow := nil; end; - // Íóæíî ñìåíèòü ÿçûê: + // Нужно сменить язык: if gLanguageChange then begin //e_WriteLog('Read language file', MSG_NOTIFY); @@ -2313,7 +2019,7 @@ begin end; end; -// Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10): +// Горячая клавиша для вызова меню выхода из игры (F10): if e_KeyPressed(IK_F10) and gGameOn and (not gConsoleShow) and @@ -2324,7 +2030,7 @@ begin Time := sys_GetTicks() {div 1000}; -// Îáðàáîòêà îòëîæåííûõ ñîáûòèé: +// Обработка отложенных событий: if gDelayedEvents <> nil then for a := 0 to High(gDelayedEvents) do if gDelayedEvents[a].Pending and @@ -2353,7 +2059,7 @@ begin gDelayedEvents[a].Pending := False; end; -// Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé: +// Каждую секунду обновляем счетчик обновлений: UPSCounter := UPSCounter + 1; if Time - UPSTime >= 1000 then begin @@ -2574,6 +2280,28 @@ begin DataLoaded := True; end; +procedure g_Game_Quit(); +begin + g_Game_StopAllSounds(True); + gMusic.Free(); + g_Game_FreeData(); + g_PlayerModel_FreeData(); + g_Texture_DeleteAll(); + g_Frames_DeleteAll(); +{$IFNDEF HEADLESS} + //g_Menu_Free(); //k8: this segfaults after resolution change; who cares? +{$ENDIF} + + if NetInitDone then g_Net_Free; + +// remove map after test + if gMapToDelete <> '' then + g_Game_DeleteTestMap(); + + gExit := EXIT_QUIT; + sys_RequestQuit; +end; + procedure g_Game_FreeData(); begin if not DataLoaded then Exit; @@ -2667,1572 +2395,6 @@ begin DataLoaded := False; 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', @g_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', @g_Items_DrawDrop); - drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door); - drawOther('gfx', @g_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 g_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; - -procedure g_Game_Quit(); -begin - g_Game_StopAllSounds(True); - gMusic.Free(); - g_Game_FreeData(); - g_PlayerModel_FreeData(); - g_Texture_DeleteAll(); - g_Frames_DeleteAll(); -{$IFNDEF HEADLESS} - //g_Menu_Free(); //k8: this segfaults after resolution change; who cares? -{$ENDIF} - - if NetInitDone then g_Net_Free; - -// Íàäî óäàëèòü êàðòó ïîñëå òåñòà: - if gMapToDelete <> '' then - g_Game_DeleteTestMap(); - - gExit := EXIT_QUIT; - sys_RequestQuit; -end; - procedure g_FatalError(Text: String); begin g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True); @@ -4256,14 +2418,14 @@ var rf: Single; bw, bh: Word; begin -// Ðàçìåð ýêðàíîâ èãðîêîâ: +// Размер экранов игроков: gPlayerScreenSize.X := gScreenWidth-196; if (gPlayer1 <> nil) and (gPlayer2 <> nil) then gPlayerScreenSize.Y := gScreenHeight div 2 else gPlayerScreenSize.Y := gScreenHeight; -// Ðàçìåð çàäíåãî ïëàíà: +// Размер заднего плана: if BackID <> DWORD(-1) then begin s := SKY_STRETCH; @@ -4313,7 +2475,7 @@ begin if not (Team in [TEAM_RED, TEAM_BLUE]) then Team := gPlayer1Settings.Team; - // Ñîçäàíèå ïåðâîãî èãðîêà: + // Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, Team, False)); @@ -4343,7 +2505,7 @@ begin if not (Team in [TEAM_RED, TEAM_BLUE]) then Team := gPlayer2Settings.Team; - // Ñîçäàíèå âòîðîãî èãðîêà: + // Создание второго игрока: gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, gPlayer2Settings.Color, Team, False)); @@ -4433,7 +2595,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: FillByte(gGameSettings, SizeOf(TGameSettings), 0); gAimLine := False; gShowMap := False; @@ -4453,10 +2615,10 @@ begin g_Game_ExecuteEvent('ongamestart'); -// Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: +// Установка размеров окон игроков: g_Game_SetupScreenSize(); -// Ñîçäàíèå ïåðâîãî èãðîêà: +// Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, gPlayer1Settings.Team, False)); @@ -4469,7 +2631,7 @@ begin gPlayer1.Name := gPlayer1Settings.Name; nPl := 1; -// Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü: +// Создание второго игрока, если есть: if TwoPlayers then begin gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, @@ -4485,7 +2647,7 @@ begin Inc(nPl); end; -// Çàãðóçêà è çàïóñê êàðòû: +// Загрузка и запуск карты: if not g_Game_StartMap(false{asMegawad}, MAP, True) then begin if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP; @@ -4493,10 +2655,10 @@ begin Exit; end; -// Íàñòðîéêè èãðîêîâ è áîòîâ: +// Настройки игроков и ботов: g_Player_Init(); -// Ñîçäàåì áîòîâ: +// Создаем ботов: for i := nPl+1 to nPlayers do g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True); end; @@ -4514,7 +2676,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: gGameSettings.GameType := GT_CUSTOM; gGameSettings.GameMode := GameMode; gSwitchGameMode := GameMode; @@ -4537,10 +2699,10 @@ begin g_Game_ExecuteEvent('ongamestart'); -// Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: +// Установка размеров окон игроков: g_Game_SetupScreenSize(); -// Ðåæèì íàáëþäàòåëÿ: +// Режим наблюдателя: if nPlayers = 0 then begin gPlayer1 := nil; @@ -4550,7 +2712,7 @@ begin nPl := 0; if nPlayers >= 1 then begin - // Ñîçäàíèå ïåðâîãî èãðîêà: + // Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, gPlayer1Settings.Team, False)); @@ -4566,7 +2728,7 @@ begin if nPlayers >= 2 then begin - // Ñîçäàíèå âòîðîãî èãðîêà: + // Создание второго игрока: gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, gPlayer2Settings.Color, gPlayer2Settings.Team, False)); @@ -4580,14 +2742,14 @@ begin Inc(nPl); end; -// Çàãðóçêà è çàïóñê êàðòû: +// Загрузка и запуск карты: if not g_Game_StartMap(true{asMegawad}, Map, True) then begin g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map])); Exit; end; -// Íåò òî÷åê ïîÿâëåíèÿ: +// Нет точек появления: if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) + g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) + g_Map_GetPointCount(RESPAWNPOINT_DM) + @@ -4598,10 +2760,10 @@ begin Exit; end; -// Íàñòðîéêè èãðîêîâ è áîòîâ: +// Настройки игроков и ботов: g_Player_Init(); -// Ñîçäàåì áîòîâ: +// Создаем ботов: for i := nPl+1 to nPlayers do g_Player_Create(STD_PLAYER_MODEL, _RGB(0, 0, 0), 0, True); end; @@ -4618,7 +2780,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: gGameSettings.GameType := GT_SERVER; gGameSettings.GameMode := GameMode; gSwitchGameMode := GameMode; @@ -4641,10 +2803,10 @@ begin g_Game_ExecuteEvent('ongamestart'); -// Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà +// Установка размеров окна игрока g_Game_SetupScreenSize(); -// Ðåæèì íàáëþäàòåëÿ: +// Режим наблюдателя: if nPlayers = 0 then begin gPlayer1 := nil; @@ -4653,7 +2815,7 @@ begin if nPlayers >= 1 then begin - // Ñîçäàíèå ïåðâîãî èãðîêà: + // Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, gPlayer1Settings.Team, False)); @@ -4668,7 +2830,7 @@ begin if nPlayers >= 2 then begin - // Ñîçäàíèå âòîðîãî èãðîêà: + // Создание второго игрока: gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, gPlayer2Settings.Color, gPlayer2Settings.Team, False)); @@ -4685,7 +2847,7 @@ begin if NetForwardPorts then g_Game_SetLoadingText(_lc[I_LOAD_PORTS], 0, False); -// Ñòàðòóåì ñåðâåð +// Стартуем сервер if not g_Net_Host(IPAddr, Port, NetMaxClients) then begin g_FatalError(_lc[I_NET_MSG] + Format(_lc[I_NET_ERR_HOST], [Port])); @@ -4696,7 +2858,7 @@ begin g_Net_Slist_ServerStarted(); -// Çàãðóçêà è çàïóñê êàðòû: +// Загрузка и запуск карты: if not g_Game_StartMap(false{asMegawad}, Map, True) then begin g_Net_Slist_ServerClosed(); @@ -4704,7 +2866,7 @@ begin Exit; end; -// Íåò òî÷åê ïîÿâëåíèÿ: +// Нет точек появления: if (g_Map_GetPointCount(RESPAWNPOINT_PLAYER1) + g_Map_GetPointCount(RESPAWNPOINT_PLAYER2) + g_Map_GetPointCount(RESPAWNPOINT_DM) + @@ -4716,7 +2878,7 @@ begin Exit; end; -// Íàñòðîéêè èãðîêîâ è áîòîâ: +// Настройки игроков и ботов: g_Player_Init(); g_Net_Slist_ServerMapStarted(); @@ -4743,7 +2905,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: gGameSettings.GameType := GT_CLIENT; gCoopTotalMonstersKilled := 0; @@ -4755,7 +2917,7 @@ begin g_Game_ExecuteEvent('ongamestart'); -// Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: +// Установка размеров окон игроков: g_Game_SetupScreenSize(); NetState := NET_STATE_AUTH; @@ -4770,7 +2932,7 @@ begin gSpectLatchPID1 := 0; gSpectLatchPID2 := 0; -// Ñòàðòóåì êëèåíò +// Стартуем клиент if not g_Net_Connect(Addr, Port) then begin g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]); @@ -4925,7 +3087,7 @@ begin g_Game_ClearLoading(); Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]; - // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü + // Если уровень завершился по триггеру Выход, не очищать инвентарь if gExitByTrigger then begin Force := False; @@ -5042,7 +3204,7 @@ begin begin g_Map_ResetFlag(FLAG_RED); g_Map_ResetFlag(FLAG_BLUE); - // CTF, à ôëàãîâ íåò: + // CTF, а флагов нет: if not g_Map_HaveFlagPoints() then g_SimpleError(_lc[I_GAME_ERROR_CTF]); end; @@ -5099,7 +3261,7 @@ begin begin MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map); - // Ìàñòåðñåðâåð + // Мастерсервер g_Net_Slist_ServerMapStarted(); if NetClients <> nil then @@ -5152,10 +3314,10 @@ begin gCoopTotalMonsters := gCoopTotalMonsters + gTotalMonsters; gCoopTotalSecrets := gCoopTotalSecrets + gSecretsCount; -// Âûøëè â âûõîä â Îäèíî÷íîé èãðå: +// Вышли в выход в Одиночной игре: if gGameSettings.GameType = GT_SINGLE then gExit := EXIT_ENDLEVELSINGLE - else // Âûøëè â âûõîä â Ñâîåé èãðå + else // Вышли в выход в Своей игре begin gExit := EXIT_ENDLEVELCUSTOM; if gGameSettings.GameMode = GM_COOP then @@ -5400,7 +3562,7 @@ begin if (a = 0) then a := Pos('.wad:/', toLowerCase1251(gMapToDelete)); if (a = 0) then exit; - // Âûäåëÿåì èìÿ wad-ôàéëà è èìÿ êàðòû + // Выделяем имя wad-файла и имя карты WadName := Copy(gMapToDelete, 1, a+3); Delete(gMapToDelete, 1, a+5); gMapToDelete := UpperCase(gMapToDelete); @@ -5408,7 +3570,7 @@ begin //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete))); { -// Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå: +// Имя карты не стандартное тестовое: if MapName <> TEST_MAP_NAME then Exit; @@ -5417,14 +3579,14 @@ begin time := g_GetFileTime(WadName); WAD := TWADFile.Create(); - // ×èòàåì Wad-ôàéë: + // Читаем Wad-файл: if not WAD.ReadFile(WadName) then - begin // Íåò òàêîãî WAD-ôàéëà + begin // Нет такого WAD-файла WAD.Free(); Exit; end; - // Ñîñòàâëÿåì ñïèñîê êàðò è èùåì íóæíóþ: + // Составляем список карт и ищем нужную: WAD.CreateImage(); MapList := WAD.GetResourcesList(''); @@ -5432,7 +3594,7 @@ begin for a := 0 to High(MapList) do if MapList[a] = MapName then begin - // Óäàëÿåì è ñîõðàíÿåì: + // Удаляем и сохраняем: WAD.RemoveResource('', MapName); WAD.SaveTo(WadName); Break; @@ -5909,7 +4071,7 @@ var //pt: TDFPoint; mon: TMonster; begin -// Êîìàíäû îòëàäî÷íîãî ðåæèìà: +// Команды отладочного режима: if {gDebugMode}conIsCheatsEnabled then begin cmd := LowerCase(P[0]); @@ -6258,7 +4420,7 @@ var listen: LongWord; found: Boolean; begin -// Îáùèå êîìàíäû: +// Общие команды: cmd := LowerCase(P[0]); chstr := ''; if cmd = 'pause' then @@ -6310,7 +4472,7 @@ begin if gPlayers[a] <> nil then if Copy(LowerCase(gPlayers[a].Name), 1, Length(P[1])) = LowerCase(P[1]) then begin - // Íå îòêëþ÷àòü îñíîâíûõ èãðîêîâ â ñèíãëå + // Не отключать основных игроков в сингле if not(gPlayers[a] is TBot) and (gGameSettings.GameType = GT_SINGLE) then continue; gPlayers[a].Lives := 0; @@ -6318,7 +4480,7 @@ begin g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True); g_Player_Remove(gPlayers[a].UID); g_Net_Slist_ServerPlayerLeaves(); - // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå + // Если не перемешать, при добавлении новых ботов появятся старые g_Bot_MixNames(); end; end else @@ -7216,7 +5378,7 @@ begin gSelectWeapon[b, a] := True end end -// Êîìàíäû Ñâîåé èãðû: +// Команды Своей игры: else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then begin if cmd = 'bot_addred' then @@ -7320,7 +5482,7 @@ begin begin if (Length(P) = 1) or (StrToIntDef(P[1], -1) <= 0) then Exit; - // Äîïîëíèòåëüíîå âðåìÿ: + // Дополнительное время: gGameSettings.TimeLimit := (gTime - gGameStartTime) div 1000 + Word(StrToIntDef(P[1], 0)); g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT], @@ -7536,14 +5698,14 @@ begin end; g_Sound_PlayEx('MENU_OPEN'); - // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå: + // Пауза при меню только в одиночной игре: if (not g_Game_IsNet) then g_Game_Pause(True); end else if (g_ActiveWindow <> nil) and (not Show) then begin - // Ïàóçà ïðè ìåíþ òîëüêî â îäèíî÷íîé èãðå: + // Пауза при меню только в одиночной игре: if (not g_Game_IsNet) then g_Game_Pause(False); end; @@ -7580,7 +5742,7 @@ procedure g_Game_PauseAllSounds(Enable: Boolean); var i: Integer; begin -// Òðèããåðû: +// Триггеры: if gTriggers <> nil then for i := 0 to High(gTriggers) do with gTriggers[i] do @@ -7591,13 +5753,13 @@ begin Sound.Pause(Enable); end; -// Çâóêè èãðîêîâ: +// Звуки игроков: if gPlayers <> nil then for i := 0 to High(gPlayers) do if gPlayers[i] <> nil then gPlayers[i].PauseSounds(Enable); -// Ìóçûêà: +// Музыка: if gMusic <> nil then gMusic.Pause(Enable); end; @@ -7964,7 +6126,7 @@ end; procedure g_Game_SetDebugMode(); begin gDebugMode := True; -// ×èòû (äàæå â ñâîåé èãðå): +// Читы (даже в своей игре): gCheats := True; end; @@ -7978,7 +6140,7 @@ begin with LoadingStat do begin if not reWrite then - begin // Ïåðåõîäèì íà ñëåäóþùóþ ñòðîêó èëè ñêðîëëèðóåì: + begin // Переходим на следующую строку или скроллируем: if NextMsg = Length(Msgs) then begin // scroll for i := 0 to High(Msgs)-1 do @@ -8053,7 +6215,7 @@ begin if (s[1] = '-') and (Length(s) > 1) then begin if (s[2] = '-') and (Length(s) > 2) then - begin // Îäèíî÷íûé ïàðàìåòð + begin // Одиночный параметр SetLength(pars, Length(pars) + 1); with pars[High(pars)] do begin @@ -8063,7 +6225,7 @@ begin end else if (i < ParamCount) then - begin // Ïàðàìåòð ñî çíà÷åíèåì + begin // Параметр со значением Inc(i); SetLength(pars, Length(pars) + 1); with pars[High(pars)] do @@ -8309,4 +6471,4 @@ begin conRegVar('r_showspect', @gSpectHUD, 'show spectator hud', 'show spectator hud'); conRegVar('r_showstat', @gShowStat, 'show stats', 'show stats'); conRegVar('r_showpids', @gShowPIDs, 'show PIDs', 'show PIDs'); -end. +end. \ No newline at end of file diff --git a/src/game/g_main.pas b/src/game/g_main.pas index af95f90..e9ef055 100644 --- a/src/game/g_main.pas +++ b/src/game/g_main.pas @@ -71,7 +71,7 @@ uses e_sound, g_options, g_sound, g_player, g_basic, g_weapons, SysUtils, g_triggers, MAPDEF, g_map, e_res, g_menu, g_language, g_net, g_touch, g_system, g_res_downloader, - conbuf, envvars, + conbuf, envvars, r_game, xparser; @@ -662,7 +662,7 @@ end; procedure Draw (); begin - g_Game_Draw(); + r_Game_Draw(); end; diff --git a/src/game/g_menu.pas b/src/game/g_menu.pas index bd21314..67814ad 100644 --- a/src/game/g_menu.pas +++ b/src/game/g_menu.pas @@ -50,7 +50,7 @@ uses MAPDEF, Math, g_saveload, e_texture, g_language, e_res, g_net, g_netmsg, g_netmaster, g_items, e_input, g_touch, - utils, wadreader, g_system; + utils, wadreader, g_system, r_game; type TYNCallback = procedure (yes:Boolean); @@ -785,7 +785,7 @@ begin slWaitStr := _lc[I_NET_SLIST_WAIT]; - g_Game_Draw; + r_Game_Draw; sys_Repaint; slReturnPressed := True; diff --git a/src/game/g_netmaster.pas b/src/game/g_netmaster.pas index 8827515..2662d42 100644 --- a/src/game/g_netmaster.pas +++ b/src/game/g_netmaster.pas @@ -173,7 +173,7 @@ implementation uses e_input, e_graphics, e_log, g_window, g_net, g_console, - g_map, g_game, g_sound, g_gui, g_menu, g_options, g_language, g_basic, + g_map, g_game, g_sound, g_gui, g_menu, g_options, g_language, g_basic, r_game, wadreader, g_system, utils, hashtable; @@ -1924,7 +1924,7 @@ begin begin slWaitStr := _lc[I_NET_SLIST_WAIT]; - g_Game_Draw; + r_Game_Draw; sys_Repaint; if g_Net_Slist_Fetch(SL) then diff --git a/src/game/g_window.pas b/src/game/g_window.pas index 954790b..4108647 100644 --- a/src/game/g_window.pas +++ b/src/game/g_window.pas @@ -41,7 +41,7 @@ uses {$INCLUDE ../nogl/noGLuses.inc} SysUtils, Classes, MAPDEF, Math, e_graphics, e_log, e_texture, g_main, - g_console, r_console, e_input, g_options, g_game, + g_console, r_console, e_input, g_options, g_game, r_game, g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net, g_map, g_gfx, g_monsters, xprofiler, g_touch, g_gui, g_system, g_netmaster; diff --git a/src/game/opengl/r_console.pas b/src/game/opengl/r_console.pas index 194efe1..78eccc9 100644 --- a/src/game/opengl/r_console.pas +++ b/src/game/opengl/r_console.pas @@ -12,7 +12,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) -{$INCLUDE ../shared/a_modes.inc} +{$INCLUDE ../../shared/a_modes.inc} unit r_console; interface diff --git a/src/game/opengl/r_game.pas b/src/game/opengl/r_game.pas new file mode 100644 index 0000000..6dd62a2 --- /dev/null +++ b/src/game/opengl/r_game.pas @@ -0,0 +1,1862 @@ +(* 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 + ; + + 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', @g_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', @g_Items_DrawDrop); + drawPanelType('*door', PANEL_CLOSEDOOR, g_rlayer_door); + drawOther('gfx', @g_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. -- 2.29.2