X-Git-Url: https://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_game.pas;h=ca8ee633877789f8b162f376495952d0c8800025;hp=114c692242e50d3b5213bb7639579c7d324540ff;hb=5301ecea6db1b7e6e3de991b8a8d420e53ee220f;hpb=793c463d8e71d5b31b0f99d050bd77870a7a9f2a diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 114c692..ca8ee63 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 @@ -102,9 +101,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; @@ -142,6 +140,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); } @@ -210,12 +209,17 @@ 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} + var gStdFont: DWORD; gGameSettings: TGameSettings; @@ -233,7 +237,7 @@ var gHearPoint1, gHearPoint2: THearPoint; gSoundEffectsDF: Boolean = False; gSoundTriggerTime: Word = 0; - gAnnouncer: Byte = ANNOUNCE_NONE; + gAnnouncer: Integer = ANNOUNCE_NONE; goodsnd: array[0..3] of TPlayableSound; killsnd: array[0..3] of TPlayableSound; hahasnd: array[0..2] of TPlayableSound; @@ -250,7 +254,7 @@ 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; @@ -282,12 +286,9 @@ 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 gResolutionChange: Boolean = False; - gRC_Width, gRC_Height: Word; + gRC_Width, gRC_Height: Integer; gRC_FullScreen, gRC_Maximized: Boolean; gLanguageChange: Boolean = False; gDebugMode: Boolean = False; @@ -326,6 +327,7 @@ var gUseChatSounds: Boolean = True; gChatSounds: Array of TChatSound; 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 @@ -371,13 +373,13 @@ uses {$IFDEF ENABLE_HOLMES} g_holmes, {$ENDIF} - e_texture, g_textures, g_main, g_window, g_menu, + e_texture, e_res, g_textures, g_main, g_window, g_menu, e_input, e_log, g_console, g_items, g_map, g_panel, g_playermodel, g_gfx, g_options, Math, g_triggers, g_monsters, e_sound, CONFIG, g_language, g_net, ENet, e_msg, g_netmsg, g_netmaster, - sfs, wadreader; + sfs, wadreader, g_system; var @@ -578,6 +580,7 @@ var MessageLineLength: Integer = 80; MapList: SSArray = nil; MapIndex: Integer = -1; + InterReadyTime: Integer = -1; MegaWAD: record info: TMegaWADInfo; endpic: String; @@ -755,17 +758,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 @@ -804,16 +807,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(); @@ -885,7 +888,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; @@ -978,6 +981,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; @@ -992,6 +996,8 @@ begin end; g_Game_ExecuteEvent('onmapend'); + if not g_Game_IsClient then g_Player_ResetReady; + gInterReadyCount := 0; // Çàòóõàþùèé ýêðàí: EndingGameCounter := 255; @@ -1263,6 +1269,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 := ''; @@ -1271,10 +1281,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); @@ -1290,26 +1302,42 @@ 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; @@ -1324,11 +1352,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'); @@ -1562,6 +1592,9 @@ begin if gPlayerAction[p, ACTION_WEAPPREV] then plr.PressKey(KEY_PREVWEAPON); if gPlayerAction[p, ACTION_ACTIVATE] then plr.PressKey(KEY_OPEN); + gPlayerAction[p, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation + gPlayerAction[p, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation + for i := WP_FIRST to WP_LAST do begin if gSelectWeapon[p, i] then @@ -1587,6 +1620,14 @@ 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_Update(); var Msg: g_gui.TMessage; @@ -1650,6 +1691,9 @@ begin Exit; end; + // process master server communications + g_Net_Slist_Pulse(); + case gState of STATE_INTERSINGLE, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Îäèíî÷íîé èãðå STATE_INTERCUSTOM, // Ñòàòèñòêà ïîñëå ïðîõîæäåíèÿ óðîâíÿ â Ñâîåé èãðå @@ -1679,7 +1723,7 @@ begin 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))) ) then begin // Íàæàëè /<Ïðîáåë> èëè ïðîøëî äîñòàòî÷íî âðåìåíè: @@ -1714,6 +1758,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 @@ -1728,6 +1788,7 @@ begin // Çàêîí÷èëñÿ óðîâåíü â Ñâîåé èãðå: if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then begin + InterReadyTime := -1; if gLastMap and (gGameSettings.GameMode = GM_COOP) then begin g_Game_ExecuteEvent('onwadend'); @@ -2080,19 +2141,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 @@ -2131,7 +2191,9 @@ 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; @@ -2145,7 +2207,7 @@ begin KeyPress(IK_F10); end; - Time := GetTimer() {div 1000}; + Time := sys_GetTicks() {div 1000}; // Îáðàáîòêà îòëîæåííûõ ñîáûòèé: if gDelayedEvents <> nil then @@ -2501,7 +2563,7 @@ var begin e_TextureFontGetSize(gStdFont, ww2, hh2); - g_ProcessMessages(); + sys_HandleInput; if g_Console_Action(ACTION_SCORES) then begin @@ -2689,9 +2751,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; @@ -2716,7 +2781,10 @@ 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; @@ -2963,6 +3031,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; @@ -3440,7 +3527,7 @@ end; procedure DrawPlayer(p: TPlayer); var - px, py, a, b, c, d: Integer; + px, py, a, b, c, d, i: Integer; //R: TRect; begin if (p = nil) or (p.FDummy) then @@ -3595,8 +3682,21 @@ begin renderMapInternal(-c, -d, true); - if (gGameSettings.GameMode <> GM_SINGLE) and gPlayerIndicator then - p.DrawIndicator(); + if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then + case gPlayerIndicator of + 1: + p.DrawIndicator(_RGB(255, 255, 255)); + + 2: + for i := 0 to High(gPlayers) do + if gPlayers[i] <> nil then + if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255)) + else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then + if gPlayerIndicatorStyle = 1 then + gPlayers[i].DrawIndicator(_RGB(192, 192, 192)) + else gPlayers[i].DrawIndicator(gPlayers[i].GetColor); + end; + if p.FSpectator then e_TextureFontPrintEx(p.GameX + PLAYER_RECT_CX - 4, p.GameY + PLAYER_RECT_CY - 4, @@ -3652,7 +3752,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 @@ -3735,15 +3835,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; @@ -3865,11 +3965,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 @@ -3904,10 +4000,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(); @@ -3928,10 +4021,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(); @@ -3946,13 +4036,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 @@ -3963,12 +4048,13 @@ 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; +// 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; @@ -3983,7 +4069,9 @@ begin g_ActiveWindow.Draw(); end; +{$IFNDEF HEADLESS} g_Console_Draw(); +{$ENDIF} if g_debug_Sounds and gGameOn then begin @@ -3998,7 +4086,7 @@ 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(); @@ -4014,12 +4102,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; @@ -4028,7 +4117,7 @@ begin g_Game_DeleteTestMap(); gExit := EXIT_QUIT; - PushExitEvent(); + sys_RequestQuit; end; procedure g_FatalError(Text: String); @@ -4086,7 +4175,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); @@ -4122,9 +4211,7 @@ begin 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; @@ -4154,9 +4241,7 @@ begin 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; @@ -4179,9 +4264,7 @@ 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 gPlayer2 := nil; Exit; @@ -4195,9 +4278,7 @@ 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 gPlayer1 := nil; @@ -4205,6 +4286,7 @@ begin end; Exit; end; + g_Net_Slist_ServerPlayerLeaves(); end; procedure g_Game_Spectate(); @@ -4277,7 +4359,7 @@ begin end; // Çàãðóçêà è çàïóñê êàðòû: - if not g_Game_StartMap(MAP, True) then + if not g_Game_StartMap(false{asMegawad}, MAP, True) then begin if (Pos(':\', Map) > 0) or (Pos(':/', Map) > 0) then tmps := Map else tmps := gGameSettings.WAD + ':\' + MAP; g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [tmps])); @@ -4367,7 +4449,7 @@ begin end; // Çàãðóçêà è çàïóñê êàðòû: - if not g_Game_StartMap(Map, True) then + if not g_Game_StartMap(true{asMegawad}, Map, True) then begin g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Map])); Exit; @@ -4398,6 +4480,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); @@ -4472,11 +4555,14 @@ begin Exit; end; - g_Net_Slist_Set(NetSlistIP, NetSlistPort); + g_Net_Slist_Set(NetSlistIP, NetSlistPort, NetSlistList); + + 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; @@ -4488,6 +4574,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; @@ -4495,6 +4582,7 @@ begin // Íàñòðîéêè èãðîêîâ è áîòîâ: g_Player_Init(); + g_Net_Slist_ServerMapStarted(); NetState := NET_STATE_GAME; end; @@ -4536,6 +4624,10 @@ begin NetState := NET_STATE_AUTH; g_Game_SetLoadingText(_lc[I_LOAD_CONNECT], 0, False); + + // create (or update) map/resource databases + g_Res_CreateDatabases(true); + // Ñòàðòóåì êëèåíò if not g_Net_Connect(Addr, Port) then begin @@ -4551,14 +4643,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 @@ -4579,11 +4684,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]); @@ -4591,8 +4696,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, @@ -4611,7 +4718,7 @@ begin gPlayer1.UID := NetPlrUID1; gPlayer1.Reset(True); - if not g_Game_StartMap(newResPath + ':\' + Map, True) then + if not g_Game_StartMap(false{asMegawad}, newResPath + ':\' + Map, True) then begin g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WadName + ':\' + Map])); @@ -4646,7 +4753,7 @@ begin ProcessLoading(true); - if e_KeyPressed(IK_SPACE) or e_KeyPressed(VK_ESCAPE) then + if g_Net_UserRequestExit() then begin State := 0; break; @@ -4669,10 +4776,8 @@ begin 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 @@ -4687,7 +4792,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; @@ -4698,17 +4803,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(); @@ -4726,21 +4833,59 @@ begin g_Player_ResetTeams(); + lastAsMegaWad := asMegawad; if isWadPath(Map) then begin NewWAD := g_ExtractWadName(Map); ResName := g_ExtractFileName(Map); if g_Game_IsServer then begin - gWADHash := MD5File(MapsDir + NewWAD); - g_Game_LoadWAD(NewWAD); - end else + nws := findDiskWad(NewWAD); + //writeln('000: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']'); + if (asMegawad) then + begin + if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD); + if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD); + end + else + begin + if (length(nws) = 0) then nws := e_FindWad(MapDirs, NewWAD); + if (length(nws) = 0) then nws := e_FindWad(MegawadDirs, NewWAD); + end; + //if (length(nws) = 0) then nws := e_FindWad(MapDownloadDirs, NewWAD); + //writeln('001: Map=[', Map, ']; nws=[', nws, ']; NewWAD=[', NewWAD, ']'); + //nws := NewWAD; + if (length(nws) = 0) then + begin + ResName := ''; // failed + end + else + begin + NewWAD := nws; + if (g_Game_IsNet) then gWADHash := MD5File(nws); + //writeln('********: nws=', nws, ' : Map=', Map, ' : nw=', NewWAD, ' : resname=', ResName); + g_Game_LoadWAD(NewWAD); + end; + end + else + begin // hash received in MC_RECV_GameEvent -> NET_EV_MAPSTART - g_Game_ClientWAD(NewWAD, gWADHash); - end else + NewWAD := g_Game_ClientWAD(NewWAD, gWADHash); + end; + end + else + begin + NewWAD := gGameSettings.WAD; ResName := Map; + end; - Result := g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName); + //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); @@ -4809,14 +4954,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 @@ -4845,11 +4983,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; @@ -4877,7 +5015,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 @@ -4886,7 +5024,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 @@ -4912,19 +5050,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(); @@ -4934,6 +5089,7 @@ begin end; NewWAD := ExtractRelativePath(MapsDir, gWAD); g_Game_LoadWAD(NewWAD); + } end; procedure g_Game_RestartRound(NoMapRestart: Boolean = False); @@ -5044,7 +5200,7 @@ var begin Result := ''; - MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD); + MapList := g_Map_GetMapsList(gGameSettings.WAD); if MapList = nil then Exit; @@ -5066,7 +5222,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; @@ -5327,27 +5483,34 @@ begin 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(); + s := e_GetWriteableDir(ConfigDirs); + if s <> '' then + begin + config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); + config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel); + config.SaveFile(s + '/' + CONFIG_FILENAME); + config.Free + end end else if cmd = 'net_forceplayerupdate' then begin - if (Length(P) > 1) and - ((P[1] = '1') or (P[1] = '0')) then + 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(); + + s := e_GetWriteableDir(ConfigDirs); + if s <> '' then + begin + config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); + config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate); + config.SaveFile(s + '/' + CONFIG_FILENAME); + config.Free + end end else if cmd = 'net_predictself' then begin @@ -5359,10 +5522,15 @@ begin 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(); + + s := e_GetWriteableDir(ConfigDirs); + if s <> '' then + begin + config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); + config.WriteBool('Client', 'PredictSelf', NetPredictSelf); + config.SaveFile(s + '/' + CONFIG_FILENAME); + config.Free + end end else if cmd = 'sv_name' then begin @@ -5371,8 +5539,7 @@ 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; + g_Net_Slist_ServerRenamed(); end; g_Console_Add(cmd + ' = "' + NetServerName + '"'); @@ -5384,8 +5551,7 @@ 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; + g_Net_Slist_ServerRenamed(); end; g_Console_Add(cmd + ' = "' + AnsiLowerCase(NetPassword) + '"'); @@ -5399,6 +5565,7 @@ begin begin b := 0; for a := 0 to High(NetClients) do + begin if NetClients[a].Used then begin Inc(b); @@ -5410,8 +5577,8 @@ begin MH_SEND_GameEvent(NET_EV_PLAYER_KICK, 0, s); end; end; - if NetUseMaster then - g_Net_Slist_Update; + end; + g_Net_Slist_ServerRenamed(); end; end; @@ -5422,17 +5589,7 @@ begin if (Length(P) > 1) then begin NetUseMaster := StrToIntDef(P[1], Byte(NetUseMaster)) > 0; - if g_Game_IsServer and g_Game_IsNet then - if NetUseMaster then - begin - if NetMPeer = nil then - 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(); + if NetUseMaster then g_Net_Slist_Public() else g_Net_Slist_Private(); end; g_Console_Add(cmd + ' = ' + IntToStr(Byte(NetUseMaster))); @@ -5530,18 +5687,7 @@ begin end else if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then begin - if cmd = 'r_showtime' 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]); - end - else if cmd = 'r_showscore' then + if cmd = 'r_showscore' then begin if (Length(P) > 1) and ((P[1] = '1') or (P[1] = '0')) then @@ -5700,11 +5846,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])); end else if cmd = 'd_sounds' then begin @@ -6044,6 +6187,7 @@ var prt: Word; nm: Boolean; listen: LongWord; + found: Boolean; begin // Îáùèå êîìàíäû: cmd := LowerCase(P[0]); @@ -6098,8 +6242,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 @@ -6112,8 +6255,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; @@ -6144,8 +6286,7 @@ 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 @@ -6174,8 +6315,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 @@ -6205,8 +6345,7 @@ 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]); @@ -6235,8 +6374,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 @@ -6267,8 +6405,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; end else g_Console_Add(_lc[I_MSG_SERVERONLY]); @@ -6368,7 +6505,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); @@ -6376,10 +6515,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() @@ -6452,22 +6595,26 @@ 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 @@ -6507,43 +6654,46 @@ 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; + 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 @@ -6552,91 +6702,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 @@ -6644,113 +6829,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 @@ -6805,19 +7025,6 @@ begin begin g_TakeScreenShot() end - else if cmd = 'togglechat' then - begin - g_Console_Chat_Switch(False); - gSkipFirstChar := not g_Console_Interactive() - end - else if cmd = 'toggleteamchat' then - begin - if gGameSettings.GameMode in [GM_TDM, GM_CTF] then - begin - g_Console_Chat_Switch(True); - gSkipFirstChar := not g_Console_Interactive() - end - end else if cmd = 'weapon' then begin if Length(p) = 2 then @@ -7018,47 +7225,28 @@ begin end; end; -procedure g_TakeScreenShot(); -var - a: Word; - FileName: string; - ssdir, t: string; - st: TStream; - ok: Boolean; +procedure g_TakeScreenShot; + var s: TStream; t: TDateTime; dir, date, name: String; begin if e_NoGraphics then Exit; - ssdir := GameDir+'/screenshots'; - if not findFileCI(ssdir, true) then - begin - // try to create dir + try + t := Now; + dir := e_GetWriteableDir(ScreenshotDirs); + DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t); + name := e_CatPath(dir, 'screenshot-' + date + '.png'); + s := createDiskFile(name); try - CreateDir(ssdir); + e_MakeScreenshot(s, gScreenWidth, gScreenHeight); + s.Free; + g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name])) 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(); - end; - if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)])); - break; - end; - end; + 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); @@ -7163,27 +7351,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; @@ -7748,13 +7923,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; @@ -7770,9 +7949,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'); @@ -7786,8 +7965,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); @@ -7855,4 +8034,5 @@ begin 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'); end.