X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_saveload.pas;h=09f5076c9b919fc95d9f96a09f672f20193bc24b;hp=3d8c14cd669c6fed75253e9bf1f7916a79d02379;hb=a959696d242bc66e6890d281eb6e5d627c2588e9;hpb=af3c404e11867c6794975f1d45dd98932d804ede diff --git a/src/game/g_saveload.pas b/src/game/g_saveload.pas index 3d8c14c..09f5076 100644 --- a/src/game/g_saveload.pas +++ b/src/game/g_saveload.pas @@ -1,317 +1,296 @@ -{$MODE DELPHI} +(* Copyright (C) Doom 2D: Forever Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License ONLY. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *) +{$INCLUDE ../shared/a_modes.inc} unit g_saveload; interface uses - e_graphics, g_phys, g_textures, BinEditor; + SysUtils, Classes, + e_graphics, g_phys, g_textures; + + +function g_GetSaveName (n: Integer; out valid: Boolean): AnsiString; + +function g_SaveGameTo (const filename: AnsiString; const aname: AnsiString; deleteOnError: Boolean=true): Boolean; +function g_LoadGameFrom (const filename: AnsiString): Boolean; + +function g_SaveGame (n: Integer; const aname: AnsiString): Boolean; +function g_LoadGame (n: Integer): Boolean; + +procedure Obj_SaveState (st: TStream; o: PObj); +procedure Obj_LoadState (o: PObj; st: TStream); -function g_GetSaveName(n: Integer): String; -function g_SaveGame(n: Integer; Name: String): Boolean; -function g_LoadGame(n: Integer): Boolean; -procedure Obj_SaveState(o: PObj; var Mem: TBinMemoryWriter); -procedure Obj_LoadState(o: PObj; var Mem: TBinMemoryReader); implementation uses + MAPDEF, utils, xstreams, g_game, g_items, g_map, g_monsters, g_triggers, - g_basic, g_main, SysUtils, Math, wadreader, - MAPSTRUCT, MAPDEF, g_weapons, g_player, g_console, - e_log, g_language; + g_basic, g_main, Math, wadreader, + g_weapons, g_player, g_console, + e_log, e_res, g_language; const SAVE_SIGNATURE = $56534644; // 'DFSV' - SAVE_VERSION = $02; + SAVE_VERSION = $07; END_MARKER_STRING = 'END'; PLAYER_VIEW_SIGNATURE = $57564C50; // 'PLVW' OBJ_SIGNATURE = $4A424F5F; // '_OBJ' -procedure Obj_SaveState(o: PObj; var Mem: TBinMemoryWriter); -var - sig: DWORD; + +procedure Obj_SaveState (st: TStream; o: PObj); begin - if Mem = nil then - Exit; - -// Ñèãíàòóðà îáúåêòà: - sig := OBJ_SIGNATURE; // '_OBJ' - Mem.WriteDWORD(sig); -// Ïîëîæåíèå ïî-ãîðèçîíòàëè: - Mem.WriteInt(o^.X); -// Ïîëîæåíèå ïî-âåðòèêàëè: - Mem.WriteInt(o^.Y); -// Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê: - Mem.WriteInt(o^.Rect.X); - Mem.WriteInt(o^.Rect.Y); - Mem.WriteWord(o^.Rect.Width); - Mem.WriteWord(o^.Rect.Height); -// Ñêîðîñòü: - Mem.WriteInt(o^.Vel.X); - Mem.WriteInt(o^.Vel.Y); -// Ïðèáàâêà ê ñêîðîñòè: - Mem.WriteInt(o^.Accel.X); - Mem.WriteInt(o^.Accel.Y); + if (st = nil) then exit; + // Ñèãíàòóðà îáúåêòà + utils.writeSign(st, '_OBJ'); + utils.writeInt(st, Byte(0)); // version + // Ïîëîæåíèå ïî-ãîðèçîíòàëè + utils.writeInt(st, LongInt(o^.X)); + // Ïîëîæåíèå ïî-âåðòèêàëè + utils.writeInt(st, LongInt(o^.Y)); + // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê + utils.writeInt(st, LongInt(o^.Rect.X)); + utils.writeInt(st, LongInt(o^.Rect.Y)); + utils.writeInt(st, Word(o^.Rect.Width)); + utils.writeInt(st, Word(o^.Rect.Height)); + // Ñêîðîñòü + utils.writeInt(st, LongInt(o^.Vel.X)); + utils.writeInt(st, LongInt(o^.Vel.Y)); + // Óñêîðåíèå + utils.writeInt(st, LongInt(o^.Accel.X)); + utils.writeInt(st, LongInt(o^.Accel.Y)); end; -procedure Obj_LoadState(o: PObj; var Mem: TBinMemoryReader); -var - sig: DWORD; + +procedure Obj_LoadState (o: PObj; st: TStream); begin - if Mem = nil then - Exit; - -// Ñèãíàòóðà îáúåêòà: - Mem.ReadDWORD(sig); - if sig <> OBJ_SIGNATURE then // '_OBJ' - begin - raise EBinSizeError.Create('Obj_LoadState: Wrong Object Signature'); - end; -// Ïîëîæåíèå ïî-ãîðèçîíòàëè: - Mem.ReadInt(o^.X); -// Ïîëîæåíèå ïî-âåðòèêàëè: - Mem.ReadInt(o^.Y); -// Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê: - Mem.ReadInt(o^.Rect.X); - Mem.ReadInt(o^.Rect.Y); - Mem.ReadWord(o^.Rect.Width); - Mem.ReadWord(o^.Rect.Height); -// Ñêîðîñòü: - Mem.ReadInt(o^.Vel.X); - Mem.ReadInt(o^.Vel.Y); -// Ïðèáàâêà ê ñêîðîñòè: - Mem.ReadInt(o^.Accel.X); - Mem.ReadInt(o^.Accel.Y); + if (st = nil) then exit; + // Ñèãíàòóðà îáúåêòà: + if not utils.checkSign(st, '_OBJ') then raise XStreamError.Create('invalid object signature'); + if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid object version'); + // Ïîëîæåíèå ïî-ãîðèçîíòàëè + o^.X := utils.readLongInt(st); + // Ïîëîæåíèå ïî-âåðòèêàëè + o^.Y := utils.readLongInt(st); + // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê + o^.Rect.X := utils.readLongInt(st); + o^.Rect.Y := utils.readLongInt(st); + o^.Rect.Width := utils.readWord(st); + o^.Rect.Height := utils.readWord(st); + // Ñêîðîñòü + o^.Vel.X := utils.readLongInt(st); + o^.Vel.Y := utils.readLongInt(st); + // Óñêîðåíèå + o^.Accel.X := utils.readLongInt(st); + o^.Accel.Y := utils.readLongInt(st); end; -function g_GetSaveName(n: Integer): String; -var - bFile: TBinFileReader; - bMem: TBinMemoryReader; - str: String; + +function buildSaveName (n: Integer): AnsiString; begin - Result := ''; - str := ''; - bMem := nil; - bFile := nil; + result := 'SAVGAME' + IntToStr(n) + '.DAT' +end; + +function g_GetSaveName (n: Integer; out valid: Boolean): AnsiString; +var + st: TStream = nil; + ver: Byte; + stlen: Word; + filename: AnsiString; +begin + valid := false; + result := ''; + if (n < 0) or (n > 65535) then exit; try - // Îòêðûâàåì ôàéë ñîõðàíåíèé: - bFile := TBinFileReader.Create(); - if bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT', - SAVE_SIGNATURE, SAVE_VERSION) then - begin - // ×èòàåì ïåðâûé áëîê - ñîñòîÿíèå èãðû: - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Èìÿ èãðû: - bMem.ReadString(str); - - // Çàêðûâàåì ôàéë: - bFile.Close(); + // Îòêðûâàåì ôàéë ñîõðàíåíèé + filename := buildSaveName(n); + st := e_OpenResourceRO(SaveDirs, filename); + try + if not utils.checkSign(st, 'DFSV') then + begin + e_LogWritefln('GetSaveName: not a save file: ''%s''', [st], TMsgType.Warning); + //raise XStreamError.Create('invalid save game signature'); + exit; + end; + ver := utils.readByte(st); + if (ver < 7) then + begin + utils.readLongWord(st); // section size + stlen := utils.readWord(st); + if (stlen < 1) or (stlen > 64) then + begin + e_LogWritefln('GetSaveName: not a save file: ''%s''', [st], TMsgType.Warning); + //raise XStreamError.Create('invalid save game version'); + exit; + end; + // Èìÿ ñýéâà + SetLength(result, stlen); + st.ReadBuffer(result[1], stlen); + end + else + begin + // 7+ + // Èìÿ ñýéâà + result := utils.readStr(st, 64); + end; + valid := (ver = SAVE_VERSION); + //if (utils.readByte(st) <> SAVE_VERSION) then raise XStreamError.Create('invalid save game version'); + finally + st.Free(); end; - except - on E1: EInOutError do - e_WriteLog('GetSaveName I/O Error: '+E1.Message, MSG_WARNING); - on E2: EBinSizeError do - e_WriteLog('GetSaveName Size Error: '+E2.Message, MSG_WARNING); + begin + //e_WriteLog('GetSaveName Error: '+e.message, MSG_WARNING); + //{$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF} + result := ''; + end; end; - - bMem.Free(); - bFile.Free(); - - Result := str; end; -function g_SaveGame(n: Integer; Name: String): Boolean; + +function g_SaveGameTo (const filename: AnsiString; const aname: AnsiString; deleteOnError: Boolean=true): Boolean; var - bFile: TBinFileWriter; - bMem: TBinMemoryWriter; - sig: DWORD; - str: String; - nPlayers: Byte; + st: TStream = nil; i, k: Integer; PID1, PID2: Word; begin - Result := False; - bMem := nil; - bFile := nil; - + result := false; try - // Ñîçäàåì ôàéë ñîõðàíåíèÿ: - bFile := TBinFileWriter.Create(); - bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT', - SAVE_SIGNATURE, SAVE_VERSION); - - ///// Ïîëó÷àåì ñîñòîÿíèå èãðû: ///// - bMem := TBinMemoryWriter.Create(256); - // Èìÿ èãðû: - bMem.WriteString(Name, 32); - // Ïóòü ê êàðòå: - str := gGameSettings.WAD; - bMem.WriteString(str, 128); - // Èìÿ êàðòû: - str := g_ExtractFileName(gMapInfo.Map); - bMem.WriteString(str, 32); - // Êîëè÷åñòâî èãðîêîâ: - nPlayers := g_Player_GetCount(); - bMem.WriteByte(nPlayers); - // Èãðîâîå âðåìÿ: - bMem.WriteDWORD(gTime); - // Òèï èãðû: - bMem.WriteByte(gGameSettings.GameType); - // Ðåæèì èãðû: - bMem.WriteByte(gGameSettings.GameMode); - // Ëèìèò âðåìåíè: - bMem.WriteWord(gGameSettings.TimeLimit); - // Ëèìèò î÷êîâ: - bMem.WriteWord(gGameSettings.GoalLimit); - // Ëèìèò æèçíåé: - bMem.WriteByte(gGameSettings.MaxLives); - // Èãðîâûå îïöèè: - bMem.WriteDWORD(gGameSettings.Options); - // Äëÿ êîîïà: - bMem.WriteWord(gCoopMonstersKilled); - bMem.WriteWord(gCoopSecretsFound); - bMem.WriteWord(gCoopTotalMonstersKilled); - bMem.WriteWord(gCoopTotalSecretsFound); - bMem.WriteWord(gCoopTotalMonsters); - bMem.WriteWord(gCoopTotalSecrets); - // Ñîõðàíÿåì ñîñòîÿíèå èãðû: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ñîõðàíÿåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: ///// - bMem := TBinMemoryWriter.Create(8); - sig := PLAYER_VIEW_SIGNATURE; - bMem.WriteDWORD(sig); // 'PLVW' - PID1 := 0; - PID2 := 0; - if gPlayer1 <> nil then - PID1 := gPlayer1.UID; - if gPlayer2 <> nil then - PID2 := gPlayer2.UID; - bMem.WriteWord(PID1); - bMem.WriteWord(PID2); - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ïîëó÷àåì ñîñòîÿíèå êàðòû: ///// - g_Map_SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå êàðòû: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ïîëó÷àåì ñîñòîÿíèå ïðåäìåòîâ: ///// - g_Items_SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå ïðåäìåòîâ: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ïîëó÷àåì ñîñòîÿíèå òðèããåðîâ: ///// - g_Triggers_SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå òðèããåðîâ: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ïîëó÷àåì ñîñòîÿíèå îðóæèÿ: ///// - g_Weapon_SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå îðóæèÿ: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ïîëó÷àåì ñîñòîÿíèå ìîíñòðîâ: ///// - g_Monsters_SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå ìîíñòðîâ: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ïîëó÷àåì ñîñòîÿíèå òðóïîâ: ///// - g_Player_Corpses_SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå òðóïîâ: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Ñîõðàíÿåì èãðîêîâ (â òîì ÷èñëå áîòîâ): ///// - if nPlayers > 0 then - begin - k := 0; - for i := 0 to High(gPlayers) do - if gPlayers[i] <> nil then + st := e_CreateResource(SaveDirs, filename); + try + utils.writeSign(st, 'DFSV'); + utils.writeInt(st, Byte(SAVE_VERSION)); + // Èìÿ ñýéâà + utils.writeStr(st, aname, 64); + // Ïîëíûé ïóòü ê âàäó è êàðòà + //if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('SAVE: current map is ''%s''...', [gCurrentMapFileName]); + utils.writeStr(st, gCurrentMapFileName); + // Ïóòü ê êàðòå + utils.writeStr(st, gGameSettings.WAD); + // Èìÿ êàðòû + utils.writeStr(st, g_ExtractFileName(gMapInfo.Map)); + // Êîëè÷åñòâî èãðîêîâ + utils.writeInt(st, Word(g_Player_GetCount)); + // Èãðîâîå âðåìÿ + utils.writeInt(st, LongWord(gTime)); + // Òèï èãðû + utils.writeInt(st, Byte(gGameSettings.GameType)); + // Ðåæèì èãðû + utils.writeInt(st, Byte(gGameSettings.GameMode)); + // Ëèìèò âðåìåíè + utils.writeInt(st, Word(gGameSettings.TimeLimit)); + // Ëèìèò î÷êîâ + utils.writeInt(st, Word(gGameSettings.GoalLimit)); + // Ëèìèò æèçíåé + utils.writeInt(st, Byte(gGameSettings.MaxLives)); + // Èãðîâûå îïöèè + utils.writeInt(st, LongWord(gGameSettings.Options)); + // Äëÿ êîîïà + utils.writeInt(st, Word(gCoopMonstersKilled)); + utils.writeInt(st, Word(gCoopSecretsFound)); + utils.writeInt(st, Word(gCoopTotalMonstersKilled)); + utils.writeInt(st, Word(gCoopTotalSecretsFound)); + utils.writeInt(st, Word(gCoopTotalMonsters)); + utils.writeInt(st, Word(gCoopTotalSecrets)); + + ///// Ñîõðàíÿåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà ///// + utils.writeSign(st, 'PLVW'); + utils.writeInt(st, Byte(0)); // version + PID1 := 0; + PID2 := 0; + if (gPlayer1 <> nil) then PID1 := gPlayer1.UID; + if (gPlayer2 <> nil) then PID2 := gPlayer2.UID; + utils.writeInt(st, Word(PID1)); + utils.writeInt(st, Word(PID2)); + ///// ///// + + ///// Ñîñòîÿíèå êàðòû ///// + g_Map_SaveState(st); + ///// ///// + + ///// Ñîñòîÿíèå ïðåäìåòîâ ///// + g_Items_SaveState(st); + ///// ///// + + ///// Ñîñòîÿíèå òðèããåðîâ ///// + g_Triggers_SaveState(st); + ///// ///// + + ///// Ñîñòîÿíèå îðóæèÿ ///// + g_Weapon_SaveState(st); + ///// ///// + + ///// Ñîñòîÿíèå ìîíñòðîâ ///// + g_Monsters_SaveState(st); + ///// ///// + + ///// Ñîñòîÿíèå òðóïîâ ///// + g_Player_Corpses_SaveState(st); + ///// ///// + + ///// Ñîõðàíÿåì èãðîêîâ (â òîì ÷èñëå áîòîâ) ///// + if (g_Player_GetCount > 0) then + begin + k := 0; + for i := 0 to High(gPlayers) do begin - // Ïîëó÷àåì ñîñòîÿíèå èãðîêà: - gPlayers[i].SaveState(bMem); - // Ñîõðàíÿåì ñîñòîÿíèå èãðîêà: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - Inc(k); + if (gPlayers[i] <> nil) then + begin + // Ñîñòîÿíèå èãðîêà + gPlayers[i].SaveState(st); + Inc(k); + end; end; - // Âñå ëè èãðîêè íà ìåñòå: - if k <> nPlayers then - begin - raise EInOutError.Create('g_SaveGame: Wrong Players Count'); + // Âñå ëè èãðîêè íà ìåñòå + if (k <> g_Player_GetCount) then raise XStreamError.Create('g_SaveGame: wrong players count'); end; + ///// ///// + + ///// Ìàðêåð îêîí÷àíèÿ ///// + utils.writeSign(st, 'END'); + utils.writeInt(st, Byte(0)); + ///// ///// + result := true; + finally + st.Free(); end; - ///// ///// - - ///// Ìàðêåð îêîí÷àíèÿ: ///// - bMem := TBinMemoryWriter.Create(4); - // Ñòðîêà - îáîçíà÷åíèå êîíöà: - str := END_MARKER_STRING; // 'END' - bMem.WriteString(str, 3); - // Ñîõðàíÿåì ìàðêåð îêîí÷àíèÿ: - bFile.WriteMemory(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - // Çàêðûâàåì ôàéë ñîõðàíåíèÿ: - bFile.Close(); - Result := True; except - on E1: EInOutError do - begin - g_Console_Add(_lc[I_GAME_ERROR_SAVE]); - e_WriteLog('SaveState I/O Error: '+E1.Message, MSG_WARNING); - end; - on E2: EBinSizeError do + on e: Exception do begin + st.Free(); g_Console_Add(_lc[I_GAME_ERROR_SAVE]); - e_WriteLog('SaveState Size Error: '+E2.Message, MSG_WARNING); + e_WriteLog('SaveState Error: '+e.message, TMsgType.Warning); + if deleteOnError then DeleteFile(filename); + {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF} +e_WriteStackTrace(e.message); + result := false; end; end; - - bMem.Free(); - bFile.Free(); end; -function g_LoadGame(n: Integer): Boolean; + +function g_LoadGameFrom (const filename: AnsiString): Boolean; var - bFile: TBinFileReader; - bMem: TBinMemoryReader; - sig: DWORD; - str, WAD_Path, Map_Name: String; - nPlayers, Game_Type, Game_Mode, Game_MaxLives: Byte; + st: TStream = nil; + WAD_Path, Map_Name: AnsiString; + nPlayers: Integer; + Game_Type, Game_Mode, Game_MaxLives: Byte; Game_TimeLimit, Game_GoalLimit: Word; Game_Time, Game_Options: Cardinal; Game_CoopMonstersKilled, @@ -322,257 +301,241 @@ var Game_CoopTotalSecrets, PID1, PID2: Word; i: Integer; + gameCleared: Boolean = false; + curmapfile: AnsiString = ''; + {$IF DEFINED(D2F_DEBUG)} + errpos: LongWord = 0; + {$ENDIF} begin - Result := False; - bMem := nil; - bFile := nil; + result := false; try - // Îòêðûâàåì ôàéë ñ ñîõðàíåíèåì: - bFile := TBinFileReader.Create(); - if not bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT', - SAVE_SIGNATURE, SAVE_VERSION) then - begin - bFile.Free(); - Exit; - end; + st := e_OpenResourceRO(SaveDirs, filename); + try + if not utils.checkSign(st, 'DFSV') then raise XStreamError.Create('invalid save game signature'); + if (utils.readByte(st) <> SAVE_VERSION) then raise XStreamError.Create('invalid save game version'); + + e_WriteLog('Loading saved game...', TMsgType.Notify); + + {$IF DEFINED(D2F_DEBUG)}try{$ENDIF} + //g_Game_Free(false); // don't free textures for the same map + g_Game_ClearLoading(); + g_Game_SetLoadingText(_lc[I_LOAD_SAVE_FILE], 0, False); + gLoadGameMode := True; + + ///// Çàãðóæàåì ñîñòîÿíèå èãðû ///// + // Èìÿ ñýéâà + {str :=} utils.readStr(st, 64); + + // Ïîëíûé ïóòü ê âàäó è êàðòà + curmapfile := utils.readStr(st); + + if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('LOAD: previous map was ''%s''...', [gCurrentMapFileName]); + if (Length(curmapfile) <> 0) then e_LogWritefln('LOAD: new map is ''%s''...', [curmapfile]); + // À âîò òóò, íàêîíåö, ÷èñòèì ðåñóðñû + g_Game_Free(curmapfile <> gCurrentMapFileName); // don't free textures for the same map + gameCleared := true; + + // Ïóòü ê êàðòå + WAD_Path := utils.readStr(st); + // Èìÿ êàðòû + Map_Name := utils.readStr(st); + // Êîëè÷åñòâî èãðîêîâ + nPlayers := utils.readWord(st); + // Èãðîâîå âðåìÿ + Game_Time := utils.readLongWord(st); + // Òèï èãðû + Game_Type := utils.readByte(st); + // Ðåæèì èãðû + Game_Mode := utils.readByte(st); + // Ëèìèò âðåìåíè + Game_TimeLimit := utils.readWord(st); + // Ëèìèò î÷êîâ + Game_GoalLimit := utils.readWord(st); + // Ëèìèò æèçíåé + Game_MaxLives := utils.readByte(st); + // Èãðîâûå îïöèè + Game_Options := utils.readLongWord(st); + // Äëÿ êîîïà + Game_CoopMonstersKilled := utils.readWord(st); + Game_CoopSecretsFound := utils.readWord(st); + Game_CoopTotalMonstersKilled := utils.readWord(st); + Game_CoopTotalSecretsFound := utils.readWord(st); + Game_CoopTotalMonsters := utils.readWord(st); + Game_CoopTotalSecrets := utils.readWord(st); + ///// ///// + + ///// Çàãðóæàåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà ///// + if not utils.checkSign(st, 'PLVW') then raise XStreamError.Create('invalid viewport signature'); + if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid viewport version'); + PID1 := utils.readWord(st); + PID2 := utils.readWord(st); + ///// ///// + + // Çàãðóæàåì êàðòó: + ZeroMemory(@gGameSettings, sizeof(TGameSettings)); + gAimLine := false; + gShowMap := false; + if (Game_Type = GT_NONE) or (Game_Type = GT_SINGLE) then + begin + // Íàñòðîéêè èãðû + gGameSettings.GameType := GT_SINGLE; + gGameSettings.MaxLives := 0; + gGameSettings.Options := gGameSettings.Options+GAME_OPTION_ALLOWEXIT; + gGameSettings.Options := gGameSettings.Options+GAME_OPTION_MONSTERS; + gGameSettings.Options := gGameSettings.Options+GAME_OPTION_BOTVSMONSTER; + gSwitchGameMode := GM_SINGLE; + end + else + begin + // Íàñòðîéêè èãðû + gGameSettings.GameType := GT_CUSTOM; + gGameSettings.GameMode := Game_Mode; + gSwitchGameMode := Game_Mode; + gGameSettings.TimeLimit := Game_TimeLimit; + gGameSettings.GoalLimit := Game_GoalLimit; + gGameSettings.MaxLives := IfThen(Game_Mode = GM_CTF, 0, Game_MaxLives); + gGameSettings.Options := Game_Options; + end; + g_Game_ExecuteEvent('ongamestart'); - e_WriteLog('Loading saved game...', MSG_NOTIFY); - g_Game_Free(); - - g_Game_ClearLoading(); - g_Game_SetLoadingText(_lc[I_LOAD_SAVE_FILE], 0, False); - gLoadGameMode := True; - - ///// Çàãðóæàåì ñîñòîÿíèå èãðû: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Èìÿ èãðû: - bMem.ReadString(str); - // Ïóòü ê êàðòå: - bMem.ReadString(WAD_Path); - // Èìÿ êàðòû: - bMem.ReadString(Map_Name); - // Êîëè÷åñòâî èãðîêîâ: - bMem.ReadByte(nPlayers); - // Èãðîâîå âðåìÿ: - bMem.ReadDWORD(Game_Time); - // Òèï èãðû: - bMem.ReadByte(Game_Type); - // Ðåæèì èãðû: - bMem.ReadByte(Game_Mode); - // Ëèìèò âðåìåíè: - bMem.ReadWord(Game_TimeLimit); - // Ëèìèò î÷êîâ: - bMem.ReadWord(Game_GoalLimit); - // Ëèìèò æèçíåé: - bMem.ReadByte(Game_MaxLives); - // Èãðîâûå îïöèè: - bMem.ReadDWORD(Game_Options); - // Äëÿ êîîïà: - bMem.ReadWord(Game_CoopMonstersKilled); - bMem.ReadWord(Game_CoopSecretsFound); - bMem.ReadWord(Game_CoopTotalMonstersKilled); - bMem.ReadWord(Game_CoopTotalSecretsFound); - bMem.ReadWord(Game_CoopTotalMonsters); - bMem.ReadWord(Game_CoopTotalSecrets); - // Cîñòîÿíèå èãðû çàãðóæåíî: - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - bMem.ReadDWORD(sig); - if sig <> PLAYER_VIEW_SIGNATURE then // 'PLVW' - begin - raise EInOutError.Create('g_LoadGame: Wrong Player View Signature'); - end; - bMem.ReadWord(PID1); - bMem.ReadWord(PID2); - bMem.Free(); - bMem := nil; - ///// ///// - - // Çàãðóæàåì êàðòó: - ZeroMemory(@gGameSettings, SizeOf(TGameSettings)); - gAimLine := False; - gShowMap := False; - if (Game_Type = GT_NONE) or (Game_Type = GT_SINGLE) then - begin - // Íàñòðîéêè èãðû: - gGameSettings.GameType := GT_SINGLE; - gGameSettings.MaxLives := 0; - gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT; - gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS; - gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER; - gSwitchGameMode := GM_SINGLE; - end - else - begin - // Íàñòðîéêè èãðû: - gGameSettings.GameType := GT_CUSTOM; - gGameSettings.GameMode := Game_Mode; - gSwitchGameMode := Game_Mode; - gGameSettings.TimeLimit := Game_TimeLimit; - gGameSettings.GoalLimit := Game_GoalLimit; - gGameSettings.MaxLives := IfThen(Game_Mode = GM_CTF, 0, Game_MaxLives); - gGameSettings.Options := Game_Options; - end; - g_Game_ExecuteEvent('ongamestart'); + // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ + g_Game_SetupScreenSize(); - // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ: - g_Game_SetupScreenSize(); + // Çàãðóçêà è çàïóñê êàðòû + //FIXME: save/load `asMegawad` + if not g_Game_StartMap(false{asMegawad}, WAD_Path+':\'+Map_Name, True, curmapfile) then + begin + g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WAD_Path + ':\' + Map_Name])); + exit; + end; - // Çàãðóçêà è çàïóñê êàðòû: - if not g_Game_StartMap(WAD_Path + ':\' + Map_Name, True) then - begin - g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WAD_Path + ':\' + Map_Name])); - Exit; - end; + // Íàñòðîéêè èãðîêîâ è áîòîâ + g_Player_Init(); + + // Óñòàíàâëèâàåì âðåìÿ + gTime := Game_Time; + // Âîçâðàùàåì ñòàòû + gCoopMonstersKilled := Game_CoopMonstersKilled; + gCoopSecretsFound := Game_CoopSecretsFound; + gCoopTotalMonstersKilled := Game_CoopTotalMonstersKilled; + gCoopTotalSecretsFound := Game_CoopTotalSecretsFound; + gCoopTotalMonsters := Game_CoopTotalMonsters; + gCoopTotalSecrets := Game_CoopTotalSecrets; + + ///// Çàãðóæàåì ñîñòîÿíèå êàðòû ///// + g_Map_LoadState(st); + ///// ///// + + ///// Çàãðóæàåì ñîñòîÿíèå ïðåäìåòîâ ///// + g_Items_LoadState(st); + ///// ///// + + ///// Çàãðóæàåì ñîñòîÿíèå òðèããåðîâ ///// + g_Triggers_LoadState(st); + ///// ///// + + ///// Çàãðóæàåì ñîñòîÿíèå îðóæèÿ ///// + g_Weapon_LoadState(st); + ///// ///// + + ///// Çàãðóæàåì ñîñòîÿíèå ìîíñòðîâ ///// + g_Monsters_LoadState(st); + ///// ///// + + ///// Çàãðóæàåì ñîñòîÿíèå òðóïîâ ///// + g_Player_Corpses_LoadState(st); + ///// ///// + + ///// Çàãðóæàåì èãðîêîâ (â òîì ÷èñëå áîòîâ) ///// + if nPlayers > 0 then + begin + // Çàãðóæàåì + for i := 0 to nPlayers-1 do g_Player_CreateFromState(st); + end; - // Íàñòðîéêè èãðîêîâ è áîòîâ: - g_Player_Init(); - - // Óñòàíàâëèâàåì âðåìÿ: - gTime := Game_Time; - // Âîçâðàùàåì ñòàòû: - gCoopMonstersKilled := Game_CoopMonstersKilled; - gCoopSecretsFound := Game_CoopSecretsFound; - gCoopTotalMonstersKilled := Game_CoopTotalMonstersKilled; - gCoopTotalSecretsFound := Game_CoopTotalSecretsFound; - gCoopTotalMonsters := Game_CoopTotalMonsters; - gCoopTotalSecrets := Game_CoopTotalSecrets; - - ///// Çàãðóæàåì ñîñòîÿíèå êàðòû: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå êàðòû: - g_Map_LoadState(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì ñîñòîÿíèå ïðåäìåòîâ: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå ïðåäìåòîâ: - g_Items_LoadState(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì ñîñòîÿíèå òðèããåðîâ: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå òðèããåðîâ: - g_Triggers_LoadState(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì ñîñòîÿíèå îðóæèÿ: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå îðóæèÿ: - g_Weapon_LoadState(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì ñîñòîÿíèå ìîíñòðîâ: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå ìîíñòðîâ: - g_Monsters_LoadState(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì ñîñòîÿíèå òðóïîâ: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå òðóïîâ: - g_Player_Corpses_LoadState(bMem); - bMem.Free(); - bMem := nil; - ///// ///// - - ///// Çàãðóæàåì èãðîêîâ (â òîì ÷èñëå áîòîâ): ///// - if nPlayers > 0 then - begin - // Çàãðóæàåì: - for i := 0 to nPlayers-1 do - begin - // Çàãðóæàåì ñîñòîÿíèå èãðîêà: - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñîñòîÿíèå èãðîêà/áîòà: - g_Player_CreateFromState(bMem); - bMem.Free(); - bMem := nil; - end; - end; - // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà: - gPlayer1 := g_Player_Get(PID1); - gPlayer2 := g_Player_Get(PID2); - if gPlayer1 <> nil then - begin - gPlayer1.Name := gPlayer1Settings.Name; - gPlayer1.FPreferredTeam := gPlayer1Settings.Team; - gPlayer1.FActualModelName := gPlayer1Settings.Model; - gPlayer1.SetModel(gPlayer1.FActualModelName); - gPlayer1.SetColor(gPlayer1Settings.Color); - end; - if gPlayer2 <> nil then - begin - gPlayer2.Name := gPlayer2Settings.Name; - gPlayer2.FPreferredTeam := gPlayer2Settings.Team; - gPlayer2.FActualModelName := gPlayer2Settings.Model; - gPlayer2.SetModel(gPlayer2.FActualModelName); - gPlayer2.SetColor(gPlayer2Settings.Color); - end; - ///// ///// - - ///// Ìàðêåð îêîí÷àíèÿ: ///// - bMem := TBinMemoryReader.Create(); - bFile.ReadMemory(bMem); - // Ñòðîêà - îáîçíà÷åíèå êîíöà: - bMem.ReadString(str); - if str <> END_MARKER_STRING then // 'END' - begin - raise EInOutError.Create('g_LoadGame: No END Marker'); - end; - // Ìàðêåð îêîí÷àíèÿ çàãðóæåí: - bMem.Free(); - bMem := nil; - ///// ///// + // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà + gPlayer1 := g_Player_Get(PID1); + gPlayer2 := g_Player_Get(PID2); + + if (gPlayer1 <> nil) then + begin + gPlayer1.Name := gPlayer1Settings.Name; + gPlayer1.FPreferredTeam := gPlayer1Settings.Team; + gPlayer1.FActualModelName := gPlayer1Settings.Model; + gPlayer1.SetModel(gPlayer1.FActualModelName); + gPlayer1.SetColor(gPlayer1Settings.Color); + end; - // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ: - if (gMonsters <> nil) and (gTriggers <> nil) then - g_Map_ReAdd_DieTriggers(); + if (gPlayer2 <> nil) then + begin + gPlayer2.Name := gPlayer2Settings.Name; + gPlayer2.FPreferredTeam := gPlayer2Settings.Team; + gPlayer2.FActualModelName := gPlayer2Settings.Model; + gPlayer2.SetModel(gPlayer2.FActualModelName); + gPlayer2.SetColor(gPlayer2Settings.Color); + end; + ///// ///// + + ///// Ìàðêåð îêîí÷àíèÿ ///// + if not utils.checkSign(st, 'END') then raise XStreamError.Create('no end marker'); + if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid end marker'); + ///// ///// - // Çàêðûâàåì ôàéë çàãðóçêè: - bFile.Close(); - gLoadGameMode := False; - Result := True; + // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ + if (gTriggers <> nil) then g_Map_ReAdd_DieTriggers(); + // done + gLoadGameMode := false; + result := true; + {$IF DEFINED(D2F_DEBUG)} + except + begin + errpos := LongWord(st.position); + raise; + end; + end; + {$ENDIF} + finally + st.Free(); + end; except - on E1: EInOutError do + on e: EFileNotFoundException do begin g_Console_Add(_lc[I_GAME_ERROR_LOAD]); - e_WriteLog('LoadState I/O Error: '+E1.Message, MSG_WARNING); + g_Console_Add('LoadState Error: '+e.message); + e_WriteLog('LoadState Error: '+e.message, TMsgType.Warning); + gLoadGameMode := false; + result := false; end; - on E2: EBinSizeError do + on e: Exception do begin g_Console_Add(_lc[I_GAME_ERROR_LOAD]); - e_WriteLog('LoadState Size Error: '+E2.Message, MSG_WARNING); + g_Console_Add('LoadState Error: '+e.message); + e_WriteLog('LoadState Error: '+e.message, TMsgType.Warning); + {$IF DEFINED(D2F_DEBUG)}e_LogWritefln('stream error position: 0x%08x', [errpos], TMsgType.Warning);{$ENDIF} + gLoadGameMode := false; + result := false; + if gState <> STATE_MENU then + g_FatalError(_lc[I_GAME_ERROR_LOAD]) + else if not gameCleared then + g_Game_Free(); + {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF} end; end; +end; + + +function g_SaveGame (n: Integer; const aname: AnsiString): Boolean; +begin + result := g_SaveGameTo(buildSaveName(n), aname, true); +end; + - bMem.Free(); - bFile.Free(); +function g_LoadGame (n: Integer): Boolean; +begin + result := g_LoadGameFrom(buildSaveName(n)); end; + end.