X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_game.pas;h=7c413aabd2ed8166a05bdaeba151d8aa78d8af3d;hp=b2c3dc06b4533c45e59df0fb5262c79780f75ae2;hb=06ce403977f0da3911c62eed46414ad03afa9111;hpb=331297e82162a6acd3e9e07605368e329ce66105 diff --git a/src/game/g_game.pas b/src/game/g_game.pas index b2c3dc0..d1246ba 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -2,8 +2,7 @@ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * the Free Software Foundation, version 3 of the License ONLY. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,7 +22,7 @@ uses MAPDEF, g_basic, g_player, e_graphics, g_res_downloader, g_sound, g_gui, utils, md5, mempool, xprofiler, - g_touch; + g_touch, g_weapons; type TGameSettings = record @@ -32,6 +31,8 @@ type TimeLimit: Word; GoalLimit: Word; WarmupTime: Word; + SpawnInvul: Word; + ItemRespawnTime: Word; MaxLives: Byte; Options: LongWord; WAD: String; @@ -61,6 +62,11 @@ type Model: String; Color: TRGB; Team: Byte; + // ones below are sent only to the server + WeaponSwitch: Byte; + WeaponPreferences: Array[WP_FIRST..WP_LAST+1] of Byte; + SwitchToEmpty: Byte; + SkipFist: Byte; end; TMegaWADInfo = record @@ -83,6 +89,7 @@ procedure g_Game_Free (freeTextures: Boolean=true); procedure g_Game_LoadData(); procedure g_Game_FreeData(); procedure g_Game_Update(); +procedure g_Game_PreUpdate(); procedure g_Game_Draw(); procedure g_Game_Quit(); procedure g_Game_SetupScreenSize(); @@ -102,9 +109,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; @@ -124,14 +130,17 @@ function g_Game_GetMegaWADInfo(WAD: String): TMegaWADInfo; procedure g_Game_ChatSound(Text: String; Taunt: Boolean = True); procedure g_Game_Announce_GoodShot(SpawnerUID: Word); 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; 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); @@ -141,6 +150,7 @@ procedure g_Game_StepLoading(Value: Integer = -1); procedure g_Game_ClearLoading(); procedure g_Game_SetDebugMode(); procedure DrawLoadingStat(); +procedure DrawMenuBackground(tex: AnsiString); { procedure SetWinPause(Enable: Boolean); } @@ -171,13 +181,19 @@ const 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; + GAME_OPTION_ALLOWDROPFLAG = 2048; + GAME_OPTION_THROWFLAG = 4096; STATE_NONE = 0; STATE_MENU = 1; @@ -201,6 +217,7 @@ const DE_GLOBEVENT = 0; DE_BFGHIT = 1; DE_KILLCOMBO = 2; + DE_BODYKILL = 3; ANNOUNCE_NONE = 0; ANNOUNCE_ME = 1; @@ -208,12 +225,19 @@ const ANNOUNCE_ALL = 3; CONFIG_FILENAME = 'Doom2DF.cfg'; - LOG_FILENAME = 'Doom2DF.log'; TEST_MAP_NAME = '$$$_TEST_$$$'; STD_PLAYER_MODEL = 'Doomer'; +{$IFDEF HEADLESS} + DEFAULT_PLAYERS = 0; +{$ELSE} + DEFAULT_PLAYERS = 1; +{$ENDIF} + + STATFILE_VERSION = $03; + var gStdFont: DWORD; gGameSettings: TGameSettings; @@ -227,13 +251,20 @@ 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; + sound_get_flag: array[0..1] of TPlayableSound; + sound_lost_flag: array[0..1] of TPlayableSound; + sound_ret_flag: array[0..1] of TPlayableSound; + sound_cap_flag: array[0..1] of TPlayableSound; + gBodyKillEvent: Integer = -1; gDefInterTime: ShortInt = -1; gInterEndTime: LongWord = 0; gInterTime: LongWord = 0; @@ -242,10 +273,11 @@ var gTotalMonsters: Integer = 0; gPauseMain: Boolean = false; gPauseHolmes: Boolean = false; - gShowTime: Boolean = True; + gShowTime: Boolean = False; gShowFPS: Boolean = False; gShowGoals: Boolean = True; gShowStat: Boolean = True; + gShowPIDs: Boolean = False; gShowKillMsg: Boolean = True; gShowLives: Boolean = True; gShowPing: Boolean = False; @@ -263,6 +295,10 @@ var gSpectViewTwo: Boolean = False; gSpectPID1: Integer = -1; gSpectPID2: Integer = -1; + gSpectAuto: Boolean = False; + gSpectAutoNext: LongWord; + gSpectAutoStepX: Integer; + gSpectAutoStepY: Integer; gMusic: TMusic = nil; gLoadGameMode: Boolean; gCheats: Boolean = False; @@ -270,12 +306,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; @@ -313,6 +347,9 @@ var gDelayedEvents: Array of TDelayedEvent; gUseChatSounds: Boolean = True; gChatSounds: Array of TChatSound; + gWeaponAction: Array [0..1, WP_FACT..WP_LACT] of Boolean; // [player, weapon_action] + gSelectWeapon: Array [0..1, WP_FIRST..WP_LAST] of Boolean; // [player, weapon] + gInterReadyCount: Integer = 0; g_dbg_ignore_bounds: Boolean = false; r_smallmap_h: Integer = 0; // 0: left; 1: center; 2: right @@ -354,18 +391,17 @@ function gPause (): Boolean; inline; implementation uses -{$IFDEF USE_NANOGL} - nanoGL, -{$ELSE} - GL, GLExt, +{$INCLUDE ../nogl/noGLuses.inc} +{$IFDEF ENABLE_HOLMES} + g_holmes, {$ENDIF} - e_texture, 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, g_weapons, Math, + g_playermodel, g_gfx, g_options, Math, g_triggers, g_monsters, e_sound, CONFIG, - g_language, g_net, + g_language, g_net, g_main, g_phys, ENet, e_msg, g_netmsg, g_netmaster, - sfs, wadreader, g_holmes; + sfs, wadreader, g_system; var @@ -556,7 +592,6 @@ var FPSCounter, UPSCounter: Word; FPSTime, UPSTime: LongWord; DataLoaded: Boolean = False; - LastScreenShot: Int64; IsDrawStat: Boolean = False; CustomStat: TEndCustomGameStat; SingleStat: TEndSingleGameStat; @@ -567,6 +602,10 @@ var MessageLineLength: Integer = 80; MapList: SSArray = nil; MapIndex: Integer = -1; + InterReadyTime: Integer = -1; + StatShotDone: Boolean = False; + StatFilename: string = ''; // used by stat screenshot to save with the same name as the csv + StatDate: string = ''; MegaWAD: record info: TMegaWADInfo; endpic: String; @@ -625,6 +664,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 := ''; @@ -744,17 +845,17 @@ var cfg: TConfig; p: Pointer; {b, }len: Integer; - s: string; + 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 @@ -793,16 +894,16 @@ begin 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/'; - g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic); + TEXTUREFILTER := GL_LINEAR; + s := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD); + g_Texture_CreateWADEx('TEXTURE_endpic', s); + TEXTUREFILTER := GL_NEAREST; end; 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(); @@ -874,7 +975,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 := (sys_GetTicks() {div 1000}) + Time else gDelayedEvents[n].Time := gTime + Time; Result := n; @@ -884,6 +985,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)); @@ -932,6 +1034,7 @@ begin end else slWaitStr := _lc[I_NET_SLIST_ERROR]; + g_Serverlist_GenerateTable(slCurrent, slTable); end; g_Game_ExecuteEvent('ongameend'); @@ -966,6 +1069,7 @@ begin SetLength(CustomStat.PlayerStat, Length(CustomStat.PlayerStat)+1); with CustomStat.PlayerStat[High(CustomStat.PlayerStat)] do begin + Num := a; Name := gPlayers[a].Name; Frags := gPlayers[a].Frags; Deaths := gPlayers[a].Death; @@ -977,9 +1081,24 @@ 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; @@ -1015,6 +1134,7 @@ begin gMusic.SetByName('MUSIC_INTERMUS'); gMusic.Play(); gState := STATE_INTERSINGLE; + e_UnpressAllKeys(); g_Game_ExecuteEvent('oninter'); end @@ -1054,6 +1174,7 @@ var stat: TPlayerStatArray; wad, map: string; mapstr: string; + namestr: string; begin s1 := ''; s2 := ''; @@ -1195,8 +1316,12 @@ begin gg := g; bb := b; end; + if gShowPIDs then + namestr := Format('[%5d] %s', [UID, Name]) + else + namestr := Name; // Èìÿ - e_TextureFontPrintEx(x+16, _y, Name, gStdFont, rr, gg, bb, 1); + e_TextureFontPrintEx(x+16, _y, namestr, gStdFont, rr, gg, bb, 1); // Ïèíã/ïîòåðè e_TextureFontPrintEx(x+w1+16, _y, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, rr, gg, bb, 1); // Ôðàãè @@ -1231,11 +1356,15 @@ begin r := 255; g := 127; end; + if gShowPIDs then + namestr := Format('[%5d] %s', [UID, Name]) + else + namestr := Name; // Öâåò èãðîêà e_DrawFillQuad(x+16, _y+4, x+32-1, _y+16+4-1, Color.R, Color.G, Color.B, 0); e_DrawQuad(x+16, _y+4, x+32-1, _y+16+4-1, 192, 192, 192); // Èìÿ - e_TextureFontPrintEx(x+16+16+8, _y+4, Name, gStdFont, r, g, 0, 1); + e_TextureFontPrintEx(x+16+16+8, _y+4, namestr, gStdFont, r, g, 0, 1); // Ïèíã/ïîòåðè e_TextureFontPrintEx(x+w1+16, _y+4, Format(_lc[I_GAME_PING_MS], [Ping, Loss]), gStdFont, r, g, 0, 1); // Ôðàãè @@ -1250,6 +1379,10 @@ end; procedure g_Game_Init(); var SR: TSearchRec; + knownFiles: array of AnsiString = nil; + found: Boolean; + wext, s: AnsiString; + f: Integer; begin gExit := 0; gMapToDelete := ''; @@ -1258,10 +1391,12 @@ begin 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); @@ -1277,32 +1412,47 @@ begin 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); + // load models from all possible wad types, in all known directories + // this does a loosy job (linear search, ooph!), but meh + for wext in wadExtensions do + begin + for f := High(ModelDirs) downto Low(ModelDirs) do + begin + if (FindFirst(ModelDirs[f]+DirectorySeparator+'*'+wext, faAnyFile, SR) = 0) then + begin + repeat + found := false; + for s in knownFiles do + begin + if (strEquCI1251(forceFilenameExt(SR.Name, ''), forceFilenameExt(ExtractFileName(s), ''))) then + begin + found := true; + break; + end; + end; + if not found then + begin + SetLength(knownFiles, length(knownFiles)+1); + knownFiles[High(knownFiles)] := ModelDirs[f]+DirectorySeparator+SR.Name; + end; + until (FindNext(SR) <> 0); + end; + FindClose(SR); + end; + end; + + if (length(knownFiles) = 0) then raise Exception.Create('no player models found!'); + + if (length(knownFiles) = 1) then e_LogWriteln('1 player model found.', TMsgType.Notify) else e_LogWritefln('%d player models found.', [Integer(length(knownFiles))], TMsgType.Notify); + for s in knownFiles do + begin + if not g_PlayerModel_Load(s) then e_LogWritefln('Error loading model "%s"', [s], TMsgType.Warning); + end; gGameOn := false; gPauseMain := false; gPauseHolmes := false; gTime := 0; - LastScreenShot := 0; {e_MouseInfo.Accel := 1.0;} @@ -1312,11 +1462,13 @@ begin g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False); g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True); g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True); - g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True); + 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'); @@ -1435,68 +1587,135 @@ begin Result := ids[(Length(ids) - 1 + idx) mod Length(ids)]; end; -function isKeyPressed (key1: Word; key2: Word): Boolean; +function GetActivePlayerID_Random(Skip: Integer = -1): Integer; +var + a, idx: Integer; + ids: Array of Word; begin - if (key1 <> 0) and e_KeyPressed(key1) then begin result := true; exit; end; - if (key2 <> 0) and e_KeyPressed(key2) then begin result := true; exit; end; - result := false; + Result := -1; + if gPlayers = nil then + Exit; + SetLength(ids, 0); + idx := -1; + for a := Low(gPlayers) to High(gPlayers) do + if IsActivePlayer(gPlayers[a]) then + begin + SetLength(ids, Length(ids) + 1); + ids[High(ids)] := gPlayers[a].UID; + if gPlayers[a].UID = Skip then + idx := High(ids); + end; + if Length(ids) = 0 then + Exit; + if Length(ids) = 1 then + begin + Result := ids[0]; + Exit; + end; + Result := ids[Random(Length(ids))]; + a := 10; + while (idx <> -1) and (Result = Skip) and (a > 0) do + begin + Result := ids[Random(Length(ids))]; + Dec(a); + end; end; -procedure processPlayerControls (plr: TPlayer; var ctrl: TPlayerControl; var MoveButton: Byte; p2hack: Boolean=false); -var - time: Word; - strafeDir: Byte; - i: Integer; +function GetRandomSpectMode(Current: Byte): Byte; +label + retry; +begin + Result := Current; +retry: + case Random(7) of + 0: Result := SPECT_STATS; + 1: Result := SPECT_MAPVIEW; + 2: Result := SPECT_MAPVIEW; + 3: Result := SPECT_PLAYERS; + 4: Result := SPECT_PLAYERS; + 5: Result := SPECT_PLAYERS; + 6: Result := SPECT_PLAYERS; + end; + if (Current in [SPECT_STATS, SPECT_MAPVIEW]) and (Current = Result) then + goto retry; +end; + +procedure ProcessPlayerControls (plr: TPlayer; p: Integer; var MoveButton: Byte); + var + time: Word; + strafeDir: Byte; + i: Integer; begin if (plr = nil) then exit; - if (p2hack) then time := 1000 else time := 1; + if (p = 2) then time := 1000 else time := 1; strafeDir := MoveButton shr 4; MoveButton := MoveButton and $0F; - with ctrl do + + if gPlayerAction[p, ACTION_MOVELEFT] and (not gPlayerAction[p, ACTION_MOVERIGHT]) then + MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî" + else if (not gPlayerAction[p, ACTION_MOVELEFT]) and gPlayerAction[p, ACTION_MOVERIGHT] then + MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî" + else if (not gPlayerAction[p, ACTION_MOVELEFT]) and (not gPlayerAction[p, ACTION_MOVERIGHT]) then + MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî" + + // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó: + if MoveButton = 1 then + plr.PressKey(KEY_LEFT, time) + else if MoveButton = 2 then + plr.PressKey(KEY_RIGHT, time); + + // if we have "strafe" key, turn off old strafe mechanics + if gPlayerAction[p, ACTION_STRAFE] then + begin + // new strafe mechanics + if (strafeDir = 0) then + strafeDir := MoveButton; // start strafing + // now set direction according to strafe (reversed) + if (strafeDir = 2) then + plr.SetDirection(TDirection.D_LEFT) + else if (strafeDir = 1) then + plr.SetDirection(TDirection.D_RIGHT) + end + else begin - if isKeyPressed(KeyLeft, KeyLeft2) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 1 // Íàæàòà òîëüêî "Âëåâî" - else if (not isKeyPressed(KeyLeft, KeyLeft2)) and isKeyPressed(KeyRight, KeyRight2) then MoveButton := 2 // Íàæàòà òîëüêî "Âïðàâî" - else if (not isKeyPressed(KeyLeft, KeyLeft2)) and (not isKeyPressed(KeyRight, KeyRight2)) then MoveButton := 0; // Íå íàæàòû íè "Âëåâî", íè "Âïðàâî" + 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; + + // fix movebutton state + MoveButton := MoveButton or (strafeDir shl 4); - // Ñåé÷àñ èëè ðàíüøå áûëè íàæàòû "Âëåâî"/"Âïðàâî" => ïåðåäàåì èãðîêó: - if MoveButton = 1 then plr.PressKey(KEY_LEFT, time) - else if MoveButton = 2 then plr.PressKey(KEY_RIGHT, time); + // Îñòàëüíûå êëàâèøè: + 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); + if gPlayerAction[p, ACTION_ATTACK] then plr.PressKey(KEY_FIRE); + if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN); - // if we have "strafe" key, turn off old strafe mechanics - if isKeyPressed(KeyStrafe, KeyStrafe2) then + for i := WP_FACT to WP_LACT do + begin + if gWeaponAction[p, i] then begin - // new strafe mechanics - if (strafeDir = 0) then strafeDir := MoveButton; // start strafing - // now set direction according to strafe (reversed) - if (strafeDir = 2) then plr.SetDirection(TDirection.D_LEFT) - else if (strafeDir = 1) then plr.SetDirection(TDirection.D_RIGHT); + plr.ProcessWeaponAction(i); + gWeaponAction[p, i] := False end - else - begin - strafeDir := 0; // not strafing anymore - // Ðàíüøå áûëà íàæàòà "Âïðàâî", à ñåé÷àñ "Âëåâî" => áåæèì âïðàâî, ñìîòðèì âëåâî: - if (MoveButton = 2) and isKeyPressed(KeyLeft, KeyLeft2) then plr.SetDirection(TDirection.D_LEFT) - // Ðàíüøå áûëà íàæàòà "Âëåâî", à ñåé÷àñ "Âïðàâî" => áåæèì âëåâî, ñìîòðèì âïðàâî: - else if (MoveButton = 1) and isKeyPressed(KeyRight, KeyRight2) then plr.SetDirection(TDirection.D_RIGHT) - // ×òî-òî áûëî íàæàòî è íå èçìåíèëîñü => êóäà áåæèì, òóäà è ñìîòðèì: - else if MoveButton <> 0 then plr.SetDirection(TDirection(MoveButton-1)); - end; - - // fix movebutton state - MoveButton := MoveButton or (strafeDir shl 4); - - // Îñòàëüíûå êëàâèøè: - if isKeyPressed(KeyJump, KeyJump2) then plr.PressKey(KEY_JUMP, time); - if isKeyPressed(KeyUp, KeyUp2) then plr.PressKey(KEY_UP, time); - if isKeyPressed(KeyDown, KeyDown2) then plr.PressKey(KEY_DOWN, time); - if isKeyPressed(KeyFire, KeyFire2) then plr.PressKey(KEY_FIRE); - if isKeyPressed(KeyNextWeapon, KeyNextWeapon2) then plr.PressKey(KEY_NEXTWEAPON); - if isKeyPressed(KeyPrevWeapon, KeyPrevWeapon2) then plr.PressKey(KEY_PREVWEAPON); - if isKeyPressed(KeyOpen, KeyOpen2) then plr.PressKey(KEY_OPEN); + end; - for i := 0 to High(KeyWeapon) do - if isKeyPressed(KeyWeapon[i], KeyWeapon2[i]) then - plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best + for i := WP_FIRST to WP_LAST do + begin + if gSelectWeapon[p, i] then + begin + plr.QueueWeaponSwitch(i); // all choices are passed there, and god will take the best + gSelectWeapon[p, i] := False + end end; // HACK: add dynlight here @@ -1515,6 +1734,25 @@ begin if gwin_has_stencil and g_playerLight then g_AddDynLight(plr.GameX+32, plr.GameY+40, 128, 1, 1, 0, 0.6); end; +// HACK: don't have a "key was pressed" function +procedure InterReady(); +begin + if InterReadyTime > gTime then Exit; + InterReadyTime := gTime + 3000; + 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; @@ -1548,6 +1786,16 @@ var if mon.gncNeedSend then MH_SEND_MonsterPos(mon.UID); end; + function sendItemPos (it: PItem): Boolean; + begin + result := false; // don't stop + if it.needSend then + begin + MH_SEND_ItemPos(it.myId); + it.needSend := False; + end; + end; + var reliableUpdate: Boolean; begin @@ -1578,6 +1826,9 @@ begin Exit; end; + // process master server communications + g_Net_Slist_Pulse(); + case gState of STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå @@ -1600,12 +1851,14 @@ begin ( ( e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or - e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) + e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or + e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or + e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK) ) and (not gJustChatted) and (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) ) - or (g_Game_IsNet and (gInterTime > gInterEndTime)) + or (g_Game_IsNet and ((gInterTime > gInterEndTime) or ((gInterReadyCount >= NetClientCount) and (NetClientCount > 0)))) ) then begin // Íàæàëè /<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè: @@ -1640,6 +1893,22 @@ begin end; Exit; + end + else if g_Game_IsClient and + ( + ( + e_KeyPressed(IK_RETURN) or e_KeyPressed(IK_KPRETURN) or e_KeyPressed(IK_SPACE) or + e_KeyPressed(VK_FIRE) or e_KeyPressed(VK_OPEN) or + e_KeyPressed(JOY0_ATTACK) or e_KeyPressed(JOY1_ATTACK) or + e_KeyPressed(JOY2_ATTACK) or e_KeyPressed(JOY3_ATTACK) + ) + and (not gJustChatted) and (not gConsoleShow) and (not gChatShow) + and (g_ActiveWindow = nil) + ) + then + begin + // ready / unready + InterReady(); end; if gState = STATE_INTERTEXT then @@ -1654,6 +1923,8 @@ begin // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå: if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then begin + gState := STATE_INTERCUSTOM; + InterReadyTime := -1; if gLastMap and (gGameSettings.GameMode = GM_COOP) then begin g_Game_ExecuteEvent('onwadend'); @@ -1662,15 +1933,15 @@ begin end else gMusic.SetByName('MUSIC_ROUNDMUS'); - gMusic.Play(); - gState := STATE_INTERCUSTOM; + e_UnpressAllKeys(); end else // Çàêîí÷èëàñü ïîñëåäíÿÿ êàðòà â Îäèíî÷íîé èãðå begin gMusic.SetByName('MUSIC_INTERMUS'); gMusic.Play(); gState := STATE_INTERSINGLE; + e_UnpressAllKeys(); end; g_Game_ExecuteEvent('oninter'); end @@ -1688,34 +1959,12 @@ begin end; STATE_SLIST: - g_Serverlist_Control(slCurrent); + g_Serverlist_Control(slCurrent, slTable); end; - if g_Game_IsNet then - if not gConsoleShow then - if not gChatShow then - begin - if g_ActiveWindow = nil then - begin - if e_KeyPressed(gGameControls.GameControls.Chat) or e_KeyPressed(VK_CHAT) then - g_Console_Chat_Switch(False) - else if (e_KeyPressed(gGameControls.GameControls.TeamChat) or e_KeyPressed(VK_TEAM)) and - (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then - g_Console_Chat_Switch(True); - end; - end else - if not gChatEnter then - if (not e_KeyPressed(gGameControls.GameControls.Chat)) - and (not e_KeyPressed(gGameControls.GameControls.TeamChat)) - and (not e_KeyPressed(VK_CHAT)) - and (not e_KeyPressed(VK_TEAM)) then - gChatEnter := True; - // Ñòàòèñòèêà ïî Tab: if gGameOn then - IsDrawStat := (not gConsoleShow) and (not gChatShow) and - (gGameSettings.GameType <> GT_SINGLE) and - (e_KeyPressed(gGameControls.GameControls.Stat) or e_KeyPressed(VK_STATUS)); + 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 @@ -1790,8 +2039,8 @@ begin if gPlayer2 <> nil then gPlayer2.ReleaseKeys(); if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then begin - processPlayerControls(gPlayer1, gGameControls.P1Control, P1MoveButton); - processPlayerControls(gPlayer2, gGameControls.P2Control, P2MoveButton, true); + ProcessPlayerControls(gPlayer1, 0, P1MoveButton); + ProcessPlayerControls(gPlayer2, 1, P2MoveButton); end // if not console else begin @@ -1806,7 +2055,7 @@ begin begin if not gSpectKeyPress then begin - if isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2) then + if gPlayerAction[0, ACTION_JUMP] and (not gSpectAuto) then begin // switch spect mode case gSpectMode of @@ -1817,78 +2066,144 @@ begin end; gSpectKeyPress := True; end; - if gSpectMode = SPECT_MAPVIEW then + if (gSpectMode = SPECT_MAPVIEW) + and (not gSpectAuto) then begin - if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then + if gPlayerAction[0, ACTION_MOVELEFT] then gSpectX := Max(gSpectX - gSpectStep, 0); - if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then + if gPlayerAction[0, ACTION_MOVERIGHT] then gSpectX := Min(gSpectX + gSpectStep, gMapInfo.Width - gScreenWidth); - if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then + if gPlayerAction[0, ACTION_LOOKUP] then gSpectY := Max(gSpectY - gSpectStep, 0); - if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then + if gPlayerAction[0, ACTION_LOOKDOWN] then gSpectY := Min(gSpectY + gSpectStep, gMapInfo.Height - gScreenHeight); - if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then + if gWeaponAction[0, WP_PREV] then begin // decrease step if gSpectStep > 4 then gSpectStep := gSpectStep shr 1; - gSpectKeyPress := True; + gWeaponAction[0, WP_PREV] := False; end; - if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then + if gWeaponAction[0, WP_NEXT] then begin // increase step if gSpectStep < 64 then gSpectStep := gSpectStep shl 1; - gSpectKeyPress := True; + gWeaponAction[0, WP_NEXT] := False; end; end; - if gSpectMode = SPECT_PLAYERS then + if (gSpectMode = SPECT_PLAYERS) + and (not gSpectAuto) then begin - if isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2) then + if gPlayerAction[0, ACTION_LOOKUP] then begin // add second view gSpectViewTwo := True; gSpectKeyPress := True; end; - if isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2) then + if gPlayerAction[0, ACTION_LOOKDOWN] then begin // remove second view gSpectViewTwo := False; gSpectKeyPress := True; end; - if isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2) then + if gPlayerAction[0, ACTION_MOVELEFT] then begin // prev player (view 1) gSpectPID1 := GetActivePlayerID_Prev(gSpectPID1); gSpectKeyPress := True; end; - if isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2) then + if gPlayerAction[0, ACTION_MOVERIGHT] then begin // next player (view 1) gSpectPID1 := GetActivePlayerID_Next(gSpectPID1); gSpectKeyPress := True; end; - if isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2) then + if gWeaponAction[0, WP_PREV] then begin // prev player (view 2) gSpectPID2 := GetActivePlayerID_Prev(gSpectPID2); - gSpectKeyPress := True; + gWeaponAction[0, WP_PREV] := False; end; - if isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2) then + if gWeaponAction[0, WP_NEXT] then begin // next player (view 2) gSpectPID2 := GetActivePlayerID_Next(gSpectPID2); - gSpectKeyPress := True; + gWeaponAction[0, WP_NEXT] := False; end; end; + if gPlayerAction[0, ACTION_ATTACK] then + begin + if (gSpectMode = SPECT_STATS) and (not gSpectAuto) then + begin + gSpectAuto := True; + gSpectAutoNext := 0; + gSpectViewTwo := False; + gSpectKeyPress := True; + end + else + if gSpectAuto then + begin + gSpectMode := SPECT_STATS; + gSpectAuto := False; + gSpectKeyPress := True; + end; + end; end else - if (not isKeyPressed(gGameControls.P1Control.KeyJump, gGameControls.P1Control.KeyJump2)) and - (not isKeyPressed(gGameControls.P1Control.KeyLeft, gGameControls.P1Control.KeyLeft2)) and - (not isKeyPressed(gGameControls.P1Control.KeyRight, gGameControls.P1Control.KeyRight2)) and - (not isKeyPressed(gGameControls.P1Control.KeyUp, gGameControls.P1Control.KeyUp2)) and - (not isKeyPressed(gGameControls.P1Control.KeyDown, gGameControls.P1Control.KeyDown2)) and - (not isKeyPressed(gGameControls.P1Control.KeyPrevWeapon, gGameControls.P1Control.KeyPrevWeapon2)) and - (not isKeyPressed(gGameControls.P1Control.KeyNextWeapon, gGameControls.P1Control.KeyNextWeapon2)) then + if (not gPlayerAction[0, ACTION_JUMP]) and + (not gPlayerAction[0, ACTION_ATTACK]) and + (not gPlayerAction[0, ACTION_MOVELEFT]) and + (not gPlayerAction[0, ACTION_MOVERIGHT]) and + (not gPlayerAction[0, ACTION_LOOKUP]) and + (not gPlayerAction[0, ACTION_LOOKDOWN]) then gSpectKeyPress := False; + + if gSpectAuto then + begin + if gSpectMode = SPECT_MAPVIEW then + begin + i := Min(Max(gSpectX + gSpectAutoStepX, 0), gMapInfo.Width - gScreenWidth); + if i = gSpectX then + gSpectAutoNext := gTime + else + gSpectX := i; + i := Min(Max(gSpectY + gSpectAutoStepY, 0), gMapInfo.Height - gScreenHeight); + if i = gSpectY then + gSpectAutoNext := gTime + else + gSpectY := i; + end; + if gSpectAutoNext <= gTime then + begin + if gSpectAutoNext > 0 then + begin + gSpectMode := GetRandomSpectMode(gSpectMode); + case gSpectMode of + SPECT_MAPVIEW: + begin + gSpectX := Random(gMapInfo.Width - gScreenWidth); + gSpectY := Random(gMapInfo.Height - gScreenHeight); + gSpectAutoStepX := Random(9) - 4; + gSpectAutoStepY := Random(9) - 4; + if ((gSpectX < 800) and (gSpectAutoStepX < 0)) or + ((gSpectX > gMapInfo.Width - gScreenWidth - 800) and (gSpectAutoStepX > 0)) then + gSpectAutoStepX := gSpectAutoStepX * -1; + if ((gSpectY < 800) and (gSpectAutoStepY < 0)) or + ((gSpectY > gMapInfo.Height - gScreenHeight - 800) and (gSpectAutoStepY > 0)) then + gSpectAutoStepY := gSpectAutoStepY * -1; + end; + SPECT_PLAYERS: + begin + gSpectPID1 := GetActivePlayerID_Random(gSpectPID1); + end; + end; + end; + case gSpectMode of + SPECT_STATS: gSpectAutoNext := gTime + (Random(3) + 5) * 1000; + SPECT_MAPVIEW: gSpectAutoNext := gTime + (Random(4) + 7) * 1000; + SPECT_PLAYERS: gSpectAutoNext := gTime + (Random(7) + 8) * 1000; + end; + end; + end; end; // Îáíîâëÿåì âñå îñòàëüíîå: @@ -1939,6 +2254,18 @@ begin g_Mons_ForEach(sendMonsPos); + // update flags that aren't stationary + if gGameSettings.GameMode = GM_CTF then + for I := FLAG_RED to FLAG_BLUE do + if gFlags[I].NeedSend then + begin + gFlags[I].NeedSend := False; + MH_SEND_FlagPos(I); + end; + + // update items that aren't stationary + g_Items_ForEachAlive(sendItemPos); + if reliableUpdate then begin NetTimeToReliable := 0; @@ -1958,19 +2285,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 - if not g_Net_Slist_Connect then g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]); - 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 @@ -2009,19 +2335,13 @@ 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; -// Äåëàåì ñêðèíøîò (íå ÷àùå 200 ìèëëèñåêóíä): - if e_KeyPressed(gGameControls.GameControls.TakeScreenshot) or e_KeyPressed(VK_PRINTSCR) then - if (GetTimer()-LastScreenShot) > 200000 div 1000 then - begin - g_TakeScreenShot(); - LastScreenShot := GetTimer(); - end; - // Ãîðÿ÷àÿ êëàâèøà äëÿ âûçîâà ìåíþ âûõîäà èç èãðû (F10): if e_KeyPressed(IK_F10) and gGameOn and @@ -2031,7 +2351,7 @@ begin KeyPress(IK_F10); end; - Time := GetTimer() {div 1000}; + Time := sys_GetTicks() {div 1000}; // Îáðàáîòêà îòëîæåííûõ ñîáûòèé: if gDelayedEvents <> nil then @@ -2055,6 +2375,9 @@ begin if g_Game_IsNet and g_Game_IsServer then MH_SEND_GameEvent(NET_EV_KILLCOMBO, gDelayedEvents[a].DENum); end; + DE_BODYKILL: + if gGameOn then + g_Game_Announce_BodyKill(gDelayedEvents[a].DENum); end; gDelayedEvents[a].Pending := False; end; @@ -2157,6 +2480,7 @@ begin 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; @@ -2181,15 +2505,22 @@ begin end; g_Frames_CreateWAD(nil, 'FRAMES_TELEPORT', GameWAD+':TEXTURES\TELEPORT', 64, 64, 10, False); - g_Frames_CreateWAD(nil, 'FRAMES_PUNCH', GameWAD+':TEXTURES\PUNCH', 64, 64, 4, 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'); g_Sound_CreateWADEx('SOUND_GAME_DOOROPEN', GameWAD+':SOUNDS\DOOROPEN'); g_Sound_CreateWADEx('SOUND_GAME_DOORCLOSE', GameWAD+':SOUNDS\DOORCLOSE'); g_Sound_CreateWADEx('SOUND_GAME_BULK1', GameWAD+':SOUNDS\BULK1'); g_Sound_CreateWADEx('SOUND_GAME_BULK2', GameWAD+':SOUNDS\BULK2'); g_Sound_CreateWADEx('SOUND_GAME_BUBBLE1', GameWAD+':SOUNDS\BUBBLE1'); g_Sound_CreateWADEx('SOUND_GAME_BUBBLE2', GameWAD+':SOUNDS\BUBBLE2'); + g_Sound_CreateWADEx('SOUND_GAME_BURNING', GameWAD+':SOUNDS\BURNING'); g_Sound_CreateWADEx('SOUND_GAME_SWITCH1', GameWAD+':SOUNDS\SWITCH1'); g_Sound_CreateWADEx('SOUND_GAME_SWITCH0', GameWAD+':SOUNDS\SWITCH0'); g_Sound_CreateWADEx('SOUND_GAME_RADIO', GameWAD+':SOUNDS\RADIO'); @@ -2201,6 +2532,17 @@ begin g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL3X', GameWAD+':SOUNDS\KILL3X'); g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILL4X', GameWAD+':SOUNDS\KILL4X'); g_Sound_CreateWADEx('SOUND_ANNOUNCER_KILLMX', GameWAD+':SOUNDS\KILLMX'); + g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA1', GameWAD+':SOUNDS\MUHAHA1'); + g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA2', GameWAD+':SOUNDS\MUHAHA2'); + g_Sound_CreateWADEx('SOUND_ANNOUNCER_MUHAHA3', GameWAD+':SOUNDS\MUHAHA3'); + g_Sound_CreateWADEx('SOUND_CTF_GET1', GameWAD+':SOUNDS\GETFLAG1'); + g_Sound_CreateWADEx('SOUND_CTF_GET2', GameWAD+':SOUNDS\GETFLAG2'); + g_Sound_CreateWADEx('SOUND_CTF_LOST1', GameWAD+':SOUNDS\LOSTFLG1'); + g_Sound_CreateWADEx('SOUND_CTF_LOST2', GameWAD+':SOUNDS\LOSTFLG2'); + g_Sound_CreateWADEx('SOUND_CTF_RETURN1', GameWAD+':SOUNDS\RETFLAG1'); + g_Sound_CreateWADEx('SOUND_CTF_RETURN2', GameWAD+':SOUNDS\RETFLAG2'); + g_Sound_CreateWADEx('SOUND_CTF_CAPTURE1', GameWAD+':SOUNDS\CAPFLAG1'); + g_Sound_CreateWADEx('SOUND_CTF_CAPTURE2', GameWAD+':SOUNDS\CAPFLAG2'); goodsnd[0] := TPlayableSound.Create(); goodsnd[1] := TPlayableSound.Create(); @@ -2222,6 +2564,32 @@ begin killsnd[2].SetByName('SOUND_ANNOUNCER_KILL4X'); killsnd[3].SetByName('SOUND_ANNOUNCER_KILLMX'); + hahasnd[0] := TPlayableSound.Create(); + hahasnd[1] := TPlayableSound.Create(); + hahasnd[2] := TPlayableSound.Create(); + + hahasnd[0].SetByName('SOUND_ANNOUNCER_MUHAHA1'); + hahasnd[1].SetByName('SOUND_ANNOUNCER_MUHAHA2'); + hahasnd[2].SetByName('SOUND_ANNOUNCER_MUHAHA3'); + + sound_get_flag[0] := TPlayableSound.Create(); + sound_get_flag[1] := TPlayableSound.Create(); + sound_lost_flag[0] := TPlayableSound.Create(); + sound_lost_flag[1] := TPlayableSound.Create(); + sound_ret_flag[0] := TPlayableSound.Create(); + sound_ret_flag[1] := TPlayableSound.Create(); + sound_cap_flag[0] := TPlayableSound.Create(); + sound_cap_flag[1] := TPlayableSound.Create(); + + sound_get_flag[0].SetByName('SOUND_CTF_GET1'); + sound_get_flag[1].SetByName('SOUND_CTF_GET2'); + sound_lost_flag[0].SetByName('SOUND_CTF_LOST1'); + sound_lost_flag[1].SetByName('SOUND_CTF_LOST2'); + sound_ret_flag[0].SetByName('SOUND_CTF_RETURN1'); + sound_ret_flag[1].SetByName('SOUND_CTF_RETURN2'); + sound_cap_flag[0].SetByName('SOUND_CTF_CAPTURE1'); + sound_cap_flag[1].SetByName('SOUND_CTF_CAPTURE2'); + g_Game_LoadChatSounds(GameWAD+':CHATSND\SNDCFG'); g_Game_SetLoadingText(_lc[I_LOAD_ITEMS_DATA], 0, False); @@ -2259,14 +2627,21 @@ begin 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'); g_Sound_Delete('SOUND_GAME_DOOROPEN'); g_Sound_Delete('SOUND_GAME_DOORCLOSE'); g_Sound_Delete('SOUND_GAME_BULK1'); g_Sound_Delete('SOUND_GAME_BULK2'); g_Sound_Delete('SOUND_GAME_BUBBLE1'); g_Sound_Delete('SOUND_GAME_BUBBLE2'); + g_Sound_Delete('SOUND_GAME_BURNING'); g_Sound_Delete('SOUND_GAME_SWITCH1'); g_Sound_Delete('SOUND_GAME_SWITCH0'); @@ -2290,6 +2665,32 @@ begin g_Sound_Delete('SOUND_ANNOUNCER_KILL4X'); g_Sound_Delete('SOUND_ANNOUNCER_KILLMX'); + hahasnd[0].Free(); + hahasnd[1].Free(); + hahasnd[2].Free(); + + g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA1'); + g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA2'); + g_Sound_Delete('SOUND_ANNOUNCER_MUHAHA3'); + + sound_get_flag[0].Free(); + sound_get_flag[1].Free(); + sound_lost_flag[0].Free(); + sound_lost_flag[1].Free(); + sound_ret_flag[0].Free(); + sound_ret_flag[1].Free(); + sound_cap_flag[0].Free(); + sound_cap_flag[1].Free(); + + g_Sound_Delete('SOUND_CTF_GET1'); + g_Sound_Delete('SOUND_CTF_GET2'); + g_Sound_Delete('SOUND_CTF_LOST1'); + g_Sound_Delete('SOUND_CTF_LOST2'); + g_Sound_Delete('SOUND_CTF_RETURN1'); + g_Sound_Delete('SOUND_CTF_RETURN2'); + g_Sound_Delete('SOUND_CTF_CAPTURE1'); + g_Sound_Delete('SOUND_CTF_CAPTURE2'); + g_Game_FreeChatSounds(); DataLoaded := False; @@ -2306,9 +2707,9 @@ var begin e_TextureFontGetSize(gStdFont, ww2, hh2); - g_ProcessMessages(); + sys_HandleInput; - if e_KeyPressed(IK_TAB) or e_KeyPressed(VK_STATUS) then + if g_Console_Action(ACTION_SCORES) then begin if not gStatsPressed then begin @@ -2494,9 +2895,12 @@ begin gg := g; bb := b; end; - e_TextureFontPrintEx(x+8, _y, Name, gStdFont, rr, gg, bb, 1); - e_TextureFontPrintEx(x+w1+8, _y, IntToStr(Frags), gStdFont, rr, gg, bb, 1); - e_TextureFontPrintEx(x+w1+w2+8, _y, IntToStr(Deaths), gStdFont, rr, gg, bb, 1); + 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; @@ -2521,12 +2925,22 @@ begin else r := 255; - e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True); + if (gPlayers[Num] <> nil) and (gPlayers[Num].FReady) then + e_TextureFontPrintEx(x+8+16+8, _y+4, Name + ' *', gStdFont, r, r, r, 1, True) + else + e_TextureFontPrintEx(x+8+16+8, _y+4, Name, gStdFont, r, r, r, 1, True); e_TextureFontPrintEx(x+w1+8+16+8, _y+4, IntToStr(Frags), gStdFont, r, r, r, 1, True); e_TextureFontPrintEx(x+w1+w2+8+16+8, _y+4, IntToStr(Deaths), gStdFont, r, r, r, 1, True); _y := _y+24; end; end; + + // HACK: take stats screenshot immediately after the first frame of the stats showing + if gScreenshotStats and (not StatShotDone) and (Length(CustomStat.PlayerStat) > 1) then + begin + g_TakeScreenShot('stats/' + StatFilename); + StatShotDone := True; + end; end; procedure DrawSingleStat(); @@ -2768,6 +3182,25 @@ begin 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; @@ -2868,10 +3301,10 @@ begin aY2 := aY + aY2 - 1; case LiftType of - 0: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0); - 1: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0); - 2: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0); - 3: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0); + 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; @@ -3045,7 +3478,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); @@ -3245,7 +3678,8 @@ end; procedure DrawPlayer(p: TPlayer); var - px, py, a, b, c, d: Integer; + px, py, a, b, c, d, i, fX, fY: Integer; + camObj: TObj; //R: TRect; begin if (p = nil) or (p.FDummy) then @@ -3263,8 +3697,10 @@ begin glPushMatrix(); - px := p.GameX + PLAYER_RECT_CX; - py := p.GameY + PLAYER_RECT_CY+p.Obj.slopeUpLeft; + camObj := p.getCameraObj(); + camObj.lerp(gLerpFactor, fX, fY); + px := fX + PLAYER_RECT_CX; + py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor); if (g_dbg_scale = 1.0) and (not g_dbg_ignore_bounds) then begin @@ -3295,67 +3731,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 - nlerp(p.IncCamOld, p.IncCam, gLerpFactor); - 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? @@ -3390,18 +3804,31 @@ begin 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 p.FSpectator then - e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4, - p.GameY + PLAYER_RECT_CY - 4, - 'X', gStdFont, 255, 255, 255, 1, True); + if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then + case gPlayerIndicator of + 1: + p.DrawIndicator(_RGB(255, 255, 255)); + + 2: + for i := 0 to High(gPlayers) do + if gPlayers[i] <> nil then + if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255)) + else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then + if gPlayerIndicatorStyle = 1 then + gPlayers[i].DrawIndicator(_RGB(192, 192, 192)) + else gPlayers[i].DrawIndicator(gPlayers[i].GetColor); + end; + { for a := 0 to High(gCollideMap) do for b := 0 to High(gCollideMap[a]) do @@ -3453,7 +3880,7 @@ var begin if gExit = EXIT_QUIT then Exit; - Time := GetTimer() {div 1000}; + Time := sys_GetTicks() {div 1000}; FPSCounter := FPSCounter+1; if Time - FPSTime >= 1000 then begin @@ -3462,6 +3889,9 @@ begin FPSTime := Time; end; + e_SetRendertarget(True); + e_SetViewPort(0, 0, gScreenWidth, gScreenHeight); + if gGameOn or (gState = STATE_FOLD) then begin if (gPlayer1 <> nil) and (gPlayer2 <> nil) then @@ -3536,15 +3966,15 @@ begin if plView1 <> nil then begin gHearPoint1.Active := True; - gHearPoint1.Coords.X := plView1.GameX; - gHearPoint1.Coords.Y := plView1.GameY; + 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; - gHearPoint2.Coords.Y := plView2.GameY; + gHearPoint2.Coords.X := plView2.GameX + PLAYER_RECT.Width; + gHearPoint2.Coords.Y := plView2.GameY + PLAYER_RECT.Height DIV 2; end else gHearPoint2.Active := False; @@ -3584,8 +4014,10 @@ begin 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 @@ -3600,9 +4032,10 @@ begin Round(gScreenHeight / 2.75)-(h div 2), MessageText); end; - if IsDrawStat or (gSpectMode = 1) then DrawStat(); + if IsDrawStat or (gSpectMode = SPECT_STATS) then + DrawStat(); - if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) then + if gSpectHUD and (not gChatShow) and (gSpectMode <> SPECT_NONE) and (not gSpectAuto) then begin // Draw spectator GUI ww := 0; @@ -3617,6 +4050,11 @@ begin 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); @@ -3659,11 +4097,7 @@ begin begin if (gState = STATE_MENU) then begin - if (g_ActiveWindow = nil) or (g_ActiveWindow.BackTexture = '') then - begin - if g_Texture_Get('MENU_BACKGROUND', ID) then e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight) - else e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); - end; + 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 @@ -3698,10 +4132,7 @@ begin else back := 'INTER'; - if g_Texture_Get(back, ID) then - e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight) - else - e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); + DrawMenuBackground(back); DrawCustomStat(); @@ -3722,10 +4153,7 @@ begin begin back := 'INTER'; - if g_Texture_Get(back, ID) then - e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight) - else - e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); + DrawMenuBackground(back); DrawSingleStat(); @@ -3740,13 +4168,8 @@ begin if gState = STATE_ENDPIC then begin ID := DWORD(-1); - if not g_Texture_Get('TEXTURE_endpic', ID) then - g_Texture_Get(_lc[I_TEXTURE_ENDPIC], ID); - - if ID <> DWORD(-1) then - e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight) - else - e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); + if g_Texture_Get('TEXTURE_endpic', ID) then DrawMenuBackground('TEXTURE_endpic') + else DrawMenuBackground(_lc[I_TEXTURE_ENDPIC]); if g_ActiveWindow <> nil then begin @@ -3757,13 +4180,14 @@ begin 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); - e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150); - end; - g_Serverlist_Draw(slCurrent); +// 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; @@ -3777,7 +4201,9 @@ begin g_ActiveWindow.Draw(); end; +{$IFNDEF HEADLESS} g_Console_Draw(); +{$ENDIF} if g_debug_Sounds and gGameOn then begin @@ -3792,12 +4218,24 @@ begin e_TextureFontPrint(0, 16, Format('UPS: %d', [UPS]), gStdFont); end; - if gGameOn and gShowTime and (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT]) then + if gGameOn and gShowTime then drawTime(gScreenWidth-72, gScreenHeight-16); if gGameOn then drawProfilers(); + // TODO: draw this after the FBO and remap mouse click coordinates + +{$IFDEF ENABLE_HOLMES} g_Holmes_DrawUI(); +{$ENDIF} + + // blit framebuffer to screen + + e_SetRendertarget(False); + e_SetViewPort(0, 0, gWinSizeX, gWinSizeY); + e_BlitFramebuffer(gWinSizeX, gWinSizeY); + + // draw the overlay stuff on top of it g_Touch_Draw; end; @@ -3806,12 +4244,13 @@ 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; @@ -3820,7 +4259,7 @@ begin g_Game_DeleteTestMap(); gExit := EXIT_QUIT; - PushExitEvent(); + sys_RequestQuit; end; procedure g_FatalError(Text: String); @@ -3829,6 +4268,7 @@ begin e_WriteLog(Format(_lc[I_FATAL_ERROR], [Text]), TMsgType.Warning); gExit := EXIT_SIMPLE; + if gGameOn then EndGame; end; procedure g_SimpleError(Text: String); @@ -3878,7 +4318,7 @@ end; procedure g_Game_ChangeResolution(newWidth, newHeight: Word; nowFull, nowMax: Boolean); begin - g_Window_SetSize(newWidth, newHeight, nowFull); + sys_SetDisplayMode(newWidth, newHeight, gBPP, nowFull, nowMax); end; procedure g_Game_AddPlayer(Team: Byte = TEAM_NONE); @@ -3886,15 +4326,16 @@ begin if ((not gGameOn) and (gState <> STATE_INTERCUSTOM)) or (not (gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT])) then Exit; + + if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) 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; @@ -3910,13 +4351,15 @@ begin else begin gPlayer1.Name := gPlayer1Settings.Name; + gPlayer1.WeapSwitchMode := gPlayer1Settings.WeaponSwitch; + gPlayer1.setWeaponPrefs(gPlayer1Settings.WeaponPreferences); + gPlayer1.SwitchToEmpty := gPlayer1Settings.SwitchToEmpty; + gPlayer1.SkipFist := gPlayer1Settings.SkipFist; 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; + g_Net_Slist_ServerPlayerComes(); end; Exit; @@ -3942,13 +4385,15 @@ begin else begin gPlayer2.Name := gPlayer2Settings.Name; + gPlayer2.WeapSwitchMode := gPlayer2Settings.WeaponSwitch; + gPlayer2.setWeaponPrefs(gPlayer2Settings.WeaponPreferences); + gPlayer2.SwitchToEmpty := gPlayer2Settings.SwitchToEmpty; + gPlayer2.SkipFist := gPlayer2Settings.SkipFist; 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; + g_Net_Slist_ServerPlayerComes(); end; Exit; @@ -3971,11 +4416,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; @@ -3987,16 +4434,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(); @@ -4032,8 +4479,15 @@ 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; + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; + g_Game_ExecuteEvent('ongamestart'); // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: @@ -4050,6 +4504,10 @@ begin end; gPlayer1.Name := gPlayer1Settings.Name; + gPlayer1.WeapSwitchMode := gPlayer1Settings.WeaponSwitch; + gPlayer1.setWeaponPrefs(gPlayer1Settings.WeaponPreferences); + gPlayer1.SwitchToEmpty := gPlayer1Settings.SwitchToEmpty; + gPlayer1.SkipFist := gPlayer1Settings.SkipFist; nPl := 1; // Ñîçäàíèå âòîðîãî èãðîêà, åñëè åñòü: @@ -4065,11 +4523,15 @@ begin end; gPlayer2.Name := gPlayer2Settings.Name; + gPlayer2.WeapSwitchMode := gPlayer2Settings.WeaponSwitch; + gPlayer2.setWeaponPrefs(gPlayer2Settings.WeaponPreferences); + gPlayer2.SwitchToEmpty := gPlayer2Settings.SwitchToEmpty; + gPlayer2.SkipFist := gPlayer2Settings.SkipFist; 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])); @@ -4113,6 +4575,11 @@ begin gAimLine := False; gShowMap := False; + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; + g_Game_ExecuteEvent('ongamestart'); // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: @@ -4139,6 +4606,10 @@ begin end; gPlayer1.Name := gPlayer1Settings.Name; + gPlayer1.WeapSwitchMode := gPlayer1Settings.WeaponSwitch; + gPlayer1.setWeaponPrefs(gPlayer1Settings.WeaponPreferences); + gPlayer1.SwitchToEmpty := gPlayer1Settings.SwitchToEmpty; + gPlayer1.SkipFist := gPlayer1Settings.SkipFist; Inc(nPl); end; @@ -4155,11 +4626,15 @@ begin end; gPlayer2.Name := gPlayer2Settings.Name; + gPlayer2.WeapSwitchMode := gPlayer2Settings.WeaponSwitch; + gPlayer2.setWeaponPrefs(gPlayer2Settings.WeaponPreferences); + gPlayer2.SwitchToEmpty := gPlayer2Settings.SwitchToEmpty; + gPlayer2.SkipFist := gPlayer2Settings.SkipFist; 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; @@ -4190,6 +4665,7 @@ 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); @@ -4211,6 +4687,11 @@ begin gAimLine := False; gShowMap := False; + gLMSRespawn := LMS_RESPAWN_NONE; + gLMSRespawnTime := 0; + gSpectLatchPID1 := 0; + gSpectLatchPID2 := 0; + g_Game_ExecuteEvent('ongamestart'); // Óñòàíîâêà ðàçìåðîâ îêíà èãðîêà @@ -4236,6 +4717,10 @@ begin end; gPlayer1.Name := gPlayer1Settings.Name; + gPlayer1.WeapSwitchMode := gPlayer1Settings.WeaponSwitch; + gPlayer1.setWeaponPrefs(gPlayer1Settings.WeaponPreferences); + gPlayer1.SwitchToEmpty := gPlayer1Settings.SwitchToEmpty; + gPlayer1.SkipFist := gPlayer1Settings.SkipFist; end; if nPlayers >= 2 then @@ -4251,6 +4736,10 @@ begin end; gPlayer2.Name := gPlayer2Settings.Name; + gPlayer2.WeapSwitchMode := gPlayer2Settings.WeaponSwitch; + gPlayer2.setWeaponPrefs(gPlayer2Settings.WeaponPreferences); + gPlayer2.SwitchToEmpty := gPlayer2Settings.SwitchToEmpty; + gPlayer2.SkipFist := gPlayer2Settings.SkipFist; end; g_Game_SetLoadingText(_lc[I_LOAD_HOST], 0, False); @@ -4260,15 +4749,18 @@ 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; - 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; @@ -4280,6 +4772,7 @@ begin 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; @@ -4287,6 +4780,7 @@ begin // Íàñòðîéêè èãðîêîâ è áîòîâ: g_Player_Init(); + g_Net_Slist_ServerMapStarted(); NetState := NET_STATE_GAME; end; @@ -4328,6 +4822,15 @@ begin 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 @@ -4343,14 +4846,27 @@ 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(); if (MID = NET_MSG_INFO) and (State = 0) then @@ -4371,11 +4887,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]); @@ -4383,8 +4899,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, @@ -4400,10 +4918,14 @@ begin end; gPlayer1.Name := gPlayer1Settings.Name; + gPlayer1.WeapSwitchMode := gPlayer1Settings.WeaponSwitch; + gPlayer1.setWeaponPrefs(gPlayer1Settings.WeaponPreferences); + gPlayer1.SwitchToEmpty := gPlayer1Settings.SwitchToEmpty; + gPlayer1.SkipFist := gPlayer1Settings.SkipFist; 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])); @@ -4438,7 +4960,7 @@ begin ProcessLoading(true); - if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then + if g_Net_UserRequestExit() then begin State := 0; break; @@ -4452,19 +4974,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 @@ -4479,7 +4996,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; @@ -4490,17 +5007,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(); @@ -4518,21 +5037,61 @@ 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; + + gTime := 0; - Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName); + //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); + end; if Result then begin g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE); @@ -4562,12 +5121,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 := ''; @@ -4585,15 +5143,21 @@ 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 @@ -4601,14 +5165,7 @@ begin MH_SEND_GameEvent(NET_EV_MAPSTART, gGameSettings.GameMode, Map); // Ìàñòåðñåðâåð - if NetUseMaster then - begin - if (NetMHost = nil) or (NetMPeer = nil) then - if not g_Net_Slist_Connect then - g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]); - - g_Net_Slist_Update; - end; + g_Net_Slist_ServerMapStarted(); if NetClients <> nil then for I := 0 to High(NetClients) do @@ -4637,11 +5194,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; @@ -4669,7 +5226,7 @@ begin 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 @@ -4678,7 +5235,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 @@ -4704,19 +5261,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(); @@ -4726,18 +5300,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; @@ -4763,12 +5331,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; @@ -4798,17 +5368,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; @@ -4836,7 +5403,7 @@ var begin Result := ''; - MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD); + MapList := g_Map_GetMapsList(gGameSettings.WAD); if MapList = nil then Exit; @@ -4858,7 +5425,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; @@ -4947,77 +5514,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 = 'r_showfps' then - begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowFPS := (P[1][1] = '1'); + cmd: string; - if gShowFPS then - g_Console_Add(_lc[I_MSG_SHOW_FPS_ON]) - else - g_Console_Add(_lc[I_MSG_SHOW_FPS_OFF]); - end - else if (cmd = 'g_friendlyfire') and not g_Game_IsClient then + 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)])) @@ -5026,219 +5578,117 @@ 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_allow_monsters') and not g_Game_IsClient then + else if cmd = 'g_friendly_absorb_damage' 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_MONSTERS - else - Options := Options and (not GAME_OPTION_MONSTERS); - 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; + ParseGameFlag(GAME_OPTION_TEAMABSORBDAMAGE, I_MSG_FRIENDLY_ABSORB_DAMAGE_OFF, I_MSG_FRIENDLY_ABSORB_DAMAGE_ON); end - else if (cmd = 'g_bot_vsplayers') and not g_Game_IsClient then + else if cmd = 'g_friendly_hit_trace' 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_BOTVSPLAYER - else - Options := Options and (not GAME_OPTION_BOTVSPLAYER); - 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; + ParseGameFlag(GAME_OPTION_TEAMHITTRACE, I_MSG_FRIENDLY_HIT_TRACE_OFF, I_MSG_FRIENDLY_HIT_TRACE_ON); end - else if (cmd = 'g_bot_vsmonsters') and not g_Game_IsClient then + else if cmd = 'g_friendly_hit_projectile' 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_BOTVSMONSTER - else - Options := Options and (not GAME_OPTION_BOTVSMONSTER); - 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; + ParseGameFlag(GAME_OPTION_TEAMHITPROJECTILE, I_MSG_FRIENDLY_PROJECT_TRACE_OFF, I_MSG_FRIENDLY_PROJECT_TRACE_ON); end - else if (cmd = 'g_warmuptime') and not g_Game_IsClient then + else if cmd = 'g_weaponstay' 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); - end; - - g_Console_Add(Format(_lc[I_MSG_WARMUP], - [gGameSettings.WarmupTime])); - g_Console_Add(_lc[I_MSG_ONMAPCHANGE]); + ParseGameFlag(GAME_OPTION_WEAPONSTAY, I_MSG_WEAPONSTAY_OFF, I_MSG_WEAPONSTAY_ON); end - else if cmd = 'net_interp' then + else if cmd = 'g_allow_exit' then begin - if (Length(P) > 1) then - NetInterpLevel := StrToIntDef(P[1], NetInterpLevel); - - 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(); + ParseGameFlag(GAME_OPTION_ALLOWEXIT, I_MSG_ALLOWEXIT_OFF, I_MSG_ALLOWEXIT_ON, True); end - else if cmd = 'net_forceplayerupdate' then + else if cmd = 'g_allow_monsters' 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') - 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(); + ParseGameFlag(GAME_OPTION_MONSTERS, I_MSG_ALLOWMON_OFF, I_MSG_ALLOWMON_ON, True); end - else if cmd = 'net_predictself' then + else if cmd = 'g_allow_dropflag' 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') - 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(); + ParseGameFlag(GAME_OPTION_ALLOWDROPFLAG, I_MSG_ALLOWDROPFLAG_OFF, I_MSG_ALLOWDROPFLAG_ON); + end + else if cmd = 'g_throw_flag' then + begin + ParseGameFlag(GAME_OPTION_THROWFLAG, I_MSG_THROWFLAG_OFF, I_MSG_THROWFLAG_ON); + 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 = 'sv_name' then + else if cmd = 'g_dm_keys' then begin - if (Length(P) > 1) and (Length(P[1]) > 0) then + ParseGameFlag(GAME_OPTION_DMKEYS, I_MSG_DMKEYS_OFF, I_MSG_DMKEYS_ON, True); + end + else if cmd = 'g_gameflags' then + begin + if Length(P) > 1 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; + gsGameFlags := StrToDWordDef(P[1], gsGameFlags); + if g_Game_IsServer then + begin + gGameSettings.Options := gsGameFlags; + if g_Game_IsNet then MH_SEND_GameSettings; + end; end; - g_Console_Add(cmd + ' = "' + NetServerName + '"'); + g_Console_Add(Format('%s %u', [cmd, gsGameFlags])); end - else if cmd = 'sv_passwd' then + else if cmd = 'g_warmup_time' then begin - if (Length(P) > 1) and (Length(P[1]) > 0) then + if Length(P) > 1 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; + gsWarmupTime := nclamp(StrToIntDef(P[1], gsWarmupTime), 0, $FFFF); + if g_Game_IsServer then + begin + 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; end; - g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"'); + 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 = 'sv_maxplrs' then + else if cmd = 'g_spawn_invul' 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 + gsSpawnInvul := nclamp(StrToIntDef(P[1], gsSpawnInvul), 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; + gGameSettings.SpawnInvul := gsSpawnInvul; + if g_Game_IsNet then MH_SEND_GameSettings; end; end; - g_Console_Add(cmd + ' = ' + IntToStr(NetMaxClients)); + g_Console_Add(Format('%s %d', [cmd, Integer(gsSpawnInvul)])); end - else if cmd = 'sv_public' then + else if cmd = 'g_item_respawn_time' then begin - if (Length(P) > 1) then + 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 - if not g_Net_Slist_Connect() then - g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_SLIST_ERROR]); - g_Net_Slist_Update(); - end - else - if NetMPeer <> nil then - g_Net_Slist_Disconnect(); + 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(cmd + ' = ' + IntToStr(Byte(NetUseMaster))); + 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 = 'sv_intertime' then begin @@ -5247,232 +5697,417 @@ begin g_Console_Add(cmd + ' = ' + IntToStr(gDefInterTime)); end - else if cmd = 'p1_name' then + else if cmd = 'g_max_particles' then begin - if (Length(P) > 1) and gGameOn then + if Length(p) = 2 then begin - if g_Game_IsClient 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]); - end; - end - else if cmd = 'p2_name' then - begin - if (Length(P) > 1) and gGameOn then + a := Max(0, StrToIntDef(p[1], 0)); + g_GFX_SetMax(a) + end + else if Length(p) = 1 then begin - if g_Game_IsClient 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]); - end; - end - else if cmd = 'p1_color' then - begin - if Length(P) > 3 then - if g_Game_IsClient 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)); + e_LogWritefln('%s', [g_GFX_GetMax()]) + end + else + begin + e_LogWritefln('usage: %s ', [cmd]) + end end - else if (cmd = 'p2_color') and not g_Game_IsNet then + else if cmd = 'g_max_shells' then begin - if Length(P) > 3 then - if g_Game_IsClient 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)); + if Length(p) = 2 then + begin + a := Max(0, StrToIntDef(p[1], 0)); + g_Shells_SetMax(a) + end + else if Length(p) = 1 then + begin + e_LogWritefln('%s', [g_Shells_GetMax()]) + end + else + begin + e_LogWritefln('usage: %s ', [cmd]) + end end - else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then + else if cmd = 'g_max_gibs' then begin - if cmd = 'r_showtime' then + if Length(p) = 2 then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowTime := (P[1][1] = '1'); - - if gShowTime then - g_Console_Add(_lc[I_MSG_TIME_ON]) - else - g_Console_Add(_lc[I_MSG_TIME_OFF]); + a := Max(0, StrToIntDef(p[1], 0)); + g_Gibs_SetMax(a) end - else if cmd = 'r_showscore' then + else 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]); + e_LogWritefln('%s', [g_Gibs_GetMax()]) end - else if cmd = 'r_showstat' then + else 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]); + e_LogWritefln('usage: %s ', [cmd]) end - else if cmd = 'r_showkillmsg' then + end + else if cmd = 'g_max_corpses' then + begin + if Length(p) = 2 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]); + a := Max(0, StrToIntDef(p[1], 0)); + g_Corpses_SetMax(a) end - else if cmd = 'r_showlives' then + else if Length(p) = 1 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]); + e_LogWritefln('%s', [g_Corpses_GetMax()]) end - else if cmd = 'r_showspect' then + else 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]); + e_LogWritefln('usage: %s ', [cmd]) end - else if cmd = 'r_showping' then + end + else if cmd = 'g_scorelimit' then + begin + if Length(P) > 1 then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then - gShowPing := (P[1][1] = '1'); + gsGoalLimit := nclamp(StrToIntDef(P[1], gsGoalLimit), 0, $FFFF); + + if g_Game_IsServer 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); + + // 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(Format(_lc[I_MSG_SCORE_LIMIT], [Integer(gsGoalLimit)])); + end + else if cmd = 'g_timelimit' then + begin + if Length(P) > 1 then + begin + gsTimeLimit := nclamp(StrToIntDef(P[1], gsTimeLimit), 0, $FFFF); + if g_Game_IsServer then + begin + 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 = 'g_maxlives' then + begin + if Length(P) > 1 then + begin + gsMaxLives := nclamp(StrToIntDef(P[1], gsMaxLives), 0, $FFFF); + if g_Game_IsServer then + begin + gGameSettings.MaxLives := gsMaxLives; + if g_Game_IsNet then MH_SEND_GameSettings; + end; + end; + + g_Console_Add(Format(_lc[I_MSG_LIVES], [Integer(gsMaxLives)])); + end; +end; + +procedure PlayerSettingsCVars(P: SSArray); +var + cmd: string; + team: Byte; - 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 + function ParseTeam(s: string): Byte; + begin + 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 - if StrToIntDef(P[1], gGameSettings.GoalLimit) = 0 then - gGameSettings.GoalLimit := 0 - else + 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 - 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.Name := b_Text_Unformat(P[1]); + 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_name': 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.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; - - g_Console_Add(Format(_lc[I_MSG_LIVES], - [gGameSettings.MaxLives])); - if g_Game_IsNet then MH_SEND_GameSettings; - end; + 'p1_color': + 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 + gPlayer1.SetColor(gPlayer1Settings.Color); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; + end; + end; + 'p2_color': + 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 + gPlayer2.SetColor(gPlayer2Settings.Color); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer2.UID); + end; + end; + end; + 'p1_model': + begin + 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 + gPlayer1.FActualModelName := gPlayer1Settings.Model; + gPlayer1.SetModel(gPlayer1Settings.Model); + if g_Game_IsNet then MH_SEND_PlayerSettings(gPlayer1.UID); + end; + end; + end; + 'p2_model': + begin + if (Length(P) > 1) then + begin + 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; + 'p1_autoswitch': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponSwitch := EnsureRange(StrTointDef(P[1], 0), 0, 2); + end; + 'p2_autoswitch': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponSwitch := EnsureRange(StrTointDef(P[1], 0), 0, 2); + end; + 'p1_switch_empty': + begin + if (Length(P) = 2) then + gPlayer1Settings.SwitchToEmpty := EnsureRange(StrTointDef(P[1], 0), 0, 1); + end; + 'p2_switch_empty': + begin + if (Length(P) = 2) then + gPlayer2Settings.SwitchToEmpty := EnsureRange(StrTointDef(P[1], 0), 0, 1); + end; + 'p1_skip_fist': + begin + if (Length(P) = 2) then + gPlayer1Settings.SkipFist := EnsureRange(StrTointDef(P[1], 0), 0, 1); + end; + 'p2_skip_fist': + begin + if (Length(P) = 2) then + gPlayer2Settings.SkipFist := EnsureRange(StrTointDef(P[1], 0), 0, 1); + end; + 'p1_priority_kastet': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_kastet': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_saw': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_SAW] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_saw': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_SAW] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_pistol': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_pistol': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_KASTET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_shotgun1': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_SHOTGUN1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_shotgun1': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_SHOTGUN1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_shotgun2': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_SHOTGUN2] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_shotgun2': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_SHOTGUN2] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_chaingun': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_CHAINGUN] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_chaingun': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_CHAINGUN] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_rocketlauncher': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_ROCKETLAUNCHER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_rocketlauncher': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_ROCKETLAUNCHER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_plasma': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_PLASMA] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_plasma': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_PLASMA] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_bfg': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_BFG] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_bfg': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_BFG] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_super': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_SUPERPULEMET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_super': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_SUPERPULEMET] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_flamethrower': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WEAPON_FLAMETHROWER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_flamethrower': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WEAPON_FLAMETHROWER] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p1_priority_berserk': + begin + if (Length(P) = 2) then + gPlayer1Settings.WeaponPreferences[WP_LAST+1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; + 'p2_priority_berserk': + begin + if (Length(P) = 2) then + gPlayer2Settings.WeaponPreferences[WP_LAST+1] := EnsureRange(StrToIntDef(P[1], WP_FIRST), WP_FIRST, WP_LAST+1); + end; end; end; @@ -5503,11 +6138,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 @@ -5611,11 +6243,6 @@ begin g_Console_Add(Format(cmd + ' is %d', [Byte(g_Debug_Player)])); end - else if (cmd = 'd_joy') then - begin - for a := 1 to 8 do - g_Console_Add(e_JoystickStateToString(a)); - end else if (cmd = 'd_mem') then begin PrintHeapStats(); @@ -5852,18 +6479,12 @@ var prt: Word; nm: Boolean; listen: LongWord; + found: Boolean; begin // Îáùèå êîìàíäû: cmd := LowerCase(P[0]); chstr := ''; - if (cmd = 'quit') or - (cmd = 'exit') then - begin - g_Game_Free(); - g_Game_Quit(); - Exit; - end - else if cmd = 'pause' then + if cmd = 'pause' then begin if (g_ActiveWindow = nil) then g_Game_Pause(not gPauseMain); @@ -5906,8 +6527,7 @@ 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 @@ -5920,8 +6540,7 @@ begin 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; @@ -5952,13 +6571,40 @@ 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 @@ -5982,8 +6628,7 @@ begin 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); - 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 @@ -6013,12 +6658,40 @@ begin 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); - 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 = '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 = 'permban' then begin if g_Game_IsServer and g_Game_IsNet then @@ -6043,8 +6716,7 @@ 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; + g_Net_Slist_ServerPlayerLeaves(); end else g_Console_Add(Format(_lc[I_NET_ERR_NAME404], [P[1]])); end else @@ -6075,12 +6747,62 @@ 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; + g_Net_Slist_ServerPlayerLeaves(); end; end else g_Console_Add(_lc[I_MSG_SERVERONLY]); end + 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_pid '); + Exit; + end; + if P[1] = '' then + begin + g_Console_Add('permban_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); + 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); + g_Net_Slist_ServerPlayerLeaves(); + end; + end else + g_Console_Add(_lc[I_MSG_SERVERONLY]); + end + 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_ip '); + Exit; + end; + if P[1] = '' then + begin + g_Console_Add('permban_ip '); + Exit; + 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 else if cmd = 'unban' then begin if g_Game_IsServer and g_Game_IsNet then @@ -6176,7 +6898,9 @@ begin else if (cmd = 'addbot') or (cmd = 'bot_add') then begin - if Length(P) > 1 then + if Length(P) > 2 then + g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2), StrToIntDef(P[2], 100)) + else if Length(P) > 1 then g_Bot_Add(TEAM_NONE, StrToIntDef(P[1], 2)) else g_Bot_Add(TEAM_NONE, 2); @@ -6184,10 +6908,14 @@ begin else if cmd = 'bot_addlist' then begin if Length(P) > 1 then + begin if Length(P) = 2 then g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1)) + else if Length(P) = 3 then + g_Bot_AddList(TEAM_NONE, P[1], StrToIntDef(P[1], -1), StrToIntDef(P[2], 100)) else g_Bot_AddList(IfThen(P[2] = 'red', TEAM_RED, TEAM_BLUE), P[1], StrToIntDef(P[1], -1)); + end; end else if cmd = 'bot_removeall' then g_Bot_RemoveAll() @@ -6248,6 +6976,34 @@ begin end else g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); end + else if (cmd = 'an') or (cmd = 'announce') then + begin + if g_Game_IsNet then + begin + if Length(P) > 1 then + begin + for a := 1 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('announce '); + Exit; + end; + + chstr := 'centerprint 100 ' + b_Text_Format(chstr); + if g_Game_IsClient then + MC_SEND_RCONCommand(chstr) + else + g_Console_Process(chstr, True); + end + else + g_Console_Add('announce '); + end else + g_Console_Add(_lc[I_MSG_GM_UNAVAIL]); + end else if cmd = 'game' then begin if gGameSettings.GameType <> GT_NONE then @@ -6260,26 +7016,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; @@ -6315,43 +7076,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 @@ -6360,91 +7125,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 ôàéë - P[1] := addWadExtension(P[1]); - g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]])); - if FileExists(MapsDir + P[1]) then + 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 found then begin - // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà + // no such map, found wad + pw := P[1]; SetLength(P, 3); - 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 @@ -6452,113 +7252,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 @@ -6609,6 +7444,76 @@ begin end; end; end + else if cmd = 'screenshot' then + begin + g_TakeScreenShot() + end + else if (cmd = 'weapnext') or (cmd = 'weapprev') then + begin + a := 1 - (ord(cmd[5]) - ord('n')); + if a = -1 then + gWeaponAction[0, WP_PREV] := True; + if a = 1 then + gWeaponAction[0, WP_NEXT] := True; + end + else if cmd = 'weapon' then + begin + if Length(p) = 2 then + begin + a := WP_FIRST + StrToIntDef(p[1], 0) - 1; + if (a >= WP_FIRST) and (a <= WP_LAST) then + gSelectWeapon[0, a] := True + end + end + else if (cmd = 'p1_weapnext') or (cmd = 'p1_weapprev') + or (cmd = 'p2_weapnext') or (cmd = 'p2_weapprev') then + begin + a := 1 - (ord(cmd[8]) - ord('n')); + b := ord(cmd[2]) - ord('1'); + if a = -1 then + gWeaponAction[b, WP_PREV] := True; + if a = 1 then + gWeaponAction[b, WP_NEXT] := True; + end + else if (cmd = 'p1_weapon') or (cmd = 'p2_weapon') then + begin + if Length(p) = 2 then + begin + a := WP_FIRST + StrToIntDef(p[1], 0) - 1; + b := ord(cmd[2]) - ord('1'); + if (a >= WP_FIRST) and (a <= WP_LAST) then + gSelectWeapon[b, a] := True + end + end + else if (cmd = 'p1_weapbest') or (cmd = 'p2_weapbest') then + begin + b := ord(cmd[2]) - ord('1'); + if b = 0 then + gSelectWeapon[b, gPlayer1.GetMorePrefered()] := True + else + gSelectWeapon[b, gPlayer2.GetMorePrefered()] := True; + end + else if (cmd = 'dropflag') then + begin + if g_Game_IsServer then + begin + if gPlayer2 <> nil then gPlayer2.TryDropFlag(); + if gPlayer1 <> nil then gPlayer1.TryDropFlag(); + end + else + MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG); + end + else if (cmd = 'p1_dropflag') or (cmd = 'p2_dropflag') then + begin + b := ord(cmd[2]) - ord('1'); + if g_Game_IsServer then + begin + if (b = 1) and (gPlayer2 <> nil) then gPlayer2.TryDropFlag() + else if (b = 0) and (gPlayer1 <> nil) then gPlayer1.TryDropFlag(); + end + else + MC_SEND_CheatRequest(NET_CHEAT_DROPFLAG); + end // Êîìàíäû Ñâîåé èãðû: else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then begin @@ -6685,6 +7590,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 @@ -6790,47 +7719,101 @@ 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 - begin - ok := false; - st := createDiskFile(FileName); - try - e_MakeScreenshot(st, gScreenWidth, gScreenHeight); - ok := true; - finally - st.Free(); + cmd := LowerCase(P[0]); + case cmd of + 'exit', 'quit': + begin + g_Game_Free(); + g_Game_Quit(); + end; + 'r_reset': + begin + gRC_Width := Max(1, gRC_Width); + gRC_Height := Max(1, gRC_Height); + gBPP := Max(1, gBPP); + if sys_SetDisplayMode(gRC_Width, gRC_Height, gBPP, gRC_FullScreen, gRC_Maximized) = True then + e_LogWriteln('resolution changed') + else + e_LogWriteln('resolution not changed'); + sys_EnableVSync(gVSync); + end; + 'r_maxfps': + begin + 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; + '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; + +procedure g_TakeScreenShot(Filename: string = ''); + var s: TStream; t: TDateTime; dir, date, name: String; +begin + if e_NoGraphics then Exit; + try + 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, gWinSizeX, gWinSizeX); + 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 - end; + g_Console_Add('oh shit, i can''t create screenshot!') + end end; procedure g_Game_InGameMenu(Show: Boolean); @@ -6935,27 +7918,14 @@ begin e_StopChannels(); end; -procedure g_Game_UpdateTriggerSounds(); -var - i: Integer; +procedure g_Game_UpdateTriggerSounds; + var i: Integer; begin if gTriggers <> nil then for i := 0 to High(gTriggers) do with gTriggers[i] do - if (TriggerType = TRIGGER_SOUND) and - (Sound <> nil) and - (tgcLocal) and - Sound.IsPlaying() then - begin - if ((gPlayer1 <> nil) and g_CollidePoint(gPlayer1.GameX, gPlayer1.GameY, X, Y, Width, Height)) or - ((gPlayer2 <> nil) and g_CollidePoint(gPlayer2.GameX, gPlayer2.GameY, X, Y, Width, Height)) then - begin - Sound.SetPan(0.5 - tgcPan/255.0); - Sound.SetVolume(tgcVolume/255.0); - end - else - Sound.SetCoords(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0); - end; + if (TriggerType = TRIGGER_SOUND) and (Sound <> nil) and tgcLocal and Sound.IsPlaying() then + Sound.SetCoordsRect(X, Y, Width, Height, tgcVolume / 255.0) end; function g_Game_IsWatchedPlayer(UID: Word): Boolean; @@ -7163,6 +8133,24 @@ begin killsnd[n].Play(); end; +procedure g_Game_Announce_BodyKill(SpawnerUID: Word); +var + a: Integer; +begin + case gAnnouncer of + ANNOUNCE_NONE: + Exit; + ANNOUNCE_ME: + if not g_Game_IsWatchedPlayer(SpawnerUID) then + Exit; + end; + for a := 0 to 2 do + if hahasnd[a].IsPlaying() then + Exit; + + hahasnd[Random(3)].Play(); +end; + procedure g_Game_StartVote(Command, Initiator: string); var Need: Integer; @@ -7360,9 +8348,9 @@ begin while i <= ParamCount do begin s := ParamStr(i); - if (s[1] = '-') and (Length(s) > 1) then + if (Length(s) > 1) and (s[1] = '-') then begin - if (s[2] = '-') and (Length(s) > 2) then + if (Length(s) > 2) and (s[2] = '-') then begin // Îäèíî÷íûé ïàðàìåòð SetLength(pars, Length(pars) + 1); with pars[High(pars)] do @@ -7487,11 +8475,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'); @@ -7501,13 +8487,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; @@ -7523,9 +8513,9 @@ begin // Number of players: s := Find_Param_Value(pars, '-pl'); if (s = '') then - n := 1 + n := DEFAULT_PLAYERS else - n := StrToIntDef(s, 1); + n := StrToIntDef(s, DEFAULT_PLAYERS); // Start: s := Find_Param_Value(pars, '-port'); @@ -7539,8 +8529,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); @@ -7593,15 +8583,28 @@ begin 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); +{$IFDEF ENABLE_HOLMES} conRegVar('dbg_holmes', @g_holmes_enabled, 'enable/disable Holmes', 'Holmes', true); +{$ENDIF} 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'); conRegVar('r_smallmap_align_h', @r_smallmap_h, 'halign: 0: left; 1: center; 2: right', 'horizontal aligning of small maps'); conRegVar('r_smallmap_align_v', @r_smallmap_v, 'valign: 0: top; 1: center; 2: bottom', 'vertial aligning of small maps'); + + 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.