X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_game.pas;h=019fdc48445c2c861b49055aff9302419817fc90;hb=16342bee09fa001d05697571124e48a93cd35f2c;hp=2084d8876e43323c661c78b7f741a01567defe89;hpb=7d53b8719f818712900fefb1c36e41b1fe9b3f32;p=d2df-sdl.git diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 2084d88..019fdc4 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -17,12 +17,15 @@ unit g_game; interface -uses - SysUtils, Classes, - MAPDEF, - g_basic, g_player, e_graphics, g_res_downloader, - g_sound, g_gui, utils, md5, mempool, xprofiler, - g_touch, g_weapons; + uses + {$IFDEF ENABLE_MENU} + g_gui, + {$ENDIF} + SysUtils, Classes, MAPDEF, + g_base, g_basic, g_player, g_res_downloader, + g_sound, utils, md5, mempool, xprofiler, + g_weapons + ; type TGameSettings = record @@ -31,6 +34,8 @@ type TimeLimit: Word; GoalLimit: Word; WarmupTime: Word; + SpawnInvul: Word; + ItemRespawnTime: Word; MaxLives: Byte; Options: LongWord; WAD: String; @@ -82,10 +87,8 @@ procedure g_Game_Free (freeTextures: Boolean=true); procedure g_Game_LoadData(); procedure g_Game_FreeData(); procedure g_Game_Update(); -procedure g_Game_Draw(); +procedure g_Game_PreUpdate(); procedure g_Game_Quit(); -procedure g_Game_SetupScreenSize(); -procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean); function g_Game_ModeToText(Mode: Byte): string; function g_Game_TextToMode(Mode: string): Byte; procedure g_Game_ExecuteEvent(Name: String); @@ -101,9 +104,8 @@ procedure g_Game_StartClient(Addr: String; Port: Word; PW: String); procedure g_Game_Restart(); procedure g_Game_RestartLevel(); procedure g_Game_RestartRound(NoMapRestart: Boolean = False); -procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest); -procedure g_Game_SaveOptions(); -function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean; +function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString; +function g_Game_StartMap(asMegawad: Boolean; Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean; procedure g_Game_ChangeMap(const MapPath: String); procedure g_Game_ExitLevel(const Map: AnsiString); function g_Game_GetFirstMap(WAD: String): String; @@ -111,7 +113,6 @@ function g_Game_GetNextMap(): String; procedure g_Game_NextLevel(); procedure g_Game_Pause(Enable: Boolean); procedure g_Game_HolmesPause(Enable: Boolean); -procedure g_Game_InGameMenu(Show: Boolean); function g_Game_IsWatchedPlayer(UID: Word): Boolean; function g_Game_IsWatchedTeam(Team: Byte): Boolean; procedure g_Game_Message(Msg: String; Time: Word); @@ -126,12 +127,16 @@ procedure g_Game_Announce_KillCombo(Param: Integer); procedure g_Game_Announce_BodyKill(SpawnerUID: Word); procedure g_Game_StartVote(Command, Initiator: string); procedure g_Game_CheckVote; -procedure g_TakeScreenShot(); +{$IFNDEF HEADLESS} + procedure g_TakeScreenShot(Filename: string = ''); +{$ENDIF} procedure g_FatalError(Text: String); procedure g_SimpleError(Text: String); function g_Game_IsTestMap(): Boolean; procedure g_Game_DeleteTestMap(); procedure GameCVars(P: SSArray); +procedure PlayerSettingsCVars(P: SSArray); +procedure SystemCommands(P: SSArray); procedure GameCommands(P: SSArray); procedure GameCheats(P: SSArray); procedure DebugCommands(P: SSArray); @@ -140,8 +145,19 @@ 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); + + +{$IFDEF ENABLE_MENU} + procedure g_Game_InGameMenu(Show: Boolean); +{$ENDIF} +{$IFNDEF HEADLESS} + procedure CharPress (C: AnsiChar); +{$ENDIF} + procedure KeyPress (K: Word); { procedure SetWinPause(Enable: Boolean); } @@ -164,21 +180,23 @@ const GM_COOP = 4; GM_SINGLE = 5; - MESSAGE_DIKEY = WM_USER + 1; - EXIT_QUIT = 1; EXIT_SIMPLE = 2; EXIT_RESTART = 3; EXIT_ENDLEVELSINGLE = 4; EXIT_ENDLEVELCUSTOM = 5; - GAME_OPTION_RESERVED = 1; - GAME_OPTION_TEAMDAMAGE = 2; - GAME_OPTION_ALLOWEXIT = 4; - GAME_OPTION_WEAPONSTAY = 8; - GAME_OPTION_MONSTERS = 16; - GAME_OPTION_BOTVSPLAYER = 32; - GAME_OPTION_BOTVSMONSTER = 64; + GAME_OPTION_RESERVED = 1; + GAME_OPTION_TEAMDAMAGE = 2; + GAME_OPTION_ALLOWEXIT = 4; + GAME_OPTION_WEAPONSTAY = 8; + GAME_OPTION_MONSTERS = 16; + GAME_OPTION_BOTVSPLAYER = 32; + GAME_OPTION_BOTVSMONSTER = 64; + GAME_OPTION_DMKEYS = 128; + GAME_OPTION_TEAMHITTRACE = 256; + GAME_OPTION_TEAMHITPROJECTILE = 512; + GAME_OPTION_TEAMABSORBDAMAGE = 1024; STATE_NONE = 0; STATE_MENU = 1; @@ -221,8 +239,9 @@ const DEFAULT_PLAYERS = 1; {$ENDIF} + STATFILE_VERSION = $03; + var - gStdFont: DWORD; gGameSettings: TGameSettings; gPlayer1Settings: TPlayerSettings; gPlayer2Settings: TPlayerSettings; @@ -234,11 +253,12 @@ var gPlayer2: TPlayer = nil; gPlayerDrawn: TPlayer = nil; gTime: LongWord; + gLerpFactor: Single = 1.0; gSwitchGameMode: Byte = GM_DM; gHearPoint1, gHearPoint2: THearPoint; gSoundEffectsDF: Boolean = False; gSoundTriggerTime: Word = 0; - gAnnouncer: Byte = ANNOUNCE_NONE; + gAnnouncer: Integer = ANNOUNCE_NONE; goodsnd: array[0..3] of TPlayableSound; killsnd: array[0..3] of TPlayableSound; hahasnd: array[0..2] of TPlayableSound; @@ -259,6 +279,7 @@ var gShowFPS: Boolean = False; gShowGoals: Boolean = True; gShowStat: Boolean = True; + gShowPIDs: Boolean = False; gShowKillMsg: Boolean = True; gShowLives: Boolean = True; gShowPing: Boolean = False; @@ -287,12 +308,10 @@ var gMapToDelete: String; gTempDelete: Boolean = False; gLastMap: Boolean = False; - gWinPosX, gWinPosY: Integer; - gWinSizeX, gWinSizeY: Integer; - gWinFrameX, gWinFrameY, gWinCaption: Integer; - gWinActive: Boolean = True; // by default window is active, lol + gScreenWidth: Word; + gScreenHeight: Word; gResolutionChange: Boolean = False; - gRC_Width, gRC_Height: Word; + gRC_Width, gRC_Height: Integer; gRC_FullScreen, gRC_Maximized: Boolean; gLanguageChange: Boolean = False; gDebugMode: Boolean = False; @@ -361,6 +380,7 @@ var g_rlayer_water: Boolean = true; g_rlayer_fore: Boolean = true; + wNeedTimeReset: Boolean = false; procedure g_ResetDynlights (); procedure g_AddDynLight (x, y, radius: Integer; r, g, b, a: Single); @@ -369,64 +389,444 @@ 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; + LoadingStat: TLoadingStat; + MessageText: String; + IsDrawStat: Boolean; + EndingGameCounter: Byte; + UPS: Word; + g_playerLight: Boolean; + g_dynLights: array of TDynLight = nil; + g_dynLightCount: Integer = 0; + EndPicPath: AnsiString; // full path, used by render implementation uses -{$INCLUDE ../nogl/noGLuses.inc} -{$IFDEF ENABLE_HOLMES} - g_holmes, -{$ENDIF} - e_texture, g_textures, g_main, g_window, g_menu, + {$IFDEF ENABLE_HOLMES} + g_holmes, + {$ENDIF} + {$IFDEF ENABLE_MENU} + g_menu, + {$ENDIF} + {$IFDEF ENABLE_GFX} + g_gfx, + {$ENDIF} + {$IFDEF ENABLE_GIBS} + g_gibs, + {$ENDIF} + {$IFNDEF HEADLESS} + r_render, g_system, + {$ENDIF} + e_res, g_window, e_input, e_log, g_console, g_items, g_map, g_panel, - g_playermodel, g_gfx, g_options, Math, + g_playermodel, g_options, Math, g_triggers, g_monsters, e_sound, CONFIG, - g_language, g_net, + g_language, g_net, g_phys, ENet, e_msg, g_netmsg, g_netmaster, sfs, wadreader; + var + charbuff: packed array [0..15] of AnsiChar = ( + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' + ); +function Translit (const S: AnsiString): AnsiString; var - hasPBarGfx: Boolean = false; - + i: Integer; +begin + Result := S; + for i := 1 to Length(Result) do + begin + case Result[i] of + #$C9: Result[i] := 'Q'; + #$D6: Result[i] := 'W'; + #$D3: Result[i] := 'E'; + #$CA: Result[i] := 'R'; + #$C5: Result[i] := 'T'; + #$CD: Result[i] := 'Y'; + #$C3: Result[i] := 'U'; + #$D8: Result[i] := 'I'; + #$D9: Result[i] := 'O'; + #$C7: Result[i] := 'P'; + #$D5: Result[i] := '['; //Chr(219); + #$DA: Result[i] := ']'; //Chr(221); + #$D4: Result[i] := 'A'; + #$DB: Result[i] := 'S'; + #$C2: Result[i] := 'D'; + #$C0: Result[i] := 'F'; + #$CF: Result[i] := 'G'; + #$D0: Result[i] := 'H'; + #$CE: Result[i] := 'J'; + #$CB: Result[i] := 'K'; + #$C4: Result[i] := 'L'; + #$C6: Result[i] := ';'; //Chr(186); + #$DD: Result[i] := #39; //Chr(222); + #$DF: Result[i] := 'Z'; + #$D7: Result[i] := 'X'; + #$D1: Result[i] := 'C'; + #$CC: Result[i] := 'V'; + #$C8: Result[i] := 'B'; + #$D2: Result[i] := 'N'; + #$DC: Result[i] := 'M'; + #$C1: Result[i] := ','; //Chr(188); + #$DE: Result[i] := '.'; //Chr(190); + end; + end; +end; -// ////////////////////////////////////////////////////////////////////////// // -function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end; +function CheckCheat (ct: TStrings_Locale; eofs: Integer=0): Boolean; +var + ls1, ls2: string; +begin + ls1 := CheatEng[ct]; + ls2 := Translit(CheatRus[ct]); + if length(ls1) = 0 then ls1 := '~'; + if length(ls2) = 0 then ls2 := '~'; + result := + (Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)) = ls1) or + (Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))) = ls1) or + (Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)) = ls2) or + (Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))) = ls2); + { + if ct = I_GAME_CHEAT_JETPACK then + begin + e_WriteLog('ls1: ['+ls1+']', MSG_NOTIFY); + e_WriteLog('ls2: ['+ls2+']', MSG_NOTIFY); + e_WriteLog('bf0: ['+Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1))+']', MSG_NOTIFY); + e_WriteLog('bf1: ['+Translit(Copy(charbuff, 17-Length(ls1)-eofs, Length(ls1)))+']', MSG_NOTIFY); + e_WriteLog('bf2: ['+Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2))+']', MSG_NOTIFY); + e_WriteLog('bf3: ['+Translit(Copy(charbuff, 17-Length(ls2)-eofs, Length(ls2)))+']', MSG_NOTIFY); + end; + } +end; -// ////////////////////////////////////////////////////////////////////////// // -function conIsCheatsEnabled (): Boolean; inline; +procedure Cheat (); +const + CHEAT_DAMAGE = 500; +label + Cheated; +var + s, s2: string; + c: ShortString; + a: Integer; begin - result := false; - if g_Game_IsNet then exit; - if not gDebugMode then + { + if (not gGameOn) or (not gCheats) or ((gGameSettings.GameType <> GT_SINGLE) and + (gGameSettings.GameMode <> GM_COOP) and (not gDebugMode)) + or g_Game_IsNet then Exit; + } + if not gGameOn then exit; + if not conIsCheatsEnabled then exit; + + s := 'SOUND_GAME_RADIO'; + + // + if CheckCheat(I_GAME_CHEAT_GODMODE) 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; + if gPlayer1 <> nil then gPlayer1.GodMode := not gPlayer1.GodMode; + if gPlayer2 <> nil then gPlayer2.GodMode := not gPlayer2.GodMode; + goto Cheated; end; - result := true; + // RAMBO + if CheckCheat(I_GAME_CHEAT_WEAPONS) then + begin + if gPlayer1 <> nil then gPlayer1.AllRulez(False); + if gPlayer2 <> nil then gPlayer2.AllRulez(False); + goto Cheated; + end; + // TANK + if CheckCheat(I_GAME_CHEAT_HEALTH) then + begin + if gPlayer1 <> nil then gPlayer1.AllRulez(True); + if gPlayer2 <> nil then gPlayer2.AllRulez(True); + goto Cheated; + end; + // IDDQD + if CheckCheat(I_GAME_CHEAT_DEATH) then + begin + if gPlayer1 <> nil then gPlayer1.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP); + if gPlayer2 <> nil then gPlayer2.Damage(CHEAT_DAMAGE, 0, 0, 0, HIT_TRAP); + s := 'SOUND_MONSTER_HAHA'; + goto Cheated; + end; + // + if CheckCheat(I_GAME_CHEAT_DOORS) then + begin + g_Triggers_OpenAll(); + goto Cheated; + end; + // GOODBYE + if CheckCheat(I_GAME_CHEAT_NEXTMAP) then + begin + if gTriggers <> nil then + for a := 0 to High(gTriggers) do + if gTriggers[a].TriggerType = TRIGGER_EXIT then + begin + gExitByTrigger := True; + //g_Game_ExitLevel(gTriggers[a].Data.MapName); + g_Game_ExitLevel(gTriggers[a].tgcMap); + Break; + end; + goto Cheated; + end; + // + s2 := Copy(charbuff, 15, 2); + if CheckCheat(I_GAME_CHEAT_CHANGEMAP, 2) and (s2[1] >= '0') and (s2[1] <= '9') and (s2[2] >= '0') and (s2[2] <= '9') then + begin + if g_Map_Exist(gGameSettings.WAD + ':\MAP' + s2) then + begin + c := 'MAP' + s2; + g_Game_ExitLevel(c); + end; + goto Cheated; + end; + // + if CheckCheat(I_GAME_CHEAT_FLY) then + begin + gFly := not gFly; + goto Cheated; + end; + // BULLFROG + if CheckCheat(I_GAME_CHEAT_JUMPS) then + begin + VEL_JUMP := 30-VEL_JUMP; + goto Cheated; + end; + // FORMULA1 + if CheckCheat(I_GAME_CHEAT_SPEED) then + begin + MAX_RUNVEL := 32-MAX_RUNVEL; + goto Cheated; + end; + // CONDOM + if CheckCheat(I_GAME_CHEAT_SUIT) then + begin + if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_SUIT); + if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_SUIT); + goto Cheated; + end; + // + if CheckCheat(I_GAME_CHEAT_AIR) then + begin + if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_OXYGEN); + if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_OXYGEN); + goto Cheated; + end; + // PURELOVE + if CheckCheat(I_GAME_CHEAT_BERSERK) then + begin + if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_MEDKIT_BLACK); + if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_MEDKIT_BLACK); + goto Cheated; + end; + // + if CheckCheat(I_GAME_CHEAT_JETPACK) then + begin + if gPlayer1 <> nil then gPlayer1.GiveItem(ITEM_JETPACK); + if gPlayer2 <> nil then gPlayer2.GiveItem(ITEM_JETPACK); + goto Cheated; + end; + // CASPER + if CheckCheat(I_GAME_CHEAT_NOCLIP) then + begin + if gPlayer1 <> nil then gPlayer1.SwitchNoClip; + if gPlayer2 <> nil then gPlayer2.SwitchNoClip; + goto Cheated; + end; + // + if CheckCheat(I_GAME_CHEAT_NOTARGET) then + begin + if gPlayer1 <> nil then gPlayer1.NoTarget := not gPlayer1.NoTarget; + if gPlayer2 <> nil then gPlayer2.NoTarget := not gPlayer2.NoTarget; + goto Cheated; + end; + // INFERNO + if CheckCheat(I_GAME_CHEAT_NORELOAD) then + begin + if gPlayer1 <> nil then gPlayer1.NoReload := not gPlayer1.NoReload; + if gPlayer2 <> nil then gPlayer2.NoReload := not gPlayer2.NoReload; + goto Cheated; + end; + if CheckCheat(I_GAME_CHEAT_AIMLINE) then + begin + gAimLine := not gAimLine; + goto Cheated; + end; + if CheckCheat(I_GAME_CHEAT_AUTOMAP) then + begin + gShowMap := not gShowMap; + goto Cheated; + end; + Exit; + +Cheated: + g_Sound_PlayEx(s); end; -// ////////////////////////////////////////////////////////////////////////// // -var - profileFrameDraw: TProfiler = nil; +{$IFDEF ENABLE_MENU} +procedure KeyPress (K: Word); + var Msg: g_gui.TMessage; +begin + case K of + VK_ESCAPE: // : + begin + if (g_ActiveWindow <> nil) then + begin + Msg.Msg := WM_KEYDOWN; + Msg.WParam := VK_ESCAPE; + g_ActiveWindow.OnMessage(Msg); + if (not g_Game_IsNet) and (g_ActiveWindow = nil) then g_Game_Pause(false); //Fn loves to do this + end + else if (gState <> STATE_FOLD) then + begin + if gGameOn or (gState = STATE_INTERSINGLE) or (gState = STATE_INTERCUSTOM) then + begin + g_Game_InGameMenu(True); + end + else if (gExit = 0) and (gState <> STATE_SLIST) then + begin + if (gState <> STATE_MENU) then + begin + if (NetMode <> NET_NONE) then + begin + g_Game_StopAllSounds(True); + g_Game_Free; + gState := STATE_MENU; + Exit; + end; + end; + g_GUI_ShowWindow('MainMenu'); + g_Sound_PlayEx('MENU_OPEN'); + end; + end; + end; + + IK_F2, IK_F3, IK_F4, IK_F5, IK_F6, IK_F7, IK_F10: + begin // .. � + if gGameOn and (not gConsoleShow) and (not gChatShow) then + begin + while (g_ActiveWindow <> nil) do g_GUI_HideWindow(False); + if (not g_Game_IsNet) then g_Game_Pause(True); + case K of + IK_F2: g_Menu_Show_SaveMenu(); + IK_F3: g_Menu_Show_LoadMenu(); + IK_F4: g_Menu_Show_GameSetGame(); + IK_F5: g_Menu_Show_OptionsVideo(); + IK_F6: g_Menu_Show_OptionsSound(); + IK_F7: g_Menu_Show_EndGameMenu(); + IK_F10: g_Menu_Show_QuitGameMenu(); + end; + end; + end; + else + begin + gJustChatted := False; + if gConsoleShow or gChatShow then + begin + g_Console_Control(K); + end + else if (g_ActiveWindow <> nil) then + begin + Msg.Msg := WM_KEYDOWN; + Msg.WParam := K; + g_ActiveWindow.OnMessage(Msg); + end + else if (gState = STATE_MENU) then + begin + g_GUI_ShowWindow('MainMenu'); + g_Sound_PlayEx('MENU_OPEN'); + end; + end; + end; +end; +{$ELSE} + procedure KeyPress (K: Word); + begin + gJustChatted := False; + if gConsoleShow or gChatShow then + begin + g_Console_Control(K); + end + end; +{$ENDIF} -// ////////////////////////////////////////////////////////////////////////// // -type - TDynLight = record - x, y, radius: Integer; - r, g, b, a: Single; - exploCount: Integer; - exploRadius: Integer; +{$IFNDEF HEADLESS} + procedure CharPress (C: AnsiChar); + {$IFDEF ENABLE_MENU} + var Msg: g_gui.TMessage; + {$ENDIF} + var a: Integer; + begin + if gConsoleShow or gChatShow then + begin + g_Console_Char(C); + end + {$IFDEF ENABLE_MENU} + else if g_ActiveWindow <> nil then + begin + Msg.Msg := WM_CHAR; + Msg.WParam := Ord(C); + g_ActiveWindow.OnMessage(Msg); + end + {$ENDIF} + else + begin + for a := 0 to 14 do + begin + charbuff[a] := charbuff[a + 1]; + end; + charbuff[15] := upcase1251(C); + Cheat; + end; end; +{$ENDIF} -var - g_dynLights: array of TDynLight = nil; - g_dynLightCount: Integer = 0; - g_playerLight: Boolean = false; + +// ////////////////////////////////////////////////////////////////////////// // +function gPause (): Boolean; inline; begin result := gPauseMain or gPauseHolmes; end; procedure g_ResetDynlights (); var @@ -488,75 +888,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; @@ -570,41 +917,19 @@ 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; + StatDate: string = ''; MegaWAD: record info: TMegaWADInfo; endpic: String; endmus: String; - res: record - text: Array of ShortString; - anim: Array of ShortString; - pic: Array of ShortString; - mus: Array of ShortString; - end; - triggers: Array of record - event: ShortString; - actions: Array of record - action, p1, p2: Integer; - end; - end; - cur_trigger: Integer; - cur_action: Integer; end; - //InterPic: String; InterText: record lines: SSArray; img: String; @@ -643,6 +968,68 @@ begin end; end; +// saves a shitty CSV containing the game stats passed to it +procedure SaveGameStat(Stat: TEndCustomGameStat; Path: string); +var + s: TextFile; + dir, fname, map, mode, etime: String; + I: Integer; +begin + try + dir := e_GetWriteableDir(StatsDirs); + // stats are placed in stats/yy/mm/dd/*.csv + fname := e_CatPath(dir, Path); + ForceDirectories(fname); // ensure yy/mm/dd exists within the stats dir + fname := e_CatPath(fname, StatFilename + '.csv'); + AssignFile(s, fname); + try + Rewrite(s); + // line 1: stats ver, datetime, server name, map name, game mode, time limit, score limit, dmflags, game time, num players + if g_Game_IsNet then fname := NetServerName else fname := ''; + map := g_ExtractWadNameNoPath(gMapInfo.Map) + ':/' + g_ExtractFileName(gMapInfo.Map); + mode := g_Game_ModeToText(Stat.GameMode); + etime := Format('%d:%.2d:%.2d', [ + Stat.GameTime div 1000 div 3600, + (Stat.GameTime div 1000 div 60) mod 60, + Stat.GameTime div 1000 mod 60 + ]); + WriteLn(s, 'stats_ver,datetime,server,map,mode,timelimit,scorelimit,dmflags,time,num_players'); + WriteLn(s, Format('%d,%s,%s,%s,%s,%u,%u,%u,%s,%d', [ + STATFILE_VERSION, + StatDate, + dquoteStr(fname), + dquoteStr(map), + mode, + gGameSettings.TimeLimit, + gGameSettings.GoalLimit, + gGameSettings.Options, + etime, + Length(Stat.PlayerStat) + ])); + // line 2: game specific shit + // if it's a team game: red score, blue score + // if it's a coop game: monsters killed, monsters total, secrets found, secrets total + // otherwise nothing + if Stat.GameMode in [GM_TDM, GM_CTF] then + WriteLn(s, + Format('red_score,blue_score' + LineEnding + '%d,%d', [Stat.TeamStat[TEAM_RED].Goals, Stat.TeamStat[TEAM_BLUE].Goals])) + else if Stat.GameMode in [GM_COOP, GM_SINGLE] then + WriteLn(s, + Format('mon_killed,mon_total,secrets_found,secrets_total' + LineEnding + '%d,%d,%d,%d',[gCoopMonstersKilled, gTotalMonsters, gCoopSecretsFound, gSecretsCount])); + // lines 3-...: team, player name, frags, deaths + WriteLn(s, 'team,name,frags,deaths'); + for I := Low(Stat.PlayerStat) to High(Stat.PlayerStat) do + with Stat.PlayerStat[I] do + WriteLn(s, Format('%d,%s,%d,%d', [Team, dquoteStr(Name), Frags, Deaths])); + except + g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [fname])); + end; + except + g_Console_Add('could not create gamestats file "' + fname + '"'); + end; + CloseFile(s); +end; + function g_Game_ModeToText(Mode: Byte): string; begin Result := ''; @@ -731,48 +1118,31 @@ begin FreeMem(p); end; -procedure g_Game_FreeWAD(); -var - a: Integer; -begin - for a := 0 to High(MegaWAD.res.pic) do - if MegaWAD.res.pic[a] <> '' then - g_Texture_Delete(MegaWAD.res.pic[a]); - - for a := 0 to High(MegaWAD.res.mus) do - if MegaWAD.res.mus[a] <> '' then - g_Sound_Delete(MegaWAD.res.mus[a]); - - MegaWAD.res.pic := nil; - MegaWAD.res.text := nil; - MegaWAD.res.anim := nil; - MegaWAD.res.mus := nil; - MegaWAD.triggers := nil; - - g_Texture_Delete('TEXTURE_endpic'); - g_Sound_Delete('MUSIC_endmus'); - - ZeroMemory(@MegaWAD, SizeOf(MegaWAD)); - gGameSettings.WAD := ''; -end; + procedure g_Game_FreeWAD; + begin + EndPicPath := ''; + g_Sound_Delete('MUSIC_endmus'); + ZeroMemory(@MegaWAD, SizeOf(MegaWAD)); + gGameSettings.WAD := ''; + end; procedure g_Game_LoadWAD(WAD: string); var w: TWADFile; cfg: TConfig; p: Pointer; - {b, }len: Integer; - s: string; + len: Integer; + s: AnsiString; begin g_Game_FreeWAD(); gGameSettings.WAD := WAD; if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then Exit; - MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD); + MegaWAD.info := g_Game_GetMegaWADInfo(WAD); w := TWADFile.Create(); - w.ReadFile(MapsDir + WAD); + w.ReadFile(WAD); if not w.GetResource('INTERSCRIPT', p, len) then begin @@ -782,47 +1152,16 @@ begin cfg := TConfig.CreateMem(p, len); - {b := 1; - while True do - begin - s := cfg.ReadStr('pic', 'pic'+IntToStr(b), ''); - if s = '' then Break; - b := b+1; - - SetLength(MegaWAD.res.pic, Length(MegaWAD.res.pic)+1); - MegaWAD.res.pic[High(MegaWAD.res.pic)] := s; - - g_Texture_CreateWADEx(s, s); - end; - - b := 1; - while True do - begin - s := cfg.ReadStr('mus', 'mus'+IntToStr(b), ''); - if s = '' then Break; - b := b+1; - - SetLength(MegaWAD.res.mus, Length(MegaWAD.res.mus)+1); - MegaWAD.res.mus[High(MegaWAD.res.mus)] := s; - - g_Music_CreateWADEx(s, s); - end;} - + EndPicPath := ''; MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', ''); if MegaWAD.endpic <> '' then - begin - s := g_ExtractWadName(MegaWAD.endpic); - if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/'; - TEXTUREFILTER := GL_LINEAR; - g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic); - TEXTUREFILTER := GL_NEAREST; - end; - MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ'); + EndPicPath := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD); + + MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\КОНЕЦ'); if MegaWAD.endmus <> '' then begin - s := g_ExtractWadName(MegaWAD.endmus); - if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/'; - g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True); + s := e_GetResourcePath(WadDirs, MegaWAD.endmus, WAD); + g_Sound_CreateWADEx('MUSIC_endmus', s, True); end; cfg.Free(); @@ -894,7 +1233,7 @@ begin gDelayedEvents[n].DENum := Num; gDelayedEvents[n].DEStr := Str; if DEType = DE_GLOBEVENT then - gDelayedEvents[n].Time := (GetTimer() {div 1000}) + Time + gDelayedEvents[n].Time := (GetTickCount64() {div 1000}) + Time else gDelayedEvents[n].Time := gTime + Time; Result := n; @@ -904,11 +1243,12 @@ procedure EndGame(); var a: Integer; FileName: string; + t: TDateTime; begin if g_Game_IsNet and g_Game_IsServer then MH_SEND_GameEvent(NET_EV_MAPEND, Byte(gMissionFailed)); -// Ñòîï èãðà: +// Стоп игра: gPauseMain := false; gPauseHolmes := false; gGameOn := false; @@ -919,31 +1259,37 @@ begin MessageText := ''; EndingGameCounter := 0; + +{$IFDEF ENABLE_MENU} g_ActiveWindow := nil; +{$ENDIF} gLMSRespawn := LMS_RESPAWN_NONE; gLMSRespawnTime := 0; case gExit of - EXIT_SIMPLE: // Âûõîä ÷åðåç ìåíþ èëè êîíåö òåñòà + EXIT_SIMPLE: // Выход через меню или конец теста begin g_Game_Free(); - if gMapOnce then - begin // Ýòî áûë òåñò - g_Game_Quit(); - end + begin // Это был тест + g_Game_Quit(); + end else - begin // Âûõîä â ãëàâíîå ìåíþ + begin // Выход в главное меню + {$IFDEF DISABLE_MENU} + gState := STATE_MENU; // ??? + {$ELSE} gMusic.SetByName('MUSIC_MENU'); gMusic.Play(); if gState <> STATE_SLIST then begin g_GUI_ShowWindow('MainMenu'); gState := STATE_MENU; - end else + end + else begin - // Îáíîâëÿåì ñïèñîê ñåðâåðîâ + // Обновляем список серверов slReturnPressed := True; if g_Net_Slist_Fetch(slCurrent) then begin @@ -954,19 +1300,19 @@ begin slWaitStr := _lc[I_NET_SLIST_ERROR]; g_Serverlist_GenerateTable(slCurrent, slTable); end; - - g_Game_ExecuteEvent('ongameend'); - end; + {$ENDIF} + g_Game_ExecuteEvent('ongameend'); + 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; @@ -978,7 +1324,7 @@ begin CustomStat.PlayerStat := nil; - // Ñòàòèñòèêà èãðîêîâ: + // Статистика игроков: if gPlayers <> nil then begin for a := 0 to High(gPlayers) do @@ -999,13 +1345,26 @@ begin end; SortGameStat(CustomStat.PlayerStat); + + if (gSaveStats or gScreenshotStats) and (Length(CustomStat.PlayerStat) > 1) then + begin + t := Now; + if g_Game_IsNet then StatFilename := NetServerName else StatFilename := 'local'; + StatDate := FormatDateTime('yymmdd_hhnnss', t); + StatFilename := StatFilename + '_' + CustomStat.Map + '_' + g_Game_ModeToText(CustomStat.GameMode); + StatFilename := sanitizeFilename(StatFilename) + '_' + StatDate; + if gSaveStats then + SaveGameStat(CustomStat, FormatDateTime('yyyy"/"mm"/"dd', t)); + end; + + StatShotDone := False; end; g_Game_ExecuteEvent('onmapend'); if not g_Game_IsClient then g_Player_ResetReady; gInterReadyCount := 0; - // Çàòóõàþùèé ýêðàí: + // Затухающий экран: EndingGameCounter := 255; gState := STATE_FOLD; gInterTime := 0; @@ -1015,16 +1374,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; @@ -1033,7 +1392,7 @@ begin g_Game_ExecuteEvent('onmapend'); - // Åñòü åùå êàðòû: + // Есть еще карты: if gNextMap <> '' then begin gMusic.SetByName('MUSIC_INTERMUS'); @@ -1043,287 +1402,34 @@ 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; +procedure g_Game_Init(); 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); + gExit := 0; + gMapToDelete := ''; + gTempDelete := False; - 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); + sfsGCDisable(); // temporary disable removing of temporary volumes - drawTime(x+w-78, y+8); + try + g_Game_ClearLoading(); + g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False); + g_Game_SetLoadingText('', 0, False); - 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; - // Èìÿ - e_TextureFontPrintEx(x+16, _y, Name, 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; - // Öâåò èãðîêà - 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, Name, 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; -begin - gExit := 0; - gMapToDelete := ''; - gTempDelete := False; - - sfsGCDisable(); // temporary disable removing of temporary volumes - - try - TEXTUREFILTER := GL_LINEAR; - g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE'); - g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER'); - g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN'); - g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU'); - TEXTUREFILTER := GL_NEAREST; - - LoadStdFont('STDTXT', 'STDFONT', gStdFont); - LoadFont('MENUTXT', 'MENUFONT', gMenuFont); - LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont); - - g_Game_ClearLoading(); - g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False); - g_Game_SetLoadingText('', 0, False); - - g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False); - g_Console_Init(); - - g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False); - g_PlayerModel_LoadData(); - - if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then - repeat - if not g_PlayerModel_Load(ModelsDir+SR.Name) then - e_WriteLog(Format('Error loading model %s', [SR.Name]), TMsgType.Warning); - until FindNext(SR) <> 0; - FindClose(SR); - - if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then - repeat - if not g_PlayerModel_Load(ModelsDir+SR.Name) then - e_WriteLog(Format('Error loading model %s', [SR.Name]), TMsgType.Warning); - until FindNext(SR) <> 0; - FindClose(SR); - - if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then - repeat - if not g_PlayerModel_Load(ModelsDir+SR.Name) then - e_WriteLog(Format('Error loading model %s', [SR.Name]), TMsgType.Warning); - until FindNext(SR) <> 0; - FindClose(SR); +// g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False); gGameOn := false; gPauseMain := false; @@ -1341,11 +1447,6 @@ begin g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True, True); g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True); -{$IFNDEF HEADLESS} - g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False); - g_Menu_Init(); -{$ENDIF} - gMusic := TMusic.Create(); gMusic.SetByName('MUSIC_MENU'); gMusic.Play(); @@ -1392,25 +1493,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; @@ -1528,13 +1610,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 @@ -1555,13 +1637,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; @@ -1569,7 +1651,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); @@ -1590,6 +1672,7 @@ begin end end; +{$IFDEF ENABLE_MENU} // HACK: add dynlight here if gwin_k8_enable_light_experiments then begin @@ -1604,6 +1687,7 @@ begin end; if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6); +{$ENDIF} end; // HACK: don't have a "key was pressed" function @@ -1614,13 +1698,26 @@ begin MC_SEND_CheatRequest(NET_CHEAT_READY); end; +procedure g_Game_PreUpdate(); +begin + // these are in separate PreUpdate functions because they can interact during Update() + // and are synced over the net + // we don't care that much about corpses and gibs + g_Player_PreUpdate(); + g_Monsters_PreUpdate(); + g_Items_PreUpdate(); + g_Weapon_PreUpdate(); +end; + procedure g_Game_Update(); -var - Msg: g_gui.TMessage; - Time: Int64; - a: Byte; - w: Word; - i, b: Integer; + var + {$IFDEF ENABLE_MENU} + Msg: g_gui.TMessage; + w: Word; + {$ENDIF} + Time: Int64; + a: Byte; + i, b: Integer; function sendMonsPos (mon: TMonster): Boolean; begin @@ -1653,10 +1750,10 @@ begin g_ResetDynlights(); framePool.reset(); -// Ïîðà âûêëþ÷àòü èãðó: +// Пора выключать игру: if gExit = EXIT_QUIT then Exit; -// Èãðà çàêîí÷èëàñü - îáðàáàòûâàåì: +// Игра закончилась - обрабатываем: if gExit <> 0 then begin EndGame(); @@ -1664,10 +1761,10 @@ begin Exit; end; - // ×èòàåì êëàâèàòóðó è äæîéñòèê, åñëè îêíî àêòèâíî + // Читаем клавиатуру и джойстик, если окно активно // no need to, as we'll do it in event handler -// Îáíîâëÿåì êîíñîëü (äâèæåíèå è ñîîáùåíèÿ): +// Обновляем консоль (движение и сообщения): g_Console_Update(); if (NetMode = NET_NONE) and (g_Game_IsNet) and (gGameOn or (gState in [STATE_FOLD, STATE_INTERCUSTOM])) then @@ -1677,11 +1774,14 @@ begin Exit; end; + // process master server communications + 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 @@ -1704,32 +1804,36 @@ begin e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK) ) and (not gJustChatted) and (not gConsoleShow) and (not gChatShow) +{$IFDEF ENABLE_MENU} and (g_ActiveWindow = nil) +{$ENDIF} ) - or (g_Game_IsNet and ((gInterTime > gInterEndTime) or (gInterReadyCount >= NetClientCount))) + 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; +{$IFDEF ENABLE_MENU} g_GUI_ShowWindow('MainMenu'); gMusic.SetByName('MUSIC_MENU'); gMusic.Play(); +{$ENDIF} gState := STATE_MENU; end else begin - // Ôèíàëüíàÿ êàðòèíêà: + // Финальная картинка: g_Game_ExecuteEvent('onwadend'); g_Game_Free(); if not gMusic.SetByName('MUSIC_endmus') then @@ -1751,7 +1855,9 @@ begin e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK) ) and (not gJustChatted) and (not gConsoleShow) and (not gChatShow) +{$IFDEF ENABLE_MENU} and (g_ActiveWindow = nil) +{$ENDIF} ) then begin @@ -1764,11 +1870,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; @@ -1785,7 +1891,7 @@ begin gState := STATE_INTERCUSTOM; e_UnpressAllKeys(); end - else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå + else // Закончилась последняя карта в Одиночной игре begin gMusic.SetByName('MUSIC_INTERMUS'); gMusic.Play(); @@ -1798,9 +1904,9 @@ begin DecMin(EndingGameCounter, 6, 0); end; - STATE_ENDPIC: // Êàðòèíêà îêîí÷àíèÿ ìåãàÂàäà + STATE_ENDPIC: // Картинка окончания мегаВада begin - if gMapOnce then // Ýòî áûë òåñò + if gMapOnce then // Это был тест begin gExit := EXIT_SIMPLE; Exit; @@ -1811,17 +1917,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 @@ -1829,19 +1935,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 @@ -1851,19 +1957,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 @@ -1871,11 +1977,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(); @@ -1883,10 +1989,14 @@ begin end; end; - // Îáðàáàòûâàåì êëàâèøè èãðîêîâ: + // Обрабатываем клавиши игроков: if gPlayer1 <> nil then gPlayer1.ReleaseKeys(); if gPlayer2 <> nil then gPlayer2.ReleaseKeys(); +{$IFDEF DISABLE_MENU} + if (not gConsoleShow) and (not gChatShow) then +{$ELSE} if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then +{$ENDIF} begin ProcessPlayerControls(gPlayer1, 0, P1MoveButton); ProcessPlayerControls(gPlayer2, 1, P2MoveButton); @@ -1898,9 +2008,13 @@ 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 + // Наблюдатель + if (gPlayer1 = nil) and (gPlayer2 = nil) + and (not gConsoleShow) and (not gChatShow) +{$IFDEF ENABLE_MENU} + and (g_ActiveWindow = nil) +{$ENDIF} + then begin if not gSpectKeyPress then begin @@ -2057,14 +2171,19 @@ begin end; end; - // Îáíîâëÿåì âñå îñòàëüíîå: + // Обновляем все остальное: g_Map_Update(); g_Items_Update(); g_Triggers_Update(); g_Weapon_Update(); g_Monsters_Update(); - g_GFX_Update(); + {$IFDEF ENABLE_GFX} + g_GFX_Update; + {$ENDIF} g_Player_UpdateAll(); + {$IFDEF ENABLE_GIBS} + g_Gibs_Update; + {$ENDIF} g_Player_UpdatePhysicalObjects(); // server: send newly spawned monsters unconditionally @@ -2124,19 +2243,18 @@ begin // send unexpected platform changes g_Map_NetSendInterestingPanels(); + g_Net_Slist_ServerUpdate(); + { if NetUseMaster then begin - if gTime >= NetTimeToMaster then + if (gTime >= NetTimeToMaster) or g_Net_Slist_IsConnectionInProgress then begin - if (NetMHost = nil) or (NetMPeer = nil) then - begin - g_Net_Slist_Connect(false); // non-blocking connection to the master - end; - + if (not g_Net_Slist_IsConnectionActive) then g_Net_Slist_Connect(false); // non-blocking connection to the master g_Net_Slist_Update; NetTimeToMaster := gTime + NetMasterRate; end; end; + } end else if (NetMode = NET_CLIENT) then begin @@ -2144,7 +2262,8 @@ begin end; end; // if gameOn ... -// Àêòèâíî îêíî èíòåðôåéñà - ïåðåäàåì êëàâèøè åìó: +// Активно окно интерфейса - передаем клавиши ему: +{$IFDEF ENABLE_MENU} if g_ActiveWindow <> nil then begin w := e_GetFirstKeyPressed(); @@ -2156,33 +2275,33 @@ begin g_ActiveWindow.OnMessage(Msg); end; - // Åñëè îíî îò ýòîãî íå çàêðûëîñü, òî îáíîâëÿåì: + // Если оно от этого не закрылось, то обновляем: if g_ActiveWindow <> nil then g_ActiveWindow.Update(); - // Íóæíî ñìåíèòü ðàçðåøåíèå: + // Нужно сменить разрешение: if gResolutionChange then begin - e_WriteLog('Changing resolution', TMsgType.Notify); - g_Game_ChangeResolution(gRC_Width, gRC_Height, gRC_FullScreen, gRC_Maximized); + {$IFNDEF HEADLESS} + e_WriteLog('Changing resolution', TMsgType.Notify); + r_Render_Apply; + {$ENDIF} gResolutionChange := False; g_ActiveWindow := nil; end; - // Íóæíî ñìåíèòü ÿçûê: + // Нужно сменить язык: if gLanguageChange then begin //e_WriteLog('Read language file', MSG_NOTIFY); //g_Language_Load(DataDir + gLanguage + '.txt'); g_Language_Set(gLanguage); -{$IFNDEF HEADLESS} g_Menu_Reset(); -{$ENDIF} gLanguageChange := False; end; end; -// Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10): +// Горячая клавиша для вызова меню выхода из игры (F10): if e_KeyPressed(IK_F10) and gGameOn and (not gConsoleShow) and @@ -2190,10 +2309,11 @@ begin begin KeyPress(IK_F10); end; +{$ENDIF} - Time := GetTimer() {div 1000}; + Time := GetTickCount64() {div 1000}; -// Îáðàáîòêà îòëîæåííûõ ñîáûòèé: +// Обработка отложенных событий: if gDelayedEvents <> nil then for a := 0 to High(gDelayedEvents) do if gDelayedEvents[a].Pending and @@ -2222,7 +2342,7 @@ begin gDelayedEvents[a].Pending := False; end; -// Êàæäóþ ñåêóíäó îáíîâëÿåì ñ÷åò÷èê îáíîâëåíèé: +// Каждую секунду обновляем счетчик обновлений: UPSCounter := UPSCounter + 1; if Time - UPSTime >= 1000 then begin @@ -2296,61 +2416,11 @@ begin end; procedure g_Game_LoadData(); -var - wl, hl: Integer; - wr, hr: Integer; - wb, hb: Integer; - wm, hm: Integer; begin if DataLoaded then Exit; e_WriteLog('Loading game data...', TMsgType.Notify); - g_Texture_CreateWADEx('NOTEXTURE', GameWAD+':TEXTURES\NOTEXTURE'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_HUD', GameWAD+':TEXTURES\HUD'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDAIR', GameWAD+':TEXTURES\AIRBAR'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDJET', GameWAD+':TEXTURES\JETBAR'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_HUDBG', GameWAD+':TEXTURES\HUDBG'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_ARMORHUD', GameWAD+':TEXTURES\ARMORHUD'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG', GameWAD+':TEXTURES\FLAGHUD_R_BASE'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_S', GameWAD+':TEXTURES\FLAGHUD_R_STOLEN'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_REDFLAG_D', GameWAD+':TEXTURES\FLAGHUD_R_DROP'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG', GameWAD+':TEXTURES\FLAGHUD_B_BASE'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_S', GameWAD+':TEXTURES\FLAGHUD_B_STOLEN'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_BLUEFLAG_D', GameWAD+':TEXTURES\FLAGHUD_B_DROP'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_TALKBUBBLE', GameWAD+':TEXTURES\TALKBUBBLE'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_INVULPENTA', GameWAD+':TEXTURES\PENTA'); - g_Texture_CreateWADEx('TEXTURE_PLAYER_INDICATOR', GameWAD+':TEXTURES\PLRIND'); - - hasPBarGfx := true; - if not g_Texture_CreateWADEx('UI_GFX_PBAR_LEFT', GameWAD+':TEXTURES\LLEFT') then hasPBarGfx := false; - if not g_Texture_CreateWADEx('UI_GFX_PBAR_MARKER', GameWAD+':TEXTURES\LMARKER') then hasPBarGfx := false; - if not g_Texture_CreateWADEx('UI_GFX_PBAR_MIDDLE', GameWAD+':TEXTURES\LMIDDLE') then hasPBarGfx := false; - if not g_Texture_CreateWADEx('UI_GFX_PBAR_RIGHT', GameWAD+':TEXTURES\LRIGHT') then hasPBarGfx := false; - - if hasPBarGfx then - begin - g_Texture_GetSize('UI_GFX_PBAR_LEFT', wl, hl); - g_Texture_GetSize('UI_GFX_PBAR_RIGHT', wr, hr); - g_Texture_GetSize('UI_GFX_PBAR_MIDDLE', wb, hb); - g_Texture_GetSize('UI_GFX_PBAR_MARKER', wm, hm); - if (wl > 0) and (hl > 0) and (wr > 0) and (hr = hl) and (wb > 0) and (hb = hl) and (wm > 0) and (hm > 0) and (hm <= hl) then - begin - // yay! - end - else - begin - hasPBarGfx := false; - end; - end; - - g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':WEAPONS\PUNCH', 64, 64, 4, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_UP', GameWAD+':WEAPONS\PUNCH_UP', 64, 64, 4, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_DN', GameWAD+':WEAPONS\PUNCH_DN', 64, 64, 4, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK', GameWAD+':WEAPONS\PUNCHB', 64, 64, 4, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_UP', GameWAD+':WEAPONS\PUNCHB_UP', 64, 64, 4, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH_BERSERK_DN', GameWAD+':WEAPONS\PUNCHB_DN', 64, 64, 4, False); g_Sound_CreateWADEx('SOUND_GAME_TELEPORT', GameWAD+':SOUNDS\TELEPORT'); g_Sound_CreateWADEx('SOUND_GAME_NOTELEPORT', GameWAD+':SOUNDS\NOTELEPORT'); g_Sound_CreateWADEx('SOUND_GAME_SECRET', GameWAD+':SOUNDS\SECRET'); @@ -2443,6 +2513,29 @@ begin DataLoaded := True; end; +procedure g_Game_Quit(); +begin + g_Game_StopAllSounds(True); + gMusic.Free(); + g_Game_FreeData(); + g_PlayerModel_FreeData(); +{$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; + + {$IFNDEF HEADLESS} + sys_RequestQuit; + {$ENDIF} +end; + procedure g_Game_FreeData(); begin if not DataLoaded then Exit; @@ -2453,25 +2546,6 @@ begin e_WriteLog('Releasing game data...', TMsgType.Notify); - g_Texture_Delete('NOTEXTURE'); - g_Texture_Delete('TEXTURE_PLAYER_HUD'); - g_Texture_Delete('TEXTURE_PLAYER_HUDBG'); - g_Texture_Delete('TEXTURE_PLAYER_ARMORHUD'); - g_Texture_Delete('TEXTURE_PLAYER_REDFLAG'); - g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_S'); - g_Texture_Delete('TEXTURE_PLAYER_REDFLAG_D'); - g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG'); - g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_S'); - g_Texture_Delete('TEXTURE_PLAYER_BLUEFLAG_D'); - g_Texture_Delete('TEXTURE_PLAYER_TALKBUBBLE'); - g_Texture_Delete('TEXTURE_PLAYER_INVULPENTA'); - g_Frames_DeleteByName('FRAMES_TELEPORT'); - g_Frames_DeleteByName('FRAMES_PUNCH'); - g_Frames_DeleteByName('FRAMES_PUNCH_UP'); - g_Frames_DeleteByName('FRAMES_PUNCH_DN'); - g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK'); - g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_UP'); - g_Frames_DeleteByName('FRAMES_PUNCH_BERSERK_DN'); g_Sound_Delete('SOUND_GAME_TELEPORT'); g_Sound_Delete('SOUND_GAME_NOTELEPORT'); g_Sound_Delete('SOUND_GAME_SECRET'); @@ -2536,1706 +2610,91 @@ 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; +procedure g_FatalError(Text: String); begin - e_TextureFontGetSize(gStdFont, ww2, hh2); + g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True); + e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning); - g_ProcessMessages(); + gExit := EXIT_SIMPLE; + if gGameOn then EndGame; +end; - if g_Console_Action(ACTION_SCORES) then - begin - if not gStatsPressed then - begin - gStatsOff := not gStatsOff; - gStatsPressed := True; - end; - end - else - gStatsPressed := False; +procedure g_SimpleError(Text: String); +begin + g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True); + e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning); +end; - 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); +procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE); +begin + if ((not gGameOn) and (gState <> STATE_INTERCUSTOM)) + or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then 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 (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then + Exit; - if g_Game_IsNet then + if gPlayer1 = nil 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: + if g_Game_IsClient then begin - if gGameSettings.MaxLives = 0 then - s1 := _lc[I_GAME_TDM] - else - s1 := _lc[I_GAME_TLMS]; + if NetPlrUID1 > -1 then + MC_SEND_CheatRequest(NET_CHEAT_SPECTATE); + Exit; end; - GM_CTF: s1 := _lc[I_GAME_CTF]; - GM_COOP: + + 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)); + if gPlayer1 = nil then + g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1])) + else 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); + gPlayer1.Name := gPlayer1Settings.Name; + g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True); + if g_Game_IsServer and g_Game_IsNet then + MH_SEND_PlayerCreate(gPlayer1.UID); + gPlayer1.Respawn(False, True); + g_Net_Slist_ServerPlayerComes(); end; - end; - if CustomStat.GameMode in [GM_TDM, GM_CTF] then + Exit; + end; + if gPlayer2 = nil 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 + if g_Game_IsClient then 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; + if NetPlrUID2 > -1 then + gPlayer2 := g_Player_Get(NetPlrUID2); + Exit; + end; - 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; + if not (Team in [TEAM_RED, TEAM_BLUE]) then + Team := gPlayer2Settings.Team; - _y := _y+16+16; + // Создание второго игрока: + gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, + gPlayer2Settings.Color, + Team, False)); + if gPlayer2 = nil then + g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2])) + else + begin + gPlayer2.Name := gPlayer2Settings.Name; + g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True); + if g_Game_IsServer and g_Game_IsNet then + MH_SEND_PlayerCreate(gPlayer2.UID); + gPlayer2.Respawn(False, True); + g_Net_Slist_ServerPlayerComes(); 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; -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, gWinSizeX, gWinSizeY); - 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: Integer; - //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(); - - px := p.GameX + PLAYER_RECT_CX; - py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft; - - 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; - - if p.IncCam <> 0 then - begin - if py > gMapInfo.Height-(gPlayerScreenSize.Y div 2) then - begin - if p.IncCam > 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))) then - begin - p.IncCam := 120-(py-(gMapInfo.Height-(gPlayerScreenSize.Y div 2))); - end; - end; - - if py < gPlayerScreenSize.Y div 2 then - begin - if p.IncCam < -120+((gPlayerScreenSize.Y div 2)-py) then - begin - p.IncCam := -120+((gPlayerScreenSize.Y div 2)-py); - end; - end; - - if p.IncCam < 0 then - begin - while (py+(gPlayerScreenSize.Y div 2)-p.IncCam > gMapInfo.Height) and (p.IncCam < 0) do p.IncCam := p.IncCam+1; //Inc(p.IncCam); - end; - - if p.IncCam > 0 then - begin - while (py-(gPlayerScreenSize.Y div 2)-p.IncCam < 0) and (p.IncCam > 0) do p.IncCam := p.IncCam-1; //Dec(p.IncCam); - end; - end; - - if (px < gPlayerScreenSize.X div 2) or (gMapInfo.Width-gPlayerScreenSize.X <= 256) then c := 0 - else if (px > gMapInfo.Width-(gPlayerScreenSize.X div 2)) then c := gBackSize.X-gPlayerScreenSize.X - else c := round((px-(gPlayerScreenSize.X div 2))/(gMapInfo.Width-gPlayerScreenSize.X)*(gBackSize.X-gPlayerScreenSize.X)); - - if (py-p.IncCam <= gPlayerScreenSize.Y div 2) or (gMapInfo.Height-gPlayerScreenSize.Y <= 256) then d := 0 - else if (py-p.IncCam >= gMapInfo.Height-(gPlayerScreenSize.Y div 2)) then d := gBackSize.Y-gPlayerScreenSize.Y - else d := round((py-p.IncCam-(gPlayerScreenSize.Y div 2))/(gMapInfo.Height-gPlayerScreenSize.Y)*(gBackSize.Y-gPlayerScreenSize.Y)); - - sX := -a; - sY := -(b+p.IncCam); - sWidth := gPlayerScreenSize.X; - sHeight := gPlayerScreenSize.Y; - - //glTranslatef(a, b+p.IncCam, 0); - - //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight); - - //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]); - fixViewportForScale(); - //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]); - - if (g_dbg_scale <> 1.0) and (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; - - 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)); - end; - - //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; - - if p.FSpectator then - e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4, - p.GameY + PLAYER_RECT_CY - 4, - 'X', gStdFont, 255, 255, 255, 1, True); - { - 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 := GetTimer() {div 1000}; - FPSCounter := FPSCounter+1; - if Time - FPSTime >= 1000 then - begin - FPS := FPSCounter; - FPSCounter := 0; - FPSTime := Time; - end; - - 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 = 1) 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} - g_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(); - -{$IFDEF ENABLE_HOLMES} - g_Holmes_DrawUI(); -{$ENDIF} - - g_Touch_Draw; -end; - -procedure g_Game_Quit(); -begin - g_Game_StopAllSounds(True); - gMusic.Free(); - g_Game_SaveOptions(); - 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; - PushExitEvent(); -end; - -procedure g_FatalError(Text: String); -begin - g_Console_Add(Format(_lc[I_FATAL_ERROR], [Text]), True); - e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning); - - gExit := EXIT_SIMPLE; -end; - -procedure g_SimpleError(Text: String); -begin - g_Console_Add(Format(_lc[I_SIMPLE_ERROR], [Text]), True); - e_WriteLog(Format(_lc[I_SIMPLE_ERROR], [Text]), TMsgType.Warning); -end; - -procedure g_Game_SetupScreenSize(); -const - RES_FACTOR = 4.0 / 3.0; -var - s: Single; - 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; - if (gScreenWidth*s > gMapInfo.Width) or - (gScreenHeight*s > gMapInfo.Height) then - begin - gBackSize.X := gScreenWidth; - gBackSize.Y := gScreenHeight; - end - else - begin - e_GetTextureSize(BackID, @bw, @bh); - rf := Single(bw) / Single(bh); - if (rf > RES_FACTOR) then bw := Round(Single(bh) * RES_FACTOR) - else if (rf < RES_FACTOR) then bh := Round(Single(bw) / RES_FACTOR); - s := Max(gScreenWidth / bw, gScreenHeight / bh); - if (s < 1.0) then s := 1.0; - gBackSize.X := Round(bw*s); - gBackSize.Y := Round(bh*s); - end; - end; -end; - -procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean); -begin - g_Window_SetSize(newWidth, newHeight, nowFull); -end; - -procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE); -begin - if ((not gGameOn) and (gState <> STATE_INTERCUSTOM)) - or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then - Exit; - if gPlayer1 = nil then - begin - if g_Game_IsClient then - begin - if NetPlrUID1 > -1 then - begin - MC_SEND_CheatRequest(NET_CHEAT_SPECTATE); - gPlayer1 := g_Player_Get(NetPlrUID1); - end; - Exit; - end; - - 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)); - if gPlayer1 = nil then - g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [1])) - else - begin - gPlayer1.Name := gPlayer1Settings.Name; - g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer1.Name]), True); - if g_Game_IsServer and g_Game_IsNet then - MH_SEND_PlayerCreate(gPlayer1.UID); - gPlayer1.Respawn(False, True); - - if g_Game_IsNet and NetUseMaster then - g_Net_Slist_Update; - end; - - Exit; - end; - if gPlayer2 = nil then - begin - if g_Game_IsClient then - begin - if NetPlrUID2 > -1 then - gPlayer2 := g_Player_Get(NetPlrUID2); - Exit; - end; - - 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)); - if gPlayer2 = nil then - g_FatalError(Format(_lc[I_GAME_ERROR_PLAYER_CREATE], [2])) - else - begin - gPlayer2.Name := gPlayer2Settings.Name; - g_Console_Add(Format(_lc[I_PLAYER_JOIN], [gPlayer2.Name]), True); - if g_Game_IsServer and g_Game_IsNet then - MH_SEND_PlayerCreate(gPlayer2.UID); - gPlayer2.Respawn(False, True); - - if g_Game_IsNet and NetUseMaster then - g_Net_Slist_Update; - end; - - Exit; - end; -end; + Exit; + end; +end; procedure g_Game_RemovePlayer(); var @@ -4253,11 +2712,13 @@ begin Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON); g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True); g_Player_Remove(Pl.UID); - - if g_Game_IsNet and NetUseMaster then - g_Net_Slist_Update; - end else + g_Net_Slist_ServerPlayerLeaves(); + end + else + begin + gSpectLatchPID2 := Pl.UID; gPlayer2 := nil; + end; Exit; end; Pl := gPlayer1; @@ -4269,16 +2730,16 @@ begin Pl.Kill(K_SIMPLEKILL, 0, HIT_DISCON); g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True); g_Player_Remove(Pl.UID); - - if g_Game_IsNet and NetUseMaster then - g_Net_Slist_Update; + g_Net_Slist_ServerPlayerLeaves(); end else begin + gSpectLatchPID1 := Pl.UID; gPlayer1 := nil; MC_SEND_CheatRequest(NET_CHEAT_SPECTATE); end; Exit; end; + g_Net_Slist_ServerPlayerLeaves(); end; procedure g_Game_Spectate(); @@ -4305,7 +2766,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: FillByte(gGameSettings, SizeOf(TGameSettings), 0); gAimLine := False; gShowMap := False; @@ -4314,14 +2775,18 @@ begin gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT; gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS; gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER; + gGameSettings.Options := gGameSettings.Options + GAME_OPTION_TEAMHITPROJECTILE; + gGameSettings.Options := gGameSettings.Options + GAME_OPTION_TEAMHITTRACE; gSwitchGameMode := GM_SINGLE; - g_Game_ExecuteEvent('ongamestart'); + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; -// Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: - g_Game_SetupScreenSize(); + g_Game_ExecuteEvent('ongamestart'); -// Ñîçäàíèå ïåðâîãî èãðîêà: +// Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, gPlayer1Settings.Team, False)); @@ -4334,7 +2799,7 @@ begin gPlayer1.Name := gPlayer1Settings.Name; nPl := 1; -// Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü: +// Создание второго игрока, если есть: if TwoPlayers then begin gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, @@ -4350,18 +2815,18 @@ begin Inc(nPl); end; -// Çàãðóçêà è çàïóñê êàðòû: - if not g_Game_StartMap(MAP, True) then +// Загрузка и запуск карты: + 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; g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps])); 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; @@ -4379,7 +2844,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: gGameSettings.GameType := GT_CUSTOM; gGameSettings.GameMode := GameMode; gSwitchGameMode := GameMode; @@ -4395,12 +2860,14 @@ begin gAimLine := False; gShowMap := False; - g_Game_ExecuteEvent('ongamestart'); + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; -// Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: - g_Game_SetupScreenSize(); + g_Game_ExecuteEvent('ongamestart'); -// Ðåæèì íàáëþäàòåëÿ: +// Режим наблюдателя: if nPlayers = 0 then begin gPlayer1 := nil; @@ -4410,7 +2877,7 @@ begin nPl := 0; if nPlayers >= 1 then begin - // Ñîçäàíèå ïåðâîãî èãðîêà: + // Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, gPlayer1Settings.Team, False)); @@ -4426,7 +2893,7 @@ begin if nPlayers >= 2 then begin - // Ñîçäàíèå âòîðîãî èãðîêà: + // Создание второго игрока: gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, gPlayer2Settings.Color, gPlayer2Settings.Team, False)); @@ -4440,14 +2907,14 @@ begin Inc(nPl); end; -// Çàãðóçêà è çàïóñê êàðòû: - if not g_Game_StartMap(Map, True) then +// Загрузка и запуск карты: + 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) + @@ -4458,10 +2925,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; @@ -4472,12 +2939,13 @@ procedure g_Game_StartServer(Map: String; GameMode: Byte; IPAddr: LongWord; Port: Word); begin g_Game_Free(); + g_Net_Slist_ServerClosed(); e_WriteLog('Starting net game (server)...', TMsgType.Notify); g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: gGameSettings.GameType := GT_SERVER; gGameSettings.GameMode := GameMode; gSwitchGameMode := GameMode; @@ -4493,12 +2961,14 @@ begin gAimLine := False; gShowMap := False; - g_Game_ExecuteEvent('ongamestart'); + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; -// Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà - g_Game_SetupScreenSize(); + g_Game_ExecuteEvent('ongamestart'); -// Ðåæèì íàáëþäàòåëÿ: +// Режим наблюдателя: if nPlayers = 0 then begin gPlayer1 := nil; @@ -4507,7 +2977,7 @@ begin if nPlayers >= 1 then begin - // Ñîçäàíèå ïåðâîãî èãðîêà: + // Создание первого игрока: gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, gPlayer1Settings.Team, False)); @@ -4522,7 +2992,7 @@ begin if nPlayers >= 2 then begin - // Ñîçäàíèå âòîðîãî èãðîêà: + // Создание второго игрока: gPlayer2 := g_Player_Get(g_Player_Create(gPlayer2Settings.Model, gPlayer2Settings.Color, gPlayer2Settings.Team, False)); @@ -4539,36 +3009,41 @@ 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] + _lc[I_NET_ERR_HOST]); + g_FatalError(_lc[I_NET_MSG] + Format(_lc[I_NET_ERR_HOST], [Port])); Exit; end; - g_Net_Slist_Set(NetSlistIP, NetSlistPort); + g_Net_Slist_Set(NetMasterList); + + g_Net_Slist_ServerStarted(); -// Çàãðóçêà è çàïóñê êàðòû: - if not g_Game_StartMap(Map, True) then +// Загрузка и запуск карты: + if not g_Game_StartMap(false{asMegawad}, Map, True) then begin + g_Net_Slist_ServerClosed(); 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) + g_Map_GetPointCount(RESPAWNPOINT_RED)+ g_Map_GetPointCount(RESPAWNPOINT_BLUE)) < 1 then begin + g_Net_Slist_ServerClosed(); g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]); Exit; end; -// Íàñòðîéêè èãðîêîâ è áîòîâ: +// Настройки игроков и ботов: g_Player_Init(); + g_Net_Slist_ServerMapStarted(); NetState := NET_STATE_GAME; end; @@ -4592,7 +3067,7 @@ begin g_Game_ClearLoading(); -// Íàñòðîéêè èãðû: +// Настройки игры: gGameSettings.GameType := GT_CLIENT; gCoopTotalMonstersKilled := 0; @@ -4604,13 +3079,19 @@ begin g_Game_ExecuteEvent('ongamestart'); -// Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: - g_Game_SetupScreenSize(); - NetState := NET_STATE_AUTH; g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False); -// Ñòàðòóåì êëèåíò + + // create (or update) map/resource databases + g_Res_CreateDatabases(true); + + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; + +// Стартуем клиент if not g_Net_Connect(Addr, Port) then begin g_FatalError(_lc[I_NET_MSG] + _lc[I_NET_ERR_CONN]); @@ -4625,13 +3106,25 @@ begin OuterLoop := True; while OuterLoop do begin - while (enet_host_service(NetHost, @NetEvent, 0) > 0) do + // fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html + // tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure. + // thank you, enet. let's ignore failures altogether then. + while (enet_host_service(NetHost, @NetEvent, 50) > 0) do begin if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then begin + if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then + begin + // ignore all download packets, they're processed by separate code + enet_packet_destroy(NetEvent.packet); + continue; + end; Ptr := NetEvent.packet^.data; if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then + begin + enet_packet_destroy(NetEvent.packet); continue; + end; InMsg.ReadLongWord(); // skip size MID := InMsg.ReadByte(); @@ -4654,11 +3147,11 @@ begin gGameSettings.Options := InMsg.ReadLongWord(); T := InMsg.ReadLongWord(); - newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash); - if newResPath = '' then + //newResPath := g_Res_SearchSameWAD(MapsDir, WadName, gWADHash); + //if newResPath = '' then begin - g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False); - newResPath := g_Res_DownloadWAD(WadName); + //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False); + newResPath := g_Res_DownloadMapWAD(ExtractFileName(WadName), gWADHash); if newResPath = '' then begin g_FatalError(_lc[I_NET_ERR_HASH]); @@ -4666,8 +3159,10 @@ begin NetState := NET_STATE_NONE; Exit; end; + e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath, WadName], TMsgType.Notify); end; - newResPath := ExtractRelativePath(MapsDir, newResPath); + //newResPath := ExtractRelativePath(MapsDir, newResPath); + gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model, gPlayer1Settings.Color, @@ -4686,7 +3181,7 @@ begin gPlayer1.UID := NetPlrUID1; gPlayer1.Reset(True); - if not g_Game_StartMap(newResPath + ':\' + Map, True) then + if not g_Game_StartMap(false{asMegawad}, newResPath + ':\' + Map, True) then begin g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map])); @@ -4721,8 +3216,7 @@ begin ProcessLoading(true); - if e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_ESCAPE) or e_KeyPressed(VK_ESCAPE) or - e_KeyPressed(JOY0_JUMP) or e_KeyPressed(JOY1_JUMP) or e_KeyPressed(JOY2_JUMP) or e_KeyPressed(JOY3_JUMP) then + if g_Net_UserRequestExit() then begin State := 0; break; @@ -4736,19 +3230,14 @@ begin Exit; end; - gLMSRespawn := LMS_RESPAWN_NONE; - gLMSRespawnTime := 0; - g_Player_Init(); NetState := NET_STATE_GAME; MC_SEND_FullStateRequest; e_WriteLog('NET: Connection successful.', TMsgType.Notify); end; -procedure g_Game_SaveOptions(); -begin - g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME); -end; +var + lastAsMegaWad: Boolean = false; procedure g_Game_ChangeMap(const MapPath: String); var @@ -4757,13 +3246,13 @@ begin g_Game_ClearLoading(); Force := gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]; - // Åñëè óðîâåíü çàâåðøèëñÿ ïî òðèããåðó Âûõîä, íå î÷èùàòü èíâåíòàðü + // Если уровень завершился по триггеру Выход, не очищать инвентарь if gExitByTrigger then begin Force := False; gExitByTrigger := False; end; - if not g_Game_StartMap(MapPath, Force) then + if not g_Game_StartMap(lastAsMegaWad, MapPath, Force) then g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [MapPath])); end; @@ -4774,17 +3263,19 @@ begin if g_Game_IsClient then Exit; map := g_ExtractFileName(gMapInfo.Map); + e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map, gCurrentMapFileName]); MessageTime := 0; gGameOn := False; g_Game_ClearLoading(); - g_Game_StartMap(Map, True, gCurrentMapFileName); + g_Game_StartMap(lastAsMegaWad, Map, True, gCurrentMapFileName); end; -function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean; +function g_Game_StartMap (asMegawad: Boolean; Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean; var NewWAD, ResName: String; I: Integer; + nws: AnsiString; begin g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName)); g_Player_RemoveAllCorpses(); @@ -4802,37 +3293,82 @@ begin g_Player_ResetTeams(); + lastAsMegaWad := asMegawad; if isWadPath(Map) then begin NewWAD := g_ExtractWadName(Map); ResName := g_ExtractFileName(Map); if g_Game_IsServer then begin - gWADHash := MD5File(MapsDir + NewWAD); - g_Game_LoadWAD(NewWAD); - end else + nws := findDiskWad(NewWAD); + //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']'); + if (asMegawad) then + begin + if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD); + if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD); + end + else + begin + if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD); + if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD); + end; + //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD); + //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']'); + //nws := NewWAD; + if (length(nws) = 0) then + begin + ResName := ''; // failed + end + else + begin + NewWAD := nws; + if (g_Game_IsNet) then gWADHash := MD5File(nws); + //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName); + g_Game_LoadWAD(NewWAD); + end; + end + else + begin // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART - g_Game_ClientWAD(NewWAD, gWADHash); - end else + NewWAD := g_Game_ClientWAD(NewWAD, gWADHash); + end; + end + else + begin + NewWAD := gGameSettings.WAD; ResName := Map; + end; - Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName); + gTime := 0; + + //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName); + result := false; + if (ResName <> '') and (NewWAD <> '') then + begin + //result := g_Map_Load(gGameSettings.WAD + ':\' + ResName); + result := g_Map_Load(NewWAD+':\'+ResName); + {$IFNDEF HEADLESS} + r_Render_LoadTextures; + {$ENDIF} + end; if Result then begin g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE); gState := STATE_NONE; - g_ActiveWindow := nil; + {$IFDEF ENABLE_MENU} + g_ActiveWindow := nil; + {$ENDIF} gGameOn := True; DisableCheats(); - ResetTimer(); + wNeedTimeReset := True; if gGameSettings.GameMode = GM_CTF then 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; @@ -4846,12 +3382,11 @@ begin gExit := 0; gPauseMain := false; gPauseHolmes := false; - gTime := 0; NetTimeToUpdate := 1; NetTimeToReliable := 0; NetTimeToMaster := NetMasterRate; - gLMSRespawn := LMS_RESPAWN_NONE; - gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; gMissionFailed := False; gNextMap := ''; @@ -4869,31 +3404,29 @@ begin g_Game_SpectateCenterView(); - if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then + if g_Game_IsServer then begin - gLMSRespawn := LMS_RESPAWN_WARMUP; - gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000; - gLMSSoftSpawn := True; - if NetMode = NET_SERVER then - MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000) + if (gGameSettings.MaxLives > 0) and (gGameSettings.WarmupTime > 0) then + begin + gLMSRespawn := LMS_RESPAWN_WARMUP; + gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000; + gLMSSoftSpawn := True; + if g_Game_IsNet then + MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime); + end else - g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [(gLMSRespawnTime - gTime) div 1000]), True); + begin + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + end; end; if NetMode = NET_SERVER then begin MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map); - // Ìàñòåðñåðâåð - if NetUseMaster then - begin - if (NetMHost = nil) or (NetMPeer = nil) then - begin - // let the connection be blocking here, why not? - g_Net_Slist_Connect(); - end; - g_Net_Slist_Update; - end; + // Мастерсервер + g_Net_Slist_ServerMapStarted(); if NetClients <> nil then for I := 0 to High(NetClients) do @@ -4922,11 +3455,11 @@ begin g_Game_ExecuteEvent('onmapstart'); end; -procedure SetFirstLevel(); +procedure SetFirstLevel; begin gNextMap := ''; - MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD); + MapList := g_Map_GetMapsList(gGameSettings.WAD); if MapList = nil then Exit; @@ -4945,16 +3478,16 @@ 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 g_Player_RememberAll; - if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then + if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then begin gLastMap := True; if gGameSettings.GameMode = GM_COOP then @@ -4963,7 +3496,7 @@ begin gStatsPressed := True; gNextMap := 'MAP01'; - if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then + if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then g_Game_NextLevel; if g_Game_IsNet then @@ -4989,19 +3522,36 @@ begin gNextMap := Map; end; -procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest); +function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString; var - gWAD: String; + gWAD{, xwad}: String; begin - if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then - Exit; - if not g_Game_IsClient then + result := NewWAD; + if not g_Game_IsClient then Exit; + //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]); + + gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash); + if gWAD = '' then + begin + result := ''; + g_Game_Free(); + g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [ExtractFileName(NewWAD)])); Exit; + end; + + e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD, NewWAD], TMsgType.Notify); + NewWAD := gWAD; + + g_Game_LoadWAD(NewWAD); + result := NewWAD; + + { + if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit; gWAD := g_Res_SearchSameWAD(MapsDir, ExtractFileName(NewWAD), WHash); if gWAD = '' then begin g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False); - gWAD := g_Res_DownloadWAD(ExtractFileName(NewWAD)); + gWAD := g_Res_DownloadMapWAD(ExtractFileName(NewWAD), WHash); if gWAD = '' then begin g_Game_Free(); @@ -5011,18 +3561,12 @@ begin end; NewWAD := ExtractRelativePath(MapsDir, gWAD); g_Game_LoadWAD(NewWAD); + } end; procedure g_Game_RestartRound(NoMapRestart: Boolean = False); var i, n, nb, nr: Integer; - - function monRespawn (mon: TMonster): Boolean; - begin - result := false; // don't stop - if not mon.FNoRespawn then mon.Respawn(); - end; - begin if not g_Game_IsServer then Exit; if gLMSRespawn = LMS_RESPAWN_NONE then Exit; @@ -5048,12 +3592,14 @@ begin else if gPlayers[i].Team = TEAM_BLUE then Inc(nb) end; - if (n < 2) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then + if (n < 1) or ((gGameSettings.GameMode = GM_TDM) and ((nr = 0) or (nb = 0))) then begin // wait a second until the fuckers finally decide to join gLMSRespawn := LMS_RESPAWN_WARMUP; - gLMSRespawnTime := gTime + 1000; + gLMSRespawnTime := gTime + gGameSettings.WarmupTime*1000; gLMSSoftSpawn := NoMapRestart; + if g_Game_IsNet then + MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime); Exit; end; @@ -5083,17 +3629,14 @@ begin gPlayers[i].Frags := 0; gPlayers[i].RecallState; end; - if (gPlayer1 = nil) and (gLMSPID1 > 0) then - gPlayer1 := g_Player_Get(gLMSPID1); - if (gPlayer2 = nil) and (gLMSPID2 > 0) then - gPlayer2 := g_Player_Get(gLMSPID2); + if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) then + gPlayer1 := g_Player_Get(gSpectLatchPID1); + if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) then + gPlayer2 := g_Player_Get(gSpectLatchPID2); end; g_Items_RestartRound(); - - g_Mons_ForEach(monRespawn); - gLMSSoftSpawn := False; end; @@ -5121,7 +3664,7 @@ var begin Result := ''; - MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD); + MapList := g_Map_GetMapsList(gGameSettings.WAD); if MapList = nil then Exit; @@ -5143,7 +3686,7 @@ begin else Result := MapList[MapIndex + 1]; - if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map; + if not g_Map_Exist(gGameSettings.WAD + ':\' + Result) then Result := Map; end; MapList := nil; @@ -5183,7 +3726,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); @@ -5191,7 +3734,7 @@ begin //CopyMemory(@MapName[0], @gMapToDelete[1], Min(16, Length(gMapToDelete))); { -// Èìÿ êàðòû íå ñòàíäàðòíîå òåñòîâîå: +// Имя карты не стандартное тестовое: if MapName <> TEST_MAP_NAME then Exit; @@ -5200,14 +3743,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(''); @@ -5215,7 +3758,7 @@ begin for a := 0 to High(MapList) do if MapList[a] = MapName then begin - // Óäàëÿåì è ñîõðàíÿåì: + // Удаляем и сохраняем: WAD.RemoveResource('', MapName); WAD.SaveTo(WadName); Break; @@ -5232,66 +3775,62 @@ procedure GameCVars(P: SSArray); var a, b: Integer; stat: TPlayerStatArray; - cmd, s: string; - config: TConfig; -begin - stat := nil; - cmd := LowerCase(P[0]); - if (cmd = 'g_friendlyfire') and not g_Game_IsClient then + cmd: string; + + procedure ParseGameFlag(Flag: LongWord; OffMsg, OnMsg: TStrings_Locale; OnMapChange: Boolean = False); + var + x: Boolean; begin - with gGameSettings do + if Length(P) > 1 then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - begin - if (P[1][1] = '1') then - Options := Options or GAME_OPTION_TEAMDAMAGE - else - Options := Options and (not GAME_OPTION_TEAMDAMAGE); - end; + x := P[1] = '1'; - if (LongBool(Options and GAME_OPTION_TEAMDAMAGE)) then - g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_ON]) + if x then + gsGameFlags := gsGameFlags or Flag else - g_Console_Add(_lc[I_MSG_FRIENDLY_FIRE_OFF]); + gsGameFlags := gsGameFlags and (not Flag); - if g_Game_IsNet then MH_SEND_GameSettings; - end; - end - else if (cmd = 'g_weaponstay') and not g_Game_IsClient then - begin - with gGameSettings do - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then + if g_Game_IsServer then begin - if (P[1][1] = '1') then - Options := Options or GAME_OPTION_WEAPONSTAY + if x then + gGameSettings.Options := gGameSettings.Options or Flag else - Options := Options and (not GAME_OPTION_WEAPONSTAY); + gGameSettings.Options := gGameSettings.Options and (not Flag); + if g_Game_IsNet then MH_SEND_GameSettings; end; + end; - if (LongBool(Options and GAME_OPTION_WEAPONSTAY)) then - g_Console_Add(_lc[I_MSG_WEAPONSTAY_ON]) - else - g_Console_Add(_lc[I_MSG_WEAPONSTAY_OFF]); + if LongBool(gsGameFlags and Flag) then + g_Console_Add(_lc[OnMsg]) + else + g_Console_Add(_lc[OffMsg]); - if g_Game_IsNet then MH_SEND_GameSettings; - end; - end - else if cmd = 'g_gamemode' then + if OnMapChange and g_Game_IsServer then + g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); + end; + +begin + stat := nil; + cmd := LowerCase(P[0]); + + if cmd = 'g_gamemode' then begin - a := g_Game_TextToMode(P[1]); - if a = GM_SINGLE then a := GM_COOP; - if (Length(P) > 1) and (a <> GM_NONE) and (not g_Game_IsClient) then + if (Length(P) > 1) then begin - gSwitchGameMode := a; - if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or - (gState = STATE_INTERSINGLE) then - gSwitchGameMode := GM_SINGLE; - if not gGameOn then - gGameSettings.GameMode := gSwitchGameMode; + a := g_Game_TextToMode(P[1]); + if a = GM_SINGLE then a := GM_COOP; + gsGameMode := g_Game_ModeToText(a); + if g_Game_IsServer then + begin + gSwitchGameMode := a; + if (gGameOn and (gGameSettings.GameMode = GM_SINGLE)) or + (gState = STATE_INTERSINGLE) then + gSwitchGameMode := GM_SINGLE; + if not gGameOn then + gGameSettings.GameMode := gSwitchGameMode; + end; end; + if gSwitchGameMode = gGameSettings.GameMode then g_Console_Add(Format(_lc[I_MSG_GAMEMODE_CURRENT], [g_Game_ModeToText(gGameSettings.GameMode)])) @@ -5300,440 +3839,390 @@ begin [g_Game_ModeToText(gGameSettings.GameMode), g_Game_ModeToText(gSwitchGameMode)])); end - else if (cmd = 'g_allow_exit') and not g_Game_IsClient then + else if cmd = 'g_friendlyfire' then begin - with gGameSettings do - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - begin - if (P[1][1] = '1') then - Options := Options or GAME_OPTION_ALLOWEXIT - else - Options := Options and (not GAME_OPTION_ALLOWEXIT); - end; - - if (LongBool(Options and GAME_OPTION_ALLOWEXIT)) then - g_Console_Add(_lc[I_MSG_ALLOWEXIT_ON]) - else - g_Console_Add(_lc[I_MSG_ALLOWEXIT_OFF]); - g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); - - if g_Game_IsNet then MH_SEND_GameSettings; - end; + ParseGameFlag(GAME_OPTION_TEAMDAMAGE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON); + end + else if cmd = 'g_friendly_absorb_damage' then + begin + ParseGameFlag(GAME_OPTION_TEAMABSORBDAMAGE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON); + end + else if cmd = 'g_friendly_hit_trace' then + begin + ParseGameFlag(GAME_OPTION_TEAMHITTRACE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON); + end + else if cmd = 'g_friendly_hit_projectile' then + begin + ParseGameFlag(GAME_OPTION_TEAMHITPROJECTILE, I_MSG_FRIENDLY_FIRE_OFF, I_MSG_FRIENDLY_FIRE_ON); + end + else if cmd = 'g_weaponstay' then + begin + ParseGameFlag(GAME_OPTION_WEAPONSTAY, I_MSG_WEAPONSTAY_OFF, I_MSG_WEAPONSTAY_ON); + end + else if cmd = 'g_allow_exit' then + begin + ParseGameFlag(GAME_OPTION_ALLOWEXIT, I_MSG_ALLOWEXIT_OFF, I_MSG_ALLOWEXIT_ON, True); + end + else if cmd = 'g_allow_monsters' then + begin + ParseGameFlag(GAME_OPTION_MONSTERS, I_MSG_ALLOWMON_OFF, I_MSG_ALLOWMON_ON, True); + end + else if cmd = 'g_bot_vsplayers' then + begin + ParseGameFlag(GAME_OPTION_BOTVSPLAYER, I_MSG_BOTSVSPLAYERS_OFF, I_MSG_BOTSVSPLAYERS_ON); + end + else if cmd = 'g_bot_vsmonsters' then + begin + ParseGameFlag(GAME_OPTION_BOTVSMONSTER, I_MSG_BOTSVSMONSTERS_OFF, I_MSG_BOTSVSMONSTERS_ON); + end + else if cmd = 'g_dm_keys' then + begin + ParseGameFlag(GAME_OPTION_DMKEYS, I_MSG_DMKEYS_OFF, I_MSG_DMKEYS_ON, True); end - else if (cmd = 'g_allow_monsters') and not g_Game_IsClient then + else if cmd = 'g_gameflags' then begin - with gGameSettings do + if Length(P) > 1 then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then + gsGameFlags := StrToDWordDef(P[1], gsGameFlags); + if g_Game_IsServer then begin - if (P[1][1] = '1') then - Options := Options or GAME_OPTION_MONSTERS - else - Options := Options and (not GAME_OPTION_MONSTERS); + gGameSettings.Options := gsGameFlags; + if g_Game_IsNet then MH_SEND_GameSettings; end; - - if (LongBool(Options and GAME_OPTION_MONSTERS)) then - g_Console_Add(_lc[I_MSG_ALLOWMON_ON]) - else - g_Console_Add(_lc[I_MSG_ALLOWMON_OFF]); - g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); - - if g_Game_IsNet then MH_SEND_GameSettings; end; + + g_Console_Add(Format('%s %u', [cmd, gsGameFlags])); end - else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then + else if cmd = 'g_warmup_time' then begin - with gGameSettings do + if Length(P) > 1 then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then + gsWarmupTime := nclamp(StrToIntDef(P[1], gsWarmupTime), 0, $FFFF); + if g_Game_IsServer then begin - if (P[1][1] = '1') then - Options := Options or GAME_OPTION_BOTVSPLAYER - else - Options := Options and (not GAME_OPTION_BOTVSPLAYER); + gGameSettings.WarmupTime := gsWarmupTime; + // extend warmup if it's already going + if gLMSRespawn = LMS_RESPAWN_WARMUP then + begin + gLMSRespawnTime := gTime + gsWarmupTime * 1000; + if g_Game_IsNet then MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime); + end; + if g_Game_IsNet then MH_SEND_GameSettings; end; - - if (LongBool(Options and GAME_OPTION_BOTVSPLAYER)) then - g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_ON]) - else - g_Console_Add(_lc[I_MSG_BOTSVSPLAYERS_OFF]); - - if g_Game_IsNet then MH_SEND_GameSettings; end; + + g_Console_Add(Format(_lc[I_MSG_WARMUP], [Integer(gsWarmupTime)])); + if g_Game_IsServer then g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); end - else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then + else if cmd = 'g_spawn_invul' then begin - with gGameSettings do + if Length(P) > 1 then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then + gsSpawnInvul := nclamp(StrToIntDef(P[1], gsSpawnInvul), 0, $FFFF); + if g_Game_IsServer then begin - if (P[1][1] = '1') then - Options := Options or GAME_OPTION_BOTVSMONSTER - else - Options := Options and (not GAME_OPTION_BOTVSMONSTER); + gGameSettings.SpawnInvul := gsSpawnInvul; + if g_Game_IsNet then MH_SEND_GameSettings; end; - - if (LongBool(Options and GAME_OPTION_BOTVSMONSTER)) then - g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_ON]) - else - g_Console_Add(_lc[I_MSG_BOTSVSMONSTERS_OFF]); - - if g_Game_IsNet then MH_SEND_GameSettings; end; + + g_Console_Add(Format('%s %d', [cmd, Integer(gsSpawnInvul)])); end - else if (cmd = 'g_warmuptime') and not g_Game_IsClient then + else if cmd = 'g_item_respawn_time' then begin if Length(P) > 1 then begin - if StrToIntDef(P[1], gGameSettings.WarmupTime) = 0 then - gGameSettings.WarmupTime := 30 - else - gGameSettings.WarmupTime := StrToIntDef(P[1], gGameSettings.WarmupTime); + gsItemRespawnTime := nclamp(StrToIntDef(P[1], gsItemRespawnTime), 0, $FFFF); + if g_Game_IsServer then + begin + gGameSettings.ItemRespawnTime := gsItemRespawnTime; + if g_Game_IsNet then MH_SEND_GameSettings; + end; end; - g_Console_Add(Format(_lc[I_MSG_WARMUP], - [gGameSettings.WarmupTime])); - g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); + g_Console_Add(Format('%s %d', [cmd, Integer(gsItemRespawnTime)])); + if g_Game_IsServer then g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); end - else if cmd = 'net_interp' then + else if cmd = 'sv_intertime' then begin if (Length(P) > 1) then - NetInterpLevel := StrToIntDef(P[1], NetInterpLevel); + gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120); - g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel)); - config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME); - config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel); - config.SaveFile(GameDir+'/'+CONFIG_FILENAME); - config.Free(); + g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime)); end - else if cmd = 'net_forceplayerupdate' then + else if cmd = 'g_max_particles' then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - NetForcePlayerUpdate := (P[1][1] = '1'); - - if NetForcePlayerUpdate then - g_Console_Add('net_forceplayerupdate = 1') + if Length(p) = 2 then + begin + {$IFDEF ENABLE_GFX} + a := Max(0, StrToInt(p[1])); + g_GFX_SetMax(a) + {$ENDIF} + end + else if Length(p) = 1 then + begin + {$IFDEF ENABLE_GFX} + e_LogWritefln('%s', [g_GFX_GetMax()]) + {$ELSE} + e_LogWritefln('%s', [0]) + {$ENDIF} + end else - g_Console_Add('net_forceplayerupdate = 0'); - config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME); - config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate); - config.SaveFile(GameDir+'/'+CONFIG_FILENAME); - config.Free(); + begin + e_LogWritefln('usage: %s ', [cmd]) + end end - else if cmd = 'net_predictself' then + else if cmd = 'g_max_shells' then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - NetPredictSelf := (P[1][1] = '1'); - - if NetPredictSelf then - g_Console_Add('net_predictself = 1') + if Length(p) = 2 then + begin + a := Max(0, StrToInt(p[1])); + g_Shells_SetMax(a) + end + else if Length(p) = 1 then + begin + e_LogWritefln('%s', [g_Shells_GetMax()]) + end else - g_Console_Add('net_predictself = 0'); - config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME); - config.WriteBool('Client', 'PredictSelf', NetPredictSelf); - config.SaveFile(GameDir+'/'+CONFIG_FILENAME); - config.Free(); + begin + e_LogWritefln('usage: %s ', [cmd]) + end end - else if cmd = 'sv_name' then + else if cmd = 'g_max_gibs' then begin - if (Length(P) > 1) and (Length(P[1]) > 0) then + if Length(p) = 2 then begin - NetServerName := P[1]; - if Length(NetServerName) > 64 then - SetLength(NetServerName, 64); - if g_Game_IsServer and g_Game_IsNet and NetUseMaster then - g_Net_Slist_Update; - end; - - g_Console_Add(cmd + ' = "' + NetServerName + '"'); + {$IFDEF ENABLE_GIBS} + a := Max(0, StrToInt(p[1])); + g_Gibs_SetMax(a) + {$ENDIF} + end + else if Length(p) = 1 then + begin + {$IFDEF ENABLE_GIBS} + e_LogWritefln('%s', [g_Gibs_GetMax()]) + {$ELSE} + e_LogWritefln('%s', [0]) + {$ENDIF} + end + else + begin + e_LogWritefln('usage: %s ', [cmd]) + end end - else if cmd = 'sv_passwd' then + else if cmd = 'g_max_corpses' then begin - if (Length(P) > 1) and (Length(P[1]) > 0) then + if Length(p) = 2 then begin - NetPassword := P[1]; - if Length(NetPassword) > 24 then - SetLength(NetPassword, 24); - if g_Game_IsServer and g_Game_IsNet and NetUseMaster then - g_Net_Slist_Update; - end; - - g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"'); + a := Max(0, StrToInt(p[1])); + g_Corpses_SetMax(a) + end + else if Length(p) = 1 then + begin + e_LogWritefln('%s', [g_Corpses_GetMax()]) + end + else + begin + e_LogWritefln('usage: %s ', [cmd]) + end end - else if cmd = 'sv_maxplrs' then + else if cmd = 'g_scorelimit' then begin - if (Length(P) > 1) then + if Length(P) > 1 then begin - NetMaxClients := Min(Max(StrToIntDef(P[1], NetMaxClients), 1), NET_MAXCLIENTS); - if g_Game_IsServer and g_Game_IsNet then + gsGoalLimit := nclamp(StrToIntDef(P[1], gsGoalLimit), 0, $FFFF); + + if g_Game_IsServer then begin b := 0; - for a := 0 to High(NetClients) do - if NetClients[a].Used then - begin - Inc(b); - if b > NetMaxClients then - begin - s := g_Player_Get(NetClients[a].Player).Name; - enet_peer_disconnect(NetClients[a].Peer, NET_DISC_FULL); - g_Console_Add(Format(_lc[I_PLAYER_KICK], [s])); - MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s); - end; - end; - if NetUseMaster then - g_Net_Slist_Update; - end; - end; - - g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients)); - end - else if cmd = 'sv_public' then - begin - if (Length(P) > 1) then - begin - NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0; - if g_Game_IsServer and g_Game_IsNet then - if NetUseMaster then - begin - if NetMPeer = nil then g_Net_Slist_Connect(); - g_Net_Slist_Update(); + if gGameSettings.GameMode = GM_DM then + begin // DM + stat := g_Player_GetStats(); + if stat <> nil then + for a := 0 to High(stat) do + if stat[a].Frags > b then + b := stat[a].Frags; end - else - if NetMPeer <> nil then - g_Net_Slist_Disconnect(); - end; + else // TDM/CTF + b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals); - g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster))); - end - else if cmd = 'sv_intertime' then - begin - if (Length(P) > 1) then - gDefInterTime := Min(Max(StrToIntDef(P[1], gDefInterTime), -1), 120); + // if someone has a higher score, set it to that instead + gsGoalLimit := max(gsGoalLimit, b); + gGameSettings.GoalLimit := gsGoalLimit; + + if g_Game_IsNet then MH_SEND_GameSettings; + end; + end; - g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime)); + g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [Integer(gsGoalLimit)])); end - else if cmd = 'p1_name' then + else if cmd = 'g_timelimit' then begin - if (Length(P) > 1) and gGameOn then + if Length(P) > 1 then begin - if g_Game_IsClient then + gsTimeLimit := nclamp(StrToIntDef(P[1], gsTimeLimit), 0, $FFFF); + if g_Game_IsServer then begin - gPlayer1Settings.Name := b_Text_Unformat(P[1]); - MC_SEND_PlayerSettings; - end - else - if gPlayer1 <> nil then - begin - gPlayer1.Name := b_Text_Unformat(P[1]); - if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); - end - else - gPlayer1Settings.Name := b_Text_Unformat(P[1]); + gGameSettings.TimeLimit := gsTimeLimit; + if g_Game_IsNet then MH_SEND_GameSettings; + end; end; + g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT], + [gsTimeLimit div 3600, + (gsTimeLimit div 60) mod 60, + gsTimeLimit mod 60])); end - else if cmd = 'p2_name' then + else if cmd = 'g_maxlives' then begin - if (Length(P) > 1) and gGameOn then + if Length(P) > 1 then begin - if g_Game_IsClient then + gsMaxLives := nclamp(StrToIntDef(P[1], gsMaxLives), 0, $FFFF); + if g_Game_IsServer then begin - gPlayer2Settings.Name := b_Text_Unformat(P[1]); - MC_SEND_PlayerSettings; - end - else - if gPlayer2 <> nil then - begin - gPlayer2.Name := b_Text_Unformat(P[1]); - if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); - end - else - gPlayer2Settings.Name := b_Text_Unformat(P[1]); + gGameSettings.MaxLives := gsMaxLives; + if g_Game_IsNet then MH_SEND_GameSettings; + end; end; - end - else if cmd = 'p1_color' then + + g_Console_Add(Format(_lc[I_MSG_LIVES], [Integer(gsMaxLives)])); + end; +end; + +procedure PlayerSettingsCVars(P: SSArray); +var + cmd: string; + team: Byte; + + function ParseTeam(s: string): Byte; begin - if Length(P) > 3 then - if g_Game_IsClient then + result := 0; + case s of + 'red', '1': result := TEAM_RED; + 'blue', '2': result := TEAM_BLUE; + else result := TEAM_NONE; + end; + end; +begin + cmd := LowerCase(P[0]); + case cmd of + 'p1_name': begin - gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255), - EnsureRange(StrToIntDef(P[2], 0), 0, 255), - EnsureRange(StrToIntDef(P[3], 0), 0, 255)); - MC_SEND_PlayerSettings; - end - else - if gPlayer1 <> nil then + if (Length(P) > 1) then + begin + gPlayer1Settings.Name := b_Text_Unformat(P[1]); + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer1 <> nil) then + begin + gPlayer1.Name := b_Text_Unformat(P[1]); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; + end; + end; + 'p2_name': + begin + if (Length(P) > 1) then + begin + gPlayer2Settings.Name := b_Text_Unformat(P[1]); + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer2 <> nil) then + begin + gPlayer2.Name := b_Text_Unformat(P[1]); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); + end; + end; + end; + 'p1_color': + begin + if Length(P) > 3 then begin - gPlayer1.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255), - EnsureRange(StrToIntDef(P[2], 0), 0, 255), - EnsureRange(StrToIntDef(P[3], 0), 0, 255)); - if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); - end - else gPlayer1Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255), EnsureRange(StrToIntDef(P[2], 0), 0, 255), EnsureRange(StrToIntDef(P[3], 0), 0, 255)); - end - else if (cmd = 'p2_color') and not g_Game_IsNet then - begin - if Length(P) > 3 then - if g_Game_IsClient then + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer1 <> nil) then + begin + gPlayer1.SetColor(gPlayer1Settings.Color); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; + end; + end; + 'p2_color': begin - gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255), - EnsureRange(StrToIntDef(P[2], 0), 0, 255), - EnsureRange(StrToIntDef(P[3], 0), 0, 255)); - MC_SEND_PlayerSettings; - end - else - if gPlayer2 <> nil then + if Length(P) > 3 then begin - gPlayer2.Model.SetColor(EnsureRange(StrToIntDef(P[1], 0), 0, 255), - EnsureRange(StrToIntDef(P[2], 0), 0, 255), - EnsureRange(StrToIntDef(P[3], 0), 0, 255)); - if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); - end - else gPlayer2Settings.Color := _RGB(EnsureRange(StrToIntDef(P[1], 0), 0, 255), EnsureRange(StrToIntDef(P[2], 0), 0, 255), EnsureRange(StrToIntDef(P[3], 0), 0, 255)); - end - else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then - begin - if cmd = 'r_showscore' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowGoals := (P[1][1] = '1'); - - if gShowGoals then - g_Console_Add(_lc[I_MSG_SCORE_ON]) - else - g_Console_Add(_lc[I_MSG_SCORE_OFF]); - end - else if cmd = 'r_showstat' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowStat := (P[1][1] = '1'); - - if gShowStat then - g_Console_Add(_lc[I_MSG_STATS_ON]) - else - g_Console_Add(_lc[I_MSG_STATS_OFF]); - end - else if cmd = 'r_showkillmsg' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowKillMsg := (P[1][1] = '1'); - - if gShowKillMsg then - g_Console_Add(_lc[I_MSG_KILL_MSGS_ON]) - else - g_Console_Add(_lc[I_MSG_KILL_MSGS_OFF]); - end - else if cmd = 'r_showlives' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowLives := (P[1][1] = '1'); - - if gShowLives then - g_Console_Add(_lc[I_MSG_LIVES_ON]) - else - g_Console_Add(_lc[I_MSG_LIVES_OFF]); - end - else if cmd = 'r_showspect' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gSpectHUD := (P[1][1] = '1'); - - if gSpectHUD then - g_Console_Add(_lc[I_MSG_SPECT_HUD_ON]) - else - g_Console_Add(_lc[I_MSG_SPECT_HUD_OFF]); - end - else if cmd = 'r_showping' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowPing := (P[1][1] = '1'); - - if gShowPing then - g_Console_Add(_lc[I_MSG_PING_ON]) - else - g_Console_Add(_lc[I_MSG_PING_OFF]); - end - else if (cmd = 'g_scorelimit') and not g_Game_IsClient then - begin - if Length(P) > 1 then + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer2 <> nil) then + begin + gPlayer2.SetColor(gPlayer2Settings.Color); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); + end; + end; + end; + 'p1_model': begin - if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then - gGameSettings.GoalLimit := 0 - else + if (Length(P) > 1) then + begin + gPlayer1Settings.Model := P[1]; + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer1 <> nil) then begin - b := 0; - - if gGameSettings.GameMode = GM_DM then - begin // DM - stat := g_Player_GetStats(); - if stat <> nil then - for a := 0 to High(stat) do - if stat[a].Frags > b then - b := stat[a].Frags; - end - else // TDM/CTF - b := Max(gTeamStat[TEAM_RED].Goals, gTeamStat[TEAM_BLUE].Goals); - - gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b); + gPlayer1.FActualModelName := gPlayer1Settings.Model; + gPlayer1.SetModel(gPlayer1Settings.Model); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); end; - - if g_Game_IsNet then MH_SEND_GameSettings; + end; end; - - g_Console_Add(Format(_lc[I_MSG_SCORE_LIMIT], [gGameSettings.GoalLimit])); - end - else if (cmd = 'g_timelimit') and not g_Game_IsClient then - begin - if (Length(P) > 1) and (StrToIntDef(P[1], -1) >= 0) then - gGameSettings.TimeLimit := StrToIntDef(P[1], -1); - - g_Console_Add(Format(_lc[I_MSG_TIME_LIMIT], - [gGameSettings.TimeLimit div 3600, - (gGameSettings.TimeLimit div 60) mod 60, - gGameSettings.TimeLimit mod 60])); - if g_Game_IsNet then MH_SEND_GameSettings; - end - else if (cmd = 'g_maxlives') and not g_Game_IsClient then - begin - if Length(P) > 1 then + 'p2_model': begin - if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then - gGameSettings.MaxLives := 0 - else + if (Length(P) > 1) then begin - b := 0; - stat := g_Player_GetStats(); - if stat <> nil then - for a := 0 to High(stat) do - if stat[a].Lives > b then - b := stat[a].Lives; - gGameSettings.MaxLives := - Max(StrToIntDef(P[1], gGameSettings.MaxLives), b); + gPlayer2Settings.Model := P[1]; + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer2 <> nil) then + begin + gPlayer2.FActualModelName := gPlayer2Settings.Model; + gPlayer2.SetModel(gPlayer2Settings.Model); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); + end; + end; + end; + 'p1_team': + begin + // TODO: switch teams if in game or store this separately + if (Length(P) > 1) then + begin + team := ParseTeam(P[1]); + if team = TEAM_NONE then + g_Console_Add('expected ''red'', ''blue'', 1 or 2') + else if not gGameOn and not g_Game_IsNet then + gPlayer1Settings.Team := team + else + g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); + end; + end; + 'p2_team': + begin + // TODO: switch teams if in game or store this separately + if (Length(P) > 1) then + begin + team := ParseTeam(P[1]); + if team = TEAM_NONE then + g_Console_Add('expected ''red'', ''blue'', 1 or 2') + else if not gGameOn and not g_Game_IsNet then + gPlayer2Settings.Team := team + else + g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); end; end; - - g_Console_Add(Format(_lc[I_MSG_LIVES], - [gGameSettings.MaxLives])); - if g_Game_IsNet then MH_SEND_GameSettings; - end; end; end; @@ -5758,17 +4247,14 @@ var //pt: TDFPoint; mon: TMonster; begin -// Êîìàíäû îòëàäî÷íîãî ðåæèìà: +// Команды отладочного режима: if {gDebugMode}conIsCheatsEnabled then begin cmd := LowerCase(P[0]); if cmd = 'd_window' then begin - g_Console_Add(Format('gWinPosX = %d, gWinPosY %d', [gWinPosX, gWinPosY])); - g_Console_Add(Format('gWinRealPosX = %d, gWinRealPosY %d', [gWinRealPosX, gWinRealPosY])); g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight])); - g_Console_Add(Format('gWinSizeX = %d, gWinSizeY = %d', [gWinSizeX, gWinSizeY])); - g_Console_Add(Format('Frame X = %d, Y = %d, Caption Y = %d', [gWinFrameX, gWinFrameY, gWinCaption])); + g_Console_Add(Format('gScreenWidth = %d, gScreenHeight = %d', [gScreenWidth, gScreenHeight])); end else if cmd = 'd_sounds' then begin @@ -6108,21 +4594,19 @@ var prt: Word; nm: Boolean; listen: LongWord; + found: Boolean; begin -// Îáùèå êîìàíäû: +// Общие команды: cmd := LowerCase(P[0]); chstr := ''; - if (cmd = 'quit') or - (cmd = 'exit') then + if cmd = 'pause' then begin - g_Game_Free(); - g_Game_Quit(); - Exit; - end - else if cmd = 'pause' then - begin - if (g_ActiveWindow = nil) then + {$IFDEF ENABLE_MENU} + if (g_ActiveWindow = nil) then + g_Game_Pause(not gPauseMain); + {$ELSE} g_Game_Pause(not gPauseMain); + {$ENDIF} end else if cmd = 'endgame' then gExit := EXIT_SIMPLE @@ -6162,23 +4646,21 @@ begin enet_peer_disconnect(pl^.Peer, NET_DISC_KICK); g_Console_Add(Format(_lc[I_PLAYER_KICK], [s])); MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s); - if NetUseMaster then - g_Net_Slist_Update; + g_Net_Slist_ServerPlayerLeaves(); end else if gPlayers <> nil then for a := Low(gPlayers) to High(gPlayers) do 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; gPlayers[a].Kill(K_SIMPLEKILL, 0, HIT_DISCON); g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [gPlayers[a].Name]), True); g_Player_Remove(gPlayers[a].UID); - if NetUseMaster then - g_Net_Slist_Update; - // Åñëè íå ïåðåìåøàòü, ïðè äîáàâëåíèè íîâûõ áîòîâ ïîÿâÿòñÿ ñòàðûå + g_Net_Slist_ServerPlayerLeaves(); + // Если не перемешать, при добавлении новых ботов появятся старые g_Bot_MixNames(); end; end else @@ -6208,25 +4690,139 @@ begin enet_peer_disconnect(NetClients[a].Peer, NET_DISC_KICK); g_Console_Add(Format(_lc[I_PLAYER_KICK], [s])); MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s); - if NetUseMaster then - g_Net_Slist_Update; + g_Net_Slist_ServerPlayerLeaves(); + end; + end; + end else + g_Console_Add(_lc[I_MSG_SERVERONLY]); + end + else if cmd = 'kick_pid' then + begin + if g_Game_IsServer and g_Game_IsNet then + begin + if Length(P) < 2 then + begin + g_Console_Add('kick_pid '); + Exit; + end; + if P[1] = '' then + begin + g_Console_Add('kick_pid '); + Exit; + end; + + a := StrToIntDef(P[1], 0); + pl := g_Net_Client_ByPlayer(a); + if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then + begin + s := g_Net_ClientName_ByID(pl^.ID); + enet_peer_disconnect(pl^.Peer, NET_DISC_KICK); + g_Console_Add(Format(_lc[I_PLAYER_KICK], [s])); + MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s); + g_Net_Slist_ServerPlayerLeaves(); + end; + end else + g_Console_Add(_lc[I_MSG_SERVERONLY]); + end + else if cmd = 'ban' then + begin + if g_Game_IsServer and g_Game_IsNet then + begin + if Length(P) < 2 then + begin + g_Console_Add('ban '); + Exit; + end; + if P[1] = '' then + begin + g_Console_Add('ban '); + Exit; + end; + + pl := g_Net_Client_ByName(P[1]); + if (pl <> nil) then + begin + s := g_Net_ClientName_ByID(pl^.ID); + g_Net_BanHost(pl^.Peer^.address.host, False); + enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN); + g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); + MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); + g_Net_Slist_ServerPlayerLeaves(); + end else + g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]])); + end else + g_Console_Add(_lc[I_MSG_SERVERONLY]); + end + else if cmd = 'ban_id' then + begin + if g_Game_IsServer and g_Game_IsNet then + begin + if Length(P) < 2 then + begin + g_Console_Add('ban_id '); + Exit; + end; + if P[1] = '' then + begin + g_Console_Add('ban_id '); + Exit; + end; + + a := StrToIntDef(P[1], 0); + if (NetClients <> nil) and (a <= High(NetClients)) then + if NetClients[a].Used and (NetClients[a].Peer <> nil) then + begin + s := g_Net_ClientName_ByID(NetClients[a].ID); + g_Net_BanHost(NetClients[a].Peer^.address.host, False); + enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN); + g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); + MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); + g_Net_Slist_ServerPlayerLeaves(); end; + end else + g_Console_Add(_lc[I_MSG_SERVERONLY]); + end + else if cmd = 'ban_pid' then + begin + if g_Game_IsServer and g_Game_IsNet then + begin + if Length(P) < 2 then + begin + g_Console_Add('ban_pid '); + Exit; + end; + if P[1] = '' then + begin + g_Console_Add('ban_pid '); + Exit; + end; + + a := StrToIntDef(P[1], 0); + pl := g_Net_Client_ByPlayer(a); + if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then + begin + s := g_Net_ClientName_ByID(pl^.ID); + g_Net_BanHost(pl^.Peer^.address.host, False); + enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN); + g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); + MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); + g_Net_Slist_ServerPlayerLeaves(); end; end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end - else if cmd = 'ban' then + else if cmd = 'permban' then begin if g_Game_IsServer and g_Game_IsNet then begin if Length(P) < 2 then begin - g_Console_Add('ban '); + g_Console_Add('permban '); Exit; end; if P[1] = '' then begin - g_Console_Add('ban '); + g_Console_Add('permban '); Exit; end; @@ -6234,29 +4830,29 @@ begin if (pl <> nil) then begin s := g_Net_ClientName_ByID(pl^.ID); - g_Net_BanHost(pl^.Peer^.address.host, False); - enet_peer_disconnect(pl^.Peer, NET_DISC_TEMPBAN); + g_Net_BanHost(pl^.Peer^.address.host); + enet_peer_disconnect(pl^.Peer, NET_DISC_BAN); + g_Net_SaveBanList(); g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); - if NetUseMaster then - g_Net_Slist_Update; + g_Net_Slist_ServerPlayerLeaves(); end else g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]])); end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end - else if cmd = 'ban_id' then + else if cmd = 'permban_id' then begin if g_Game_IsServer and g_Game_IsNet then begin if Length(P) < 2 then begin - g_Console_Add('ban_id '); + g_Console_Add('permban_id '); Exit; end; if P[1] = '' then begin - g_Console_Add('ban_id '); + g_Console_Add('permban_id '); Exit; end; @@ -6265,33 +4861,34 @@ begin if NetClients[a].Used and (NetClients[a].Peer <> nil) then begin s := g_Net_ClientName_ByID(NetClients[a].ID); - g_Net_BanHost(NetClients[a].Peer^.address.host, False); - enet_peer_disconnect(NetClients[a].Peer, NET_DISC_TEMPBAN); + g_Net_BanHost(NetClients[a].Peer^.address.host); + enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN); + g_Net_SaveBanList(); g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); - if NetUseMaster then - g_Net_Slist_Update; + g_Net_Slist_ServerPlayerLeaves(); end; end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end - else if cmd = 'permban' then + else if cmd = 'permban_pid' then begin if g_Game_IsServer and g_Game_IsNet then begin if Length(P) < 2 then begin - g_Console_Add('permban '); + g_Console_Add('permban_pid '); Exit; end; if P[1] = '' then begin - g_Console_Add('permban '); + g_Console_Add('permban_pid '); Exit; end; - pl := g_Net_Client_ByName(P[1]); - if (pl <> nil) then + a := StrToIntDef(P[1], 0); + pl := g_Net_Client_ByPlayer(a); + if (pl <> nil) and pl^.Used and (pl^.Peer <> nil) then begin s := g_Net_ClientName_ByID(pl^.ID); g_Net_BanHost(pl^.Peer^.address.host); @@ -6299,41 +4896,29 @@ begin g_Net_SaveBanList(); g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); - if NetUseMaster then - g_Net_Slist_Update; - end else - g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]])); + g_Net_Slist_ServerPlayerLeaves(); + end; end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end - else if cmd = 'permban_id' then + else if cmd = 'permban_ip' then begin if g_Game_IsServer and g_Game_IsNet then begin if Length(P) < 2 then begin - g_Console_Add('permban_id '); + g_Console_Add('permban_ip '); Exit; end; if P[1] = '' then begin - g_Console_Add('permban_id '); + g_Console_Add('permban_ip '); Exit; end; - a := StrToIntDef(P[1], 0); - if (NetClients <> nil) and (a <= High(NetClients)) then - if NetClients[a].Used and (NetClients[a].Peer <> nil) then - begin - s := g_Net_ClientName_ByID(NetClients[a].ID); - g_Net_BanHost(NetClients[a].Peer^.address.host); - enet_peer_disconnect(NetClients[a].Peer, NET_DISC_BAN); - g_Net_SaveBanList(); - g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); - MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); - if NetUseMaster then - g_Net_Slist_Update; - end; + g_Net_BanHost(P[1]); + g_Net_SaveBanList(); + g_Console_Add(Format(_lc[I_PLAYER_BAN], [P[1]])); end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end @@ -6522,26 +5107,31 @@ begin g_Console_Add(cmd + ' [MAP] [# players]'); Exit; end; - // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD - P[1] := addWadExtension(P[1]); - if FileExists(MapsDir + P[1]) then + // game not started yet, load fist map from some wad + found := false; + s := addWadExtension(P[1]); + found := e_FindResource(AllMapDirs, s); + P[1] := s; + if found then begin - // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå + P[1] := ExpandFileName(P[1]); + // if map not choosed then set first map if Length(P) < 3 then begin SetLength(P, 3); - P[2] := g_Game_GetFirstMap(MapsDir + P[1]); + P[2] := g_Game_GetFirstMap(P[1]); end; s := P[1] + ':\' + UpperCase(P[2]); - if g_Map_Exist(MapsDir + s) then + if g_Map_Exist(s) then begin - // Çàïóñêàåì ñâîþ èãðó + // start game g_Game_Free(); with gGameSettings do begin - GameMode := g_Game_TextToMode(gcGameMode); + Options := gsGameFlags; + GameMode := g_Game_TextToMode(gsGameMode); if gSwitchGameMode <> GM_NONE then GameMode := gSwitchGameMode; if GameMode = GM_NONE then GameMode := GM_DM; @@ -6577,43 +5167,47 @@ begin Exit; prt := StrToIntDef(P[2], 25666); - P[3] := addWadExtension(P[3]); - if FileExists(MapsDir + P[3]) then + s := addWadExtension(P[3]); + found := e_FindResource(AllMapDirs, s); + P[3] := s; + if found then begin - // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå + // get first map in wad, if not specified if Length(P) < 5 then begin SetLength(P, 5); - P[4] := g_Game_GetFirstMap(MapsDir + P[1]); + P[4] := g_Game_GetFirstMap(P[1]); end; - s := P[3] + ':\' + UpperCase(P[4]); - - if g_Map_Exist(MapsDir + s) then + if g_Map_Exist(s) then begin - // Çàïóñêàåì ñâîþ èãðó + // start game g_Game_Free(); with gGameSettings do begin - GameMode := g_Game_TextToMode(gcGameMode); - if gSwitchGameMode <> GM_NONE then - GameMode := gSwitchGameMode; + Options := gsGameFlags; + GameMode := g_Game_TextToMode(gsGameMode); + if gSwitchGameMode <> GM_NONE then GameMode := gSwitchGameMode; if GameMode = GM_NONE then GameMode := GM_DM; if GameMode = GM_SINGLE then GameMode := GM_COOP; b := 0; if Length(P) >= 6 then b := StrToIntDef(P[5], 0); - g_Game_StartServer(s, GameMode, TimeLimit, - GoalLimit, MaxLives, Options, b, listen, prt); - end; + g_Game_StartServer(s, GameMode, TimeLimit, GoalLimit, MaxLives, Options, b, listen, prt) + end end else + begin if P[4] = '' then g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]])) else - g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]])); - end else - g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]])); + g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]])) + end + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]])) + end end else if cmd = 'map' then begin @@ -6622,92 +5216,126 @@ begin if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then begin g_Console_Add(cmd + ' '); - g_Console_Add(cmd + ' [MAP]'); - end else - g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); - end else + g_Console_Add(cmd + ' [MAP]') + end + else + begin + g_Console_Add(_lc[I_MSG_GM_UNAVAIL]) + end + end + else + begin if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then begin - // Èä¸ò ñâîÿ èãðà èëè ñåðâåð if Length(P) < 3 then begin - // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà + // first param is map or wad s := UpperCase(P[1]); - if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then - begin // Êàðòà íàøëàñü + if g_Map_Exist(gGameSettings.WAD + ':\' + s) then + begin gExitByTrigger := False; if gGameOn then - begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü + begin + // already in game, finish current map gNextMap := s; gExit := EXIT_ENDLEVELCUSTOM; end - else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó - g_Game_ChangeMap(s); - end else + else + begin + // intermission, so change map immediately + g_Game_ChangeMap(s) + end + end + else begin - // Òàêîé êàðòû íåò, èùåì WAD ôàéë - pw := findDiskWad(MapsDir + P[1]); + s := P[1]; + found := e_FindResource(AllMapDirs, s); + P[1] := s; g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, 'WAD ' + P[1]])); - if FileExists(pw) then + if found then begin - // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà + // no such map, found wad + pw := P[1]; SetLength(P, 3); - P[1] := ExtractRelativePath(MapsDir, pw); - P[2] := g_Game_GetFirstMap(MapsDir + P[1]); - + P[1] := ExpandFileName(pw); + P[2] := g_Game_GetFirstMap(P[1]); s := P[1] + ':\' + P[2]; - - if g_Map_Exist(MapsDir + s) then + if g_Map_Exist(s) then begin gExitByTrigger := False; if gGameOn then - begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü + begin + // already in game, finish current map gNextMap := s; - gExit := EXIT_ENDLEVELCUSTOM; + gExit := EXIT_ENDLEVELCUSTOM end - else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó - g_Game_ChangeMap(s); - end else + else + begin + // intermission, so change map immediately + g_Game_ChangeMap(s) + end + end + else + begin if P[2] = '' then g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]])) else - g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])); - end else - g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])); + g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])) + end + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])) + end end; - end else + end + else begin - // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà - P[1] := addWadExtension(P[1]); - if FileExists(MapsDir + P[1]) then + s := addWadExtension(P[1]); + found := e_FindResource(AllMapDirs, s); + P[1] := s; + if found then begin - // Íàøëè WAD ôàéë P[2] := UpperCase(P[2]); s := P[1] + ':\' + P[2]; - - if g_Map_Exist(MapsDir + s) then - begin // Íàøëè êàðòó + if g_Map_Exist(s) then + begin gExitByTrigger := False; if gGameOn then - begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü + begin gNextMap := s; - gExit := EXIT_ENDLEVELCUSTOM; + gExit := EXIT_ENDLEVELCUSTOM end - else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó - g_Game_ChangeMap(s); - end else - g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])); - end else - g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])); - end; - end else - g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); + else + begin + g_Game_ChangeMap(s) + end + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])) + end + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])) + end + end + end + else + begin + g_Console_Add(_lc[I_MSG_GM_UNAVAIL]) + end + end end else if cmd = 'nextmap' then begin if not(gGameOn or (gState = STATE_INTERCUSTOM)) then + begin g_Console_Add(_lc[I_MSG_NOT_GAME]) - else begin + end + else + begin nm := True; if Length(P) = 1 then begin @@ -6715,113 +5343,148 @@ begin begin g_Console_Add(cmd + ' '); g_Console_Add(cmd + ' [MAP]'); - end else begin + end + else + begin nm := False; g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); end; - end else + end + else begin nm := False; if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then begin if Length(P) < 3 then begin - // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà + // first param is map or wad s := UpperCase(P[1]); - if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then - begin // Êàðòà íàøëàñü + if g_Map_Exist(gGameSettings.WAD + ':\' + s) then + begin + // map founded gExitByTrigger := False; gNextMap := s; nm := True; - end else + end + else begin - // Òàêîé êàðòû íåò, èùåì WAD ôàéë - P[1] := addWadExtension(P[1]); + // no such map, found wad + pw := addWadExtension(P[1]); + found := e_FindResource(MapDirs, pw); + if not found then + found := e_FindResource(WadDirs, pw); + P[1] := pw; g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]])); - if FileExists(MapsDir + P[1]) then + if found then begin - // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà + // map not specified, select first map SetLength(P, 3); - P[2] := g_Game_GetFirstMap(MapsDir + P[1]); - + P[2] := g_Game_GetFirstMap(P[1]); s := P[1] + ':\' + P[2]; - - if g_Map_Exist(MapsDir + s) then - begin // Óñòàíàâëèâàåì êàðòó + if g_Map_Exist(s) then + begin gExitByTrigger := False; gNextMap := s; - nm := True; - end else + nm := True + end + else + begin if P[2] = '' then g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]])) else - g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])); - end else - g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])); - end; - end else + g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])) + end + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])) + end + end + end + else begin - // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà - P[1] := addWadExtension(P[1]); - if FileExists(MapsDir + P[1]) then + // specified two params wad + map + pw := addWadExtension(P[1]); + found := e_FindResource(MapDirs, pw); + if not found then + found := e_FindResource(MapDirs, pw); + P[1] := pw; + if found then begin - // Íàøëè WAD ôàéë P[2] := UpperCase(P[2]); s := P[1] + ':\' + P[2]; - - if g_Map_Exist(MapsDir + s) then - begin // Íàøëè êàðòó + if g_Map_Exist(s) then + begin gExitByTrigger := False; gNextMap := s; - nm := True; - end else - g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])); - end else - g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])); - end; - end else - g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); + nm := True + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]])) + end + end + else + begin + g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]])) + end + end + end + else + begin + g_Console_Add(_lc[I_MSG_GM_UNAVAIL]) + end end; if nm then + begin if gNextMap = '' then g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET]) else - g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap])); - end; + g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap])) + end + end end else if (cmd = 'endmap') or (cmd = 'goodbye') then begin if not gGameOn then + begin g_Console_Add(_lc[I_MSG_NOT_GAME]) + end else + begin if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then begin gExitByTrigger := False; - // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä + // next map not specified, try to find trigger EXIT if (gNextMap = '') and (gTriggers <> nil) then + begin for a := 0 to High(gTriggers) do + begin if gTriggers[a].TriggerType = TRIGGER_EXIT then begin gExitByTrigger := True; //gNextMap := gTriggers[a].Data.MapName; gNextMap := gTriggers[a].tgcMap; - Break; - end; - // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå + Break + end + end + end; if gNextMap = '' then gNextMap := g_Game_GetNextMap(); - // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé if not isWadPath(gNextMap) then s := gGameSettings.WAD + ':\' + gNextMap else s := gNextMap; - // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ - if g_Map_Exist(MapsDir + s) then + if g_Map_Exist(s) then gExit := EXIT_ENDLEVELCUSTOM else - g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap])); - end else - g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); + g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap])) + end + else + begin + g_Console_Add(_lc[I_MSG_GM_UNAVAIL]) + end + end end else if (cmd = 'event') then begin @@ -6874,7 +5537,9 @@ begin end else if cmd = 'screenshot' then begin - g_TakeScreenShot() + {$IFNDEF HEADLESS} + g_TakeScreenShot() + {$ENDIF} end else if cmd = 'weapon' then begin @@ -6895,7 +5560,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 @@ -6971,11 +5636,35 @@ begin end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end + else if cmd = 'centerprint' then + begin + if (Length(P) > 2) and (P[1] <> '') then + begin + chstr := ''; + for a := 2 to High(P) do + chstr := chstr + P[a] + ' '; + + if Length(chstr) > 200 then SetLength(chstr, 200); + + if Length(chstr) < 1 then + begin + g_Console_Add('centerprint '); + Exit; + end; + + a := StrToIntDef(P[1], 100); + chstr := b_Text_Format(chstr); + g_Game_Message(chstr, a); + if g_Game_IsNet and g_Game_IsServer then + MH_SEND_GameEvent(NET_EV_BIGTEXT, a, chstr); + end + else g_Console_Add('centerprint '); + end else if (cmd = 'overtime') and not g_Game_IsClient then 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], @@ -7076,49 +5765,90 @@ begin end; end; -procedure g_TakeScreenShot(); +procedure SystemCommands(P: SSArray); var - a: Word; - FileName: string; - ssdir, t: string; - st: TStream; - ok: Boolean; + cmd: string; begin - if e_NoGraphics then Exit; - ssdir := GameDir+'/screenshots'; - if not findFileCI(ssdir, true) then - begin - // try to create dir - try - CreateDir(ssdir); - except - end; - if not findFileCI(ssdir, true) then exit; // alas - end; - try - for a := 1 to High(Word) do - begin - FileName := Format(ssdir+'screenshot%.3d.png', [a]); - t := FileName; - if findFileCI(t, true) then continue; - if not findFileCI(FileName) then + cmd := LowerCase(P[0]); + case cmd of + 'exit', 'quit': + begin + g_Game_Free(); + g_Game_Quit(); + end; +{$IFNDEF HEADLESS} + 'r_reset': + r_Render_Apply; +{$ENDIF} + 'r_maxfps': begin - ok := false; - st := createDiskFile(FileName); - try - e_MakeScreenshot(st, gScreenWidth, gScreenHeight); - ok := true; - finally - st.Free(); + if Length(p) = 2 then + begin + gMaxFPS := StrToIntDef(p[1], gMaxFPS); + if gMaxFPS > 0 then + gFrameTime := 1000 div gMaxFPS + else + gFrameTime := 0; end; - if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)])); - break; + e_LogWritefln('r_maxfps %d', [gMaxFPS]); end; - end; - except + 'g_language': + begin + if Length(p) = 2 then + begin + gAskLanguage := true; + gLanguage := LANGUAGE_ENGLISH; + case LowerCase(p[1]) of + 'english': + begin + gAskLanguage := false; + gLanguage := LANGUAGE_ENGLISH; + end; + 'russian': + begin + gAskLanguage := false; + gLanguage := LANGUAGE_RUSSIAN; + end; + 'ask': + begin + gAskLanguage := true; + gLanguage := LANGUAGE_ENGLISH; + end; + end; + g_Language_Set(gLanguage); + end + else + begin + e_LogWritefln('usage: %s ', [cmd]); + end + end; + end; +end; + +{$IFNDEF HEADLESS} +procedure g_TakeScreenShot(Filename: string = ''); + var t: TDateTime; dir, date, name: String; +begin + if e_NoGraphics then + Exit; + + dir := e_GetWriteableDir(ScreenshotDirs); + if Filename = '' then + begin + t := Now; + DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t); + Filename := 'screenshot-' + date; end; + + name := e_CatPath(dir, Filename + '.png'); + if r_Render_WriteScreenShot(name) then + g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name])) + else + g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [name])); end; +{$ENDIF} +{$IFDEF ENABLE_MENU} procedure g_Game_InGameMenu(Show: Boolean); begin if (g_ActiveWindow = nil) and Show then @@ -7137,18 +5867,19 @@ 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; end; +{$ENDIF} procedure g_Game_Pause (Enable: Boolean); var @@ -7181,7 +5912,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 @@ -7192,13 +5923,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; @@ -7292,12 +6023,11 @@ begin end; end; -procedure g_Game_Message(Msg: string; Time: Word); -begin - MessageLineLength := (gScreenWidth - 204) div e_CharFont_GetMaxWidth(gMenuFont); - MessageText := b_Text_Wrap(b_Text_Format(Msg), MessageLineLength); - MessageTime := Time; -end; + procedure g_Game_Message (Msg: string; Time: Word); + begin + MessageText := Msg; + MessageTime := Time; + end; procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True); const @@ -7443,8 +6173,7 @@ begin case gAnnouncer of ANNOUNCE_NONE: Exit; - ANNOUNCE_ME, - ANNOUNCE_MEPLUS: + ANNOUNCE_ME: if not g_Game_IsWatchedPlayer(SpawnerUID) then Exit; end; @@ -7566,7 +6295,7 @@ end; procedure g_Game_SetDebugMode(); begin gDebugMode := True; -// ×èòû (äàæå â ñâîåé èãðå): +// Читы (даже в своей игре): gCheats := True; end; @@ -7580,7 +6309,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 @@ -7599,7 +6328,9 @@ begin PBarWasHere := false; end; - g_ActiveWindow := nil; + {$IFDEF ENABLE_MENU} + g_ActiveWindow := nil; + {$ENDIF} ProcessLoading(true); end; @@ -7655,7 +6386,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 @@ -7665,7 +6396,7 @@ begin end else if (i < ParamCount) then - begin // Ïàðàìåòð ñî çíà÷åíèåì + begin // Параметр со значением Inc(i); SetLength(pars, Length(pars) + 1); with pars[High(pars)] do @@ -7779,11 +6510,9 @@ begin // Options: s := Find_Param_Value(pars, '-opt'); if (s = '') then - Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER + Opt := gsGameFlags else Opt := StrToIntDef(s, 0); - if Opt = 0 then - Opt := GAME_OPTION_ALLOWEXIT or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER; // Close after map: s := Find_Param_Value(pars, '--close'); @@ -7793,13 +6522,17 @@ begin // Override map to test: s := LowerCase(Find_Param_Value(pars, '-testmap')); if s <> '' then - gTestMap := MapsDir + s; + begin + if e_IsValidResourceName(s) then + e_FindResource(AllMapDirs, s); + gTestMap := ExpandFileName(s); + end; // Delete test map after play: s := Find_Param_Value(pars, '--testdelete'); if (s <> '') then begin - gMapToDelete := MapsDir + map; + //gMapToDelete := MapsDir + map; e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal); Halt(1); end; @@ -7831,8 +6564,8 @@ begin s := Find_Param_Value(pars, '-exec'); if s <> '' then begin - if not isWadPath(s) then - s := GameDir + '/' + s; +// if not isWadPath(s) then +// s := GameDir + '/' + s; {$I-} AssignFile(F, s); @@ -7879,8 +6612,10 @@ begin conRegVar('mon_sq_enabled', @gmon_debug_use_sqaccel, 'accelerated spatial queries for monsters', 'accelerated monster coldet'); conRegVar('wtrace_sq_enabled', @gwep_debug_fast_trace, 'accelerated spatial queries for weapon hitscan trace', 'accelerated weapon hitscan'); +{$IFDEF ENABLE_GFX} conRegVar('pr_enabled', @gpart_dbg_enabled, 'enable/disable particles', 'particles'); conRegVar('pr_phys_enabled', @gpart_dbg_phys_enabled, 'enable/disable particle physics', 'particle physics'); +{$ENDIF} conRegVar('los_enabled', @gmon_dbg_los_enabled, 'enable/disable monster LOS calculations', 'monster LOS', true); conRegVar('mon_think', @gmon_debug_think, 'enable/disable monster thinking', 'monster thinking', true); @@ -7891,8 +6626,6 @@ begin conRegVar('dbg_ignore_level_bounds', @g_dbg_ignore_bounds, 'ignore level bounds', '', false); - conRegVar('r_scale', @g_dbg_scale, 0.01, 100.0, 'render scale', '', false); - conRegVar('light_enabled', @gwin_k8_enable_light_experiments, 'enable/disable dynamic lighting', 'lighting'); conRegVar('light_player_halo', @g_playerLight, 'enable/disable player halo', 'player light halo'); @@ -7901,4 +6634,11 @@ begin conRegVar('r_showfps', @gShowFPS, 'draw fps counter', 'draw fps counter'); conRegVar('r_showtime', @gShowTime, 'show game time', 'show game time'); + conRegVar('r_showping', @gShowPing, 'show ping', 'show ping'); + conRegVar('r_showscore', @gShowGoals, 'show score', 'show score'); + conRegVar('r_showkillmsg', @gShowKillMsg, 'show kill log', 'show kill log'); + conRegVar('r_showlives', @gShowLives, 'show lives', 'show lives'); + 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.