X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_game.pas;h=6b1e238f6f7b5928175a440d04d9b73a1923d865;hb=23394ea7d39d0674f09b218f1dfd5b9d700e7bbe;hp=6564a23464337389d03aa98db59c0c0ee65623c8;hpb=93f620df25ad634372657a64d609dcaf44e718bd;p=d2df-sdl.git diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 6564a23..6b1e238 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -31,6 +31,7 @@ type TimeLimit: Word; GoalLimit: Word; WarmupTime: Word; + SpawnInvul: Word; MaxLives: Byte; Options: LongWord; WAD: String; @@ -102,8 +103,7 @@ procedure g_Game_Restart(); procedure g_Game_RestartLevel(); procedure g_Game_RestartRound(NoMapRestart: Boolean = False); function g_Game_ClientWAD (NewWAD: String; const WHash: TMD5Digest): AnsiString; -procedure g_Game_SaveOptions(); -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; procedure g_Game_ChangeMap(const MapPath: String); procedure g_Game_ExitLevel(const Map: AnsiString); function g_Game_GetFirstMap(WAD: String): String; @@ -126,7 +126,7 @@ 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(); +procedure g_TakeScreenShot(Filename: string = ''); procedure g_FatalError(Text: String); procedure g_SimpleError(Text: String); function g_Game_IsTestMap(): Boolean; @@ -179,6 +179,7 @@ const GAME_OPTION_MONSTERS = 16; GAME_OPTION_BOTVSPLAYER = 32; GAME_OPTION_BOTVSMONSTER = 64; + GAME_OPTION_DMKEYS = 128; STATE_NONE = 0; STATE_MENU = 1; @@ -221,6 +222,8 @@ const DEFAULT_PLAYERS = 1; {$ENDIF} + STATFILE_VERSION = $03; + var gStdFont: DWORD; gGameSettings: TGameSettings; @@ -238,7 +241,7 @@ var 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; @@ -287,12 +290,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; @@ -377,11 +378,11 @@ uses {$IFDEF ENABLE_HOLMES} g_holmes, {$ENDIF} - e_texture, e_res, g_textures, g_main, g_window, g_menu, + e_texture, e_res, g_textures, g_window, g_menu, e_input, e_log, g_console, g_items, g_map, g_panel, g_playermodel, g_gfx, g_options, Math, g_triggers, g_monsters, e_sound, CONFIG, - g_language, g_net, + g_language, g_net, g_main, ENet, e_msg, g_netmsg, g_netmaster, sfs, wadreader, g_system; @@ -585,6 +586,9 @@ var MapList: SSArray = nil; MapIndex: Integer = -1; InterReadyTime: Integer = -1; + StatShotDone: Boolean = False; + StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv + StatDate: string = ''; MegaWAD: record info: TMegaWADInfo; endpic: String; @@ -643,6 +647,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 := ''; @@ -902,6 +968,7 @@ 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)); @@ -997,6 +1064,19 @@ 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'); @@ -1727,7 +1807,7 @@ begin and (not gJustChatted) and (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) ) - 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 // Íàæàëè /<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè: @@ -2794,6 +2874,13 @@ begin _y := _y+24; end; end; + + // HACK: take stats screenshot immediately after the first frame of the stats showing + if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then + begin + g_TakeScreenShot('stats/' + StatFilename); + StatShotDone := True; + end; end; procedure DrawSingleStat(); @@ -3331,7 +3418,7 @@ begin end else begin - glScissor(0, 0, gWinSizeX, gWinSizeY); + glScissor(0, 0, gScreenWidth, gScreenHeight); end; // no need to clear stencil buffer, light blitting will do it for us... but only for normal scale if (g_dbg_scale <> 1.0) then glClear(GL_STENCIL_BUFFER_BIT); @@ -3581,67 +3668,45 @@ begin 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); + sY := -b; sWidth := gPlayerScreenSize.X; sHeight := gPlayerScreenSize.Y; + fixViewportForScale(); - //glTranslatef(a, b+p.IncCam, 0); - - //if (p = gPlayer1) and (g_dbg_scale >= 1.0) then g_Holmes_plrViewSize(sWidth, sHeight); + i := py - (sY + sHeight div 2); + if (p.IncCam > 0) then + begin + // clamp to level bounds + if (sY - p.IncCam < 0) then + p.IncCam := nclamp(sY, 0, 120); + // clamp around player position + if (i > 0) then + p.IncCam := nclamp(p.IncCam, 0, max(0, 120 - i)); + end + else if (p.IncCam < 0) then + begin + // clamp to level bounds + if (sY + sHeight - p.IncCam > gMapInfo.Height) then + p.IncCam := nclamp(sY + sHeight - gMapInfo.Height, -120, 0); + // clamp around player position + if (i < 0) then + p.IncCam := nclamp(p.IncCam, min(0, -120 - i), 0); + end; - //conwritefln('OLD: (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]); - fixViewportForScale(); - //conwritefln(' (%s,%s)-(%s,%s)', [sX, sY, sWidth, sHeight]); + sY := sY - p.IncCam; - if (g_dbg_scale <> 1.0) and (not g_dbg_ignore_bounds) then + if (not g_dbg_ignore_bounds) then begin if (sX+sWidth > gMapInfo.Width) then sX := gMapInfo.Width-sWidth; if (sY+sHeight > gMapInfo.Height) then sY := gMapInfo.Height-sHeight; if (sX < 0) then sX := 0; if (sY < 0) then sY := 0; - - 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; + if (gBackSize.X <= gPlayerScreenSize.X) or (gMapInfo.Width <= sWidth) then c := 0 else c := trunc((gBackSize.X-gPlayerScreenSize.X)*sX/(gMapInfo.Width-sWidth)); + if (gBackSize.Y <= gPlayerScreenSize.Y) or (gMapInfo.Height <= sHeight) then d := 0 else d := trunc((gBackSize.Y-gPlayerScreenSize.Y)*sY/(gMapInfo.Height-sHeight)); + //r_smallmap_h: 0: left; 1: center; 2: right //r_smallmap_v: 0: top; 1: center; 2: bottom // horiz small map? @@ -4106,7 +4171,6 @@ procedure g_Game_Quit(); begin g_Game_StopAllSounds(True); gMusic.Free(); - g_Game_SaveOptions(); g_Game_FreeData(); g_PlayerModel_FreeData(); g_Texture_DeleteAll(); @@ -4180,7 +4244,7 @@ end; procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean); begin - sys_SetDisplayMode(newWidth, newHeight, gBPP, nowFull); + sys_SetDisplayMode(newWidth, newHeight, gBPP, nowFull, nowMax); end; procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE); @@ -4364,7 +4428,7 @@ begin 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])); @@ -4454,7 +4518,7 @@ begin 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; @@ -4556,7 +4620,7 @@ begin // Ñòàðòóåì ñåðâåð 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; @@ -4565,7 +4629,7 @@ begin 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])); @@ -4693,7 +4757,7 @@ begin //if newResPath = '' then begin //g_Game_SetLoadingText(_lc[I_LOAD_DL_RES], 0, False); - newResPath := g_Res_DownloadMapWAD(WadName, gWADHash); + newResPath := g_Res_DownloadMapWAD(ExtractFileName(WadName), gWADHash); if newResPath = '' then begin g_FatalError(_lc[I_NET_ERR_HASH]); @@ -4723,7 +4787,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])); @@ -4781,15 +4845,8 @@ begin e_WriteLog('NET: Connection successful.', TMsgType.Notify); end; -procedure g_Game_SaveOptions; - var s: AnsiString; -begin - s := e_GetWriteableDir(ConfigDirs); - if s <> '' then - g_Options_Write_Video(s + '/' + CONFIG_FILENAME) - else - e_LogWritefln('unable to find or create directory for configs', []); -end; +var + lastAsMegaWad: Boolean = false; procedure g_Game_ChangeMap(const MapPath: String); var @@ -4804,7 +4861,7 @@ 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; @@ -4820,10 +4877,10 @@ begin 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; @@ -4845,6 +4902,7 @@ begin g_Player_ResetTeams(); + lastAsMegaWad := asMegawad; if isWadPath(Map) then begin NewWAD := g_ExtractWadName(Map); @@ -4853,7 +4911,17 @@ begin begin nws := findDiskWad(NewWAD); //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']'); - if (length(nws) = 0) then nws := e_FindWad(MapDirs, 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 @@ -4880,6 +4948,8 @@ begin ResName := Map; end; + gTime := 0; + //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName); result := false; if (ResName <> '') and (NewWAD <> '') then @@ -4916,7 +4986,6 @@ begin gExit := 0; gPauseMain := false; gPauseHolmes := false; - gTime := 0; NetTimeToUpdate := 1; NetTimeToReliable := 0; NetTimeToMaster := NetMasterRate; @@ -5068,14 +5137,9 @@ begin Exit; end; -(* - xwad := ExtractRelativePath(MapsDir, gWAD); - e_LogWritefln('using downloaded client map wad [%s] for [%s]`', [xwad, NewWAD], TMsgType.Notify); - NewWAD := xwad; - g_Game_LoadWAD(NewWAD); -*) + e_LogWritefln('using downloaded client map wad [%s] for [%s]', [gWAD, NewWAD], TMsgType.Notify); + NewWAD := gWAD; - e_LogWritefln('using downloaded client map wad [%s]`', [NewWAD], TMsgType.Notify); g_Game_LoadWAD(NewWAD); result := NewWAD; @@ -5318,7 +5382,6 @@ var a, b: Integer; stat: TPlayerStatArray; cmd, s: string; - config: TConfig; begin stat := nil; cmd := LowerCase(P[0]); @@ -5471,6 +5534,27 @@ begin if g_Game_IsNet then MH_SEND_GameSettings; end; end + else if (cmd = 'g_dm_keys') and not g_Game_IsClient 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_DMKEYS + else + Options := Options and (not GAME_OPTION_DMKEYS); + end; + + if (LongBool(Options and GAME_OPTION_DMKEYS)) then + g_Console_Add(_lc[I_MSG_DMKEYS_ON]) + else + g_Console_Add(_lc[I_MSG_DMKEYS_OFF]); + + if g_Game_IsNet then MH_SEND_GameSettings; + end; + end else if (cmd = 'g_warmuptime') and not g_Game_IsClient then begin if Length(P) > 1 then @@ -5485,19 +5569,25 @@ begin [gGameSettings.WarmupTime])); g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); end + else if (cmd = 'g_spawn_invul') and not g_Game_IsClient then + begin + if Length(P) > 1 then + begin + if StrToIntDef(P[1], gGameSettings.SpawnInvul) = 0 then + gGameSettings.SpawnInvul := 0 + else + gGameSettings.SpawnInvul := StrToIntDef(P[1], gGameSettings.SpawnInvul); + end; + + g_Console_Add(Format(_lc[I_MSG_SPAWNINVUL], + [gGameSettings.SpawnInvul])); + g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); + end else if cmd = 'net_interp' then begin if (Length(P) > 1) then NetInterpLevel := StrToIntDef(P[1], NetInterpLevel); g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel)); - s := e_GetWriteableDir(ConfigDirs); - if s <> '' then - begin - config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); - config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel); - config.SaveFile(s + '/' + CONFIG_FILENAME); - config.Free - end end else if cmd = 'net_forceplayerupdate' then begin @@ -5508,15 +5598,6 @@ begin g_Console_Add('net_forceplayerupdate = 1') else g_Console_Add('net_forceplayerupdate = 0'); - - s := e_GetWriteableDir(ConfigDirs); - if s <> '' then - begin - config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); - config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate); - config.SaveFile(s + '/' + CONFIG_FILENAME); - config.Free - end end else if cmd = 'net_predictself' then begin @@ -5528,15 +5609,6 @@ begin g_Console_Add('net_predictself = 1') else g_Console_Add('net_predictself = 0'); - - s := e_GetWriteableDir(ConfigDirs); - if s <> '' then - begin - config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); - config.WriteBool('Client', 'PredictSelf', NetPredictSelf); - config.SaveFile(s + '/' + CONFIG_FILENAME); - config.Free - end end else if cmd = 'sv_name' then begin @@ -5609,219 +5681,222 @@ begin end else if cmd = 'p1_name' then begin - if (Length(P) > 1) and gGameOn 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 - 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]); + gPlayer1.Name := b_Text_Unformat(P[1]); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; end; end else if cmd = 'p2_name' then begin - if (Length(P) > 1) and gGameOn then + 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 - 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]); + gPlayer2.Name := b_Text_Unformat(P[1]); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); + end; end; end else if cmd = 'p1_color' then begin if Length(P) > 3 then + 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)); if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer1 <> nil) then 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 - 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)); + gPlayer1.SetColor(gPlayer1Settings.Color); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; + end; end - else if (cmd = 'p2_color') and not g_Game_IsNet then + else if cmd = 'p2_color' then begin if Length(P) > 3 then + 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)); if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer2 <> nil) then 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 - 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)); + gPlayer2.SetColor(gPlayer2Settings.Color); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); + end; + end; end - else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then + else if cmd = 'p1_model' then begin - if cmd = 'r_showscore' then + if (Length(P) > 1) 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 + gPlayer1Settings.Model := P[1]; + if g_Game_IsClient then + MC_SEND_PlayerSettings + else if gGameOn and (gPlayer1 <> nil) then + begin + gPlayer1.FActualModelName := gPlayer1Settings.Model; + gPlayer1.SetModel(gPlayer1Settings.Model); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; + end; + end + else if cmd = 'p2_model' then + begin + if (Length(P) > 1) then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowStat := (P[1][1] = '1'); + 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 + else 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 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 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 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 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 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 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 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 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 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 - begin - if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then - gGameSettings.GoalLimit := 0 - else - 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); + 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'); - gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b); - end; + 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 + begin + if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then + gGameSettings.GoalLimit := 0 + else + begin + b := 0; - if g_Game_IsNet then MH_SEND_GameSettings; - end; + 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); - 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); + gGameSettings.GoalLimit := Max(StrToIntDef(P[1], gGameSettings.GoalLimit), b); + end; - 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 + 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 begin - if Length(P) > 1 then + if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then + gGameSettings.MaxLives := 0 + else begin - if StrToIntDef(P[1], gGameSettings.MaxLives) = 0 then - gGameSettings.MaxLives := 0 - else - 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); - end; + 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); end; - - g_Console_Add(Format(_lc[I_MSG_LIVES], - [gGameSettings.MaxLives])); - if g_Game_IsNet then MH_SEND_GameSettings; end; + + g_Console_Add(Format(_lc[I_MSG_LIVES], + [gGameSettings.MaxLives])); + if g_Game_IsNet then MH_SEND_GameSettings; end; end; @@ -5852,11 +5927,8 @@ 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 @@ -7129,6 +7201,30 @@ 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 @@ -7234,17 +7330,31 @@ begin end; end; -procedure g_TakeScreenShot; - var s: TStream; t: TDateTime; date, name: String; +procedure g_TakeScreenShot(Filename: string = ''); + var s: TStream; t: TDateTime; dir, date, name: String; begin if e_NoGraphics then Exit; - t := Now; - DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t); - name := 'screenshot-' + date + '.png'; try - s := e_CreateResource(ScreenshotDirs, name); - e_MakeScreenshot(s, gScreenWidth, gScreenHeight); - g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name])) + 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'); + s := createDiskFile(name); + try + e_MakeScreenshot(s, gScreenWidth, gScreenHeight); + s.Free; + g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name])) + except + g_Console_Add(Format(_lc[I_CONSOLE_ERROR_WRITE], [name])); + s.Free; + DeleteFile(name) + end except g_Console_Add('oh shit, i can''t create screenshot!') end @@ -8027,6 +8137,7 @@ 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('r_resolution_scale', @r_pixel_scale, 0.01, 100.0, 'upscale factor', '', 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');