1 (* Copyright (C) DooM 2D:Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.inc}
22 e_graphics
, g_phys
, g_textures
, BinEditor
;
24 function g_GetSaveName(n
: Integer): String;
25 function g_SaveGame(n
: Integer; Name
: String): Boolean;
26 function g_LoadGame(n
: Integer): Boolean;
27 procedure Obj_SaveState(o
: PObj
; var Mem
: TBinMemoryWriter
);
28 procedure Obj_LoadState(o
: PObj
; var Mem
: TBinMemoryReader
);
31 TLoadSaveHook
= procedure ();
33 procedure g_SetPreLoadHook (ahook
: TLoadSaveHook
);
34 procedure g_SetPostLoadHook (ahook
: TLoadSaveHook
);
36 procedure g_CallPreLoadHooks ();
37 procedure g_CallPostLoadHooks ();
43 g_game
, g_items
, g_map
, g_monsters
, g_triggers
,
44 g_basic
, g_main
, SysUtils
, Math
, wadreader
,
45 MAPDEF
, g_weapons
, g_player
, g_console
,
49 SAVE_SIGNATURE
= $56534644; // 'DFSV'
51 END_MARKER_STRING
= 'END';
52 PLAYER_VIEW_SIGNATURE
= $57564C50; // 'PLVW'
53 OBJ_SIGNATURE
= $4A424F5F; // '_OBJ'
57 preloadHooks
: array of TLoadSaveHook
= nil;
58 postloadHooks
: array of TLoadSaveHook
= nil;
61 procedure g_SetPreLoadHook (ahook
: TLoadSaveHook
);
63 if not assigned(ahook
) then exit
;
64 SetLength(preloadHooks
, Length(preloadHooks
)+1);
65 preloadHooks
[High(preloadHooks
)] := ahook
;
69 procedure g_SetPostLoadHook (ahook
: TLoadSaveHook
);
71 if not assigned(ahook
) then exit
;
72 SetLength(postloadHooks
, Length(postloadHooks
)+1);
73 postloadHooks
[High(postloadHooks
)] := ahook
;
77 procedure g_CallPreLoadHooks ();
81 for f
:= 0 to High(preloadHooks
) do preloadHooks
[f
]();
85 procedure g_CallPostLoadHooks ();
89 for f
:= 0 to High(postloadHooks
) do postloadHooks
[f
]();
93 procedure Obj_SaveState(o
: PObj
; var Mem
: TBinMemoryWriter
);
100 // Ñèãíàòóðà îáúåêòà:
101 sig
:= OBJ_SIGNATURE
; // '_OBJ'
103 // Ïîëîæåíèå ïî-ãîðèçîíòàëè:
105 // Ïîëîæåíèå ïî-âåðòèêàëè:
107 // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê:
108 Mem
.WriteInt(o
^.Rect
.X
);
109 Mem
.WriteInt(o
^.Rect
.Y
);
110 Mem
.WriteWord(o
^.Rect
.Width
);
111 Mem
.WriteWord(o
^.Rect
.Height
);
113 Mem
.WriteInt(o
^.Vel
.X
);
114 Mem
.WriteInt(o
^.Vel
.Y
);
115 // Ïðèáàâêà ê ñêîðîñòè:
116 Mem
.WriteInt(o
^.Accel
.X
);
117 Mem
.WriteInt(o
^.Accel
.Y
);
120 procedure Obj_LoadState(o
: PObj
; var Mem
: TBinMemoryReader
);
127 // Ñèãíàòóðà îáúåêòà:
129 if sig
<> OBJ_SIGNATURE
then // '_OBJ'
131 raise EBinSizeError
.Create('Obj_LoadState: Wrong Object Signature');
133 // Ïîëîæåíèå ïî-ãîðèçîíòàëè:
135 // Ïîëîæåíèå ïî-âåðòèêàëè:
137 // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê:
138 Mem
.ReadInt(o
^.Rect
.X
);
139 Mem
.ReadInt(o
^.Rect
.Y
);
140 Mem
.ReadWord(o
^.Rect
.Width
);
141 Mem
.ReadWord(o
^.Rect
.Height
);
143 Mem
.ReadInt(o
^.Vel
.X
);
144 Mem
.ReadInt(o
^.Vel
.Y
);
145 // Ïðèáàâêà ê ñêîðîñòè:
146 Mem
.ReadInt(o
^.Accel
.X
);
147 Mem
.ReadInt(o
^.Accel
.Y
);
150 function g_GetSaveName(n
: Integer): String;
152 bFile
: TBinFileReader
;
153 bMem
: TBinMemoryReader
;
162 // Îòêðûâàåì ôàéë ñîõðàíåíèé:
163 bFile
:= TBinFileReader
.Create();
164 if bFile
.OpenFile(DataDir
+ 'SAVGAME' + IntToStr(n
) + '.DAT',
165 SAVE_SIGNATURE
, SAVE_VERSION
) then
167 // ×èòàåì ïåðâûé áëîê - ñîñòîÿíèå èãðû:
168 bMem
:= TBinMemoryReader
.Create();
169 bFile
.ReadMemory(bMem
);
171 bMem
.ReadString(str
);
178 on E1
: EInOutError
do
179 e_WriteLog('GetSaveName I/O Error: '+E1
.Message, MSG_WARNING
);
180 on E2
: EBinSizeError
do
181 e_WriteLog('GetSaveName Size Error: '+E2
.Message, MSG_WARNING
);
190 function g_SaveGame(n
: Integer; Name
: String): Boolean;
192 bFile
: TBinFileWriter
;
193 bMem
: TBinMemoryWriter
;
205 // Ñîçäàåì ôàéë ñîõðàíåíèÿ:
206 bFile
:= TBinFileWriter
.Create();
207 bFile
.OpenFile(DataDir
+ 'SAVGAME' + IntToStr(n
) + '.DAT',
208 SAVE_SIGNATURE
, SAVE_VERSION
);
210 ///// Ïîëó÷àåì ñîñòîÿíèå èãðû: /////
211 bMem
:= TBinMemoryWriter
.Create(256);
213 bMem
.WriteString(Name
, 32);
215 str
:= gGameSettings
.WAD
;
216 bMem
.WriteString(str
, 128);
218 str
:= g_ExtractFileName(gMapInfo
.Map
);
219 bMem
.WriteString(str
, 32);
220 // Êîëè÷åñòâî èãðîêîâ:
221 nPlayers
:= g_Player_GetCount();
222 bMem
.WriteByte(nPlayers
);
224 bMem
.WriteDWORD(gTime
);
226 bMem
.WriteByte(gGameSettings
.GameType
);
228 bMem
.WriteByte(gGameSettings
.GameMode
);
230 bMem
.WriteWord(gGameSettings
.TimeLimit
);
232 bMem
.WriteWord(gGameSettings
.GoalLimit
);
234 bMem
.WriteByte(gGameSettings
.MaxLives
);
236 bMem
.WriteDWORD(gGameSettings
.Options
);
238 bMem
.WriteWord(gCoopMonstersKilled
);
239 bMem
.WriteWord(gCoopSecretsFound
);
240 bMem
.WriteWord(gCoopTotalMonstersKilled
);
241 bMem
.WriteWord(gCoopTotalSecretsFound
);
242 bMem
.WriteWord(gCoopTotalMonsters
);
243 bMem
.WriteWord(gCoopTotalSecrets
);
244 // Ñîõðàíÿåì ñîñòîÿíèå èãðû:
245 bFile
.WriteMemory(bMem
);
250 ///// Ñîõðàíÿåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: /////
251 bMem
:= TBinMemoryWriter
.Create(8);
252 sig
:= PLAYER_VIEW_SIGNATURE
;
253 bMem
.WriteDWORD(sig
); // 'PLVW'
256 if gPlayer1
<> nil then
257 PID1
:= gPlayer1
.UID
;
258 if gPlayer2
<> nil then
259 PID2
:= gPlayer2
.UID
;
260 bMem
.WriteWord(PID1
);
261 bMem
.WriteWord(PID2
);
262 bFile
.WriteMemory(bMem
);
267 ///// Ïîëó÷àåì ñîñòîÿíèå êàðòû: /////
268 g_Map_SaveState(bMem
);
269 // Ñîõðàíÿåì ñîñòîÿíèå êàðòû:
270 bFile
.WriteMemory(bMem
);
275 ///// Ïîëó÷àåì ñîñòîÿíèå ïðåäìåòîâ: /////
276 g_Items_SaveState(bMem
);
277 // Ñîõðàíÿåì ñîñòîÿíèå ïðåäìåòîâ:
278 bFile
.WriteMemory(bMem
);
283 ///// Ïîëó÷àåì ñîñòîÿíèå òðèããåðîâ: /////
284 g_Triggers_SaveState(bMem
);
285 // Ñîõðàíÿåì ñîñòîÿíèå òðèããåðîâ:
286 bFile
.WriteMemory(bMem
);
291 ///// Ïîëó÷àåì ñîñòîÿíèå îðóæèÿ: /////
292 g_Weapon_SaveState(bMem
);
293 // Ñîõðàíÿåì ñîñòîÿíèå îðóæèÿ:
294 bFile
.WriteMemory(bMem
);
299 ///// Ïîëó÷àåì ñîñòîÿíèå ìîíñòðîâ: /////
300 g_Monsters_SaveState(bMem
);
301 // Ñîõðàíÿåì ñîñòîÿíèå ìîíñòðîâ:
302 bFile
.WriteMemory(bMem
);
307 ///// Ïîëó÷àåì ñîñòîÿíèå òðóïîâ: /////
308 g_Player_Corpses_SaveState(bMem
);
309 // Ñîõðàíÿåì ñîñòîÿíèå òðóïîâ:
310 bFile
.WriteMemory(bMem
);
315 ///// Ñîõðàíÿåì èãðîêîâ (â òîì ÷èñëå áîòîâ): /////
319 for i
:= 0 to High(gPlayers
) do
320 if gPlayers
[i
] <> nil then
322 // Ïîëó÷àåì ñîñòîÿíèå èãðîêà:
323 gPlayers
[i
].SaveState(bMem
);
324 // Ñîõðàíÿåì ñîñòîÿíèå èãðîêà:
325 bFile
.WriteMemory(bMem
);
331 // Âñå ëè èãðîêè íà ìåñòå:
332 if k
<> nPlayers
then
334 raise EInOutError
.Create('g_SaveGame: Wrong Players Count');
339 ///// Ìàðêåð îêîí÷àíèÿ: /////
340 bMem
:= TBinMemoryWriter
.Create(4);
341 // Ñòðîêà - îáîçíà÷åíèå êîíöà:
342 str
:= END_MARKER_STRING
; // 'END'
343 bMem
.WriteString(str
, 3);
344 // Ñîõðàíÿåì ìàðêåð îêîí÷àíèÿ:
345 bFile
.WriteMemory(bMem
);
350 // Çàêðûâàåì ôàéë ñîõðàíåíèÿ:
355 on E1
: EInOutError
do
357 g_Console_Add(_lc
[I_GAME_ERROR_SAVE
]);
358 e_WriteLog('SaveState I/O Error: '+E1
.Message, MSG_WARNING
);
360 on E2
: EBinSizeError
do
362 g_Console_Add(_lc
[I_GAME_ERROR_SAVE
]);
363 e_WriteLog('SaveState Size Error: '+E2
.Message, MSG_WARNING
);
371 function g_LoadGame(n
: Integer): Boolean;
373 bFile
: TBinFileReader
;
374 bMem
: TBinMemoryReader
;
376 str
, WAD_Path
, Map_Name
: String;
377 nPlayers
, Game_Type
, Game_Mode
, Game_MaxLives
: Byte;
378 Game_TimeLimit
, Game_GoalLimit
: Word;
379 Game_Time
, Game_Options
: Cardinal;
380 Game_CoopMonstersKilled
,
381 Game_CoopSecretsFound
,
382 Game_CoopTotalMonstersKilled
,
383 Game_CoopTotalSecretsFound
,
384 Game_CoopTotalMonsters
,
385 Game_CoopTotalSecrets
,
394 // Îòêðûâàåì ôàéë ñ ñîõðàíåíèåì:
395 bFile
:= TBinFileReader
.Create();
396 if not bFile
.OpenFile(DataDir
+ 'SAVGAME' + IntToStr(n
) + '.DAT',
397 SAVE_SIGNATURE
, SAVE_VERSION
) then
403 e_WriteLog('Loading saved game...', MSG_NOTIFY
);
406 g_Game_ClearLoading();
407 g_Game_SetLoadingText(_lc
[I_LOAD_SAVE_FILE
], 0, False);
408 gLoadGameMode
:= True;
410 g_CallPreLoadHooks();
412 ///// Çàãðóæàåì ñîñòîÿíèå èãðû: /////
413 bMem
:= TBinMemoryReader
.Create();
414 bFile
.ReadMemory(bMem
);
416 bMem
.ReadString(str
);
418 bMem
.ReadString(WAD_Path
);
420 bMem
.ReadString(Map_Name
);
421 // Êîëè÷åñòâî èãðîêîâ:
422 bMem
.ReadByte(nPlayers
);
424 bMem
.ReadDWORD(Game_Time
);
426 bMem
.ReadByte(Game_Type
);
428 bMem
.ReadByte(Game_Mode
);
430 bMem
.ReadWord(Game_TimeLimit
);
432 bMem
.ReadWord(Game_GoalLimit
);
434 bMem
.ReadByte(Game_MaxLives
);
436 bMem
.ReadDWORD(Game_Options
);
438 bMem
.ReadWord(Game_CoopMonstersKilled
);
439 bMem
.ReadWord(Game_CoopSecretsFound
);
440 bMem
.ReadWord(Game_CoopTotalMonstersKilled
);
441 bMem
.ReadWord(Game_CoopTotalSecretsFound
);
442 bMem
.ReadWord(Game_CoopTotalMonsters
);
443 bMem
.ReadWord(Game_CoopTotalSecrets
);
444 // Cîñòîÿíèå èãðû çàãðóæåíî:
449 ///// Çàãðóæàåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: /////
450 bMem
:= TBinMemoryReader
.Create();
451 bFile
.ReadMemory(bMem
);
453 if sig
<> PLAYER_VIEW_SIGNATURE
then // 'PLVW'
455 raise EInOutError
.Create('g_LoadGame: Wrong Player View Signature');
464 ZeroMemory(@gGameSettings
, SizeOf(TGameSettings
));
467 if (Game_Type
= GT_NONE
) or (Game_Type
= GT_SINGLE
) then
470 gGameSettings
.GameType
:= GT_SINGLE
;
471 gGameSettings
.MaxLives
:= 0;
472 gGameSettings
.Options
:= gGameSettings
.Options
+ GAME_OPTION_ALLOWEXIT
;
473 gGameSettings
.Options
:= gGameSettings
.Options
+ GAME_OPTION_MONSTERS
;
474 gGameSettings
.Options
:= gGameSettings
.Options
+ GAME_OPTION_BOTVSMONSTER
;
475 gSwitchGameMode
:= GM_SINGLE
;
480 gGameSettings
.GameType
:= GT_CUSTOM
;
481 gGameSettings
.GameMode
:= Game_Mode
;
482 gSwitchGameMode
:= Game_Mode
;
483 gGameSettings
.TimeLimit
:= Game_TimeLimit
;
484 gGameSettings
.GoalLimit
:= Game_GoalLimit
;
485 gGameSettings
.MaxLives
:= IfThen(Game_Mode
= GM_CTF
, 0, Game_MaxLives
);
486 gGameSettings
.Options
:= Game_Options
;
488 g_Game_ExecuteEvent('ongamestart');
490 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
491 g_Game_SetupScreenSize();
493 // Çàãðóçêà è çàïóñê êàðòû:
494 if not g_Game_StartMap(WAD_Path
+ ':\' + Map_Name
, True) then
496 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [WAD_Path
+ ':\' + Map_Name
]));
500 // Íàñòðîéêè èãðîêîâ è áîòîâ:
503 // Óñòàíàâëèâàåì âðåìÿ:
506 gCoopMonstersKilled
:= Game_CoopMonstersKilled
;
507 gCoopSecretsFound
:= Game_CoopSecretsFound
;
508 gCoopTotalMonstersKilled
:= Game_CoopTotalMonstersKilled
;
509 gCoopTotalSecretsFound
:= Game_CoopTotalSecretsFound
;
510 gCoopTotalMonsters
:= Game_CoopTotalMonsters
;
511 gCoopTotalSecrets
:= Game_CoopTotalSecrets
;
513 ///// Çàãðóæàåì ñîñòîÿíèå êàðòû: /////
514 bMem
:= TBinMemoryReader
.Create();
515 bFile
.ReadMemory(bMem
);
517 g_Map_LoadState(bMem
);
522 ///// Çàãðóæàåì ñîñòîÿíèå ïðåäìåòîâ: /////
523 bMem
:= TBinMemoryReader
.Create();
524 bFile
.ReadMemory(bMem
);
525 // Ñîñòîÿíèå ïðåäìåòîâ:
526 g_Items_LoadState(bMem
);
531 ///// Çàãðóæàåì ñîñòîÿíèå òðèããåðîâ: /////
532 bMem
:= TBinMemoryReader
.Create();
533 bFile
.ReadMemory(bMem
);
534 // Ñîñòîÿíèå òðèããåðîâ:
535 g_Triggers_LoadState(bMem
);
540 ///// Çàãðóæàåì ñîñòîÿíèå îðóæèÿ: /////
541 bMem
:= TBinMemoryReader
.Create();
542 bFile
.ReadMemory(bMem
);
544 g_Weapon_LoadState(bMem
);
549 ///// Çàãðóæàåì ñîñòîÿíèå ìîíñòðîâ: /////
550 bMem
:= TBinMemoryReader
.Create();
551 bFile
.ReadMemory(bMem
);
552 // Ñîñòîÿíèå ìîíñòðîâ:
553 g_Monsters_LoadState(bMem
);
558 ///// Çàãðóæàåì ñîñòîÿíèå òðóïîâ: /////
559 bMem
:= TBinMemoryReader
.Create();
560 bFile
.ReadMemory(bMem
);
562 g_Player_Corpses_LoadState(bMem
);
567 ///// Çàãðóæàåì èãðîêîâ (â òîì ÷èñëå áîòîâ): /////
571 for i
:= 0 to nPlayers
-1 do
573 // Çàãðóæàåì ñîñòîÿíèå èãðîêà:
574 bMem
:= TBinMemoryReader
.Create();
575 bFile
.ReadMemory(bMem
);
576 // Ñîñòîÿíèå èãðîêà/áîòà:
577 g_Player_CreateFromState(bMem
);
582 // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà:
583 gPlayer1
:= g_Player_Get(PID1
);
584 gPlayer2
:= g_Player_Get(PID2
);
585 if gPlayer1
<> nil then
587 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
588 gPlayer1
.FPreferredTeam
:= gPlayer1Settings
.Team
;
589 gPlayer1
.FActualModelName
:= gPlayer1Settings
.Model
;
590 gPlayer1
.SetModel(gPlayer1
.FActualModelName
);
591 gPlayer1
.SetColor(gPlayer1Settings
.Color
);
593 if gPlayer2
<> nil then
595 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
596 gPlayer2
.FPreferredTeam
:= gPlayer2Settings
.Team
;
597 gPlayer2
.FActualModelName
:= gPlayer2Settings
.Model
;
598 gPlayer2
.SetModel(gPlayer2
.FActualModelName
);
599 gPlayer2
.SetColor(gPlayer2Settings
.Color
);
603 ///// Ìàðêåð îêîí÷àíèÿ: /////
604 bMem
:= TBinMemoryReader
.Create();
605 bFile
.ReadMemory(bMem
);
606 // Ñòðîêà - îáîçíà÷åíèå êîíöà:
607 bMem
.ReadString(str
);
608 if str
<> END_MARKER_STRING
then // 'END'
610 raise EInOutError
.Create('g_LoadGame: No END Marker');
612 // Ìàðêåð îêîí÷àíèÿ çàãðóæåí:
617 // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ:
618 if {(gMonsters <> nil) and} (gTriggers
<> nil) then
619 g_Map_ReAdd_DieTriggers();
621 // Çàêðûâàåì ôàéë çàãðóçêè:
623 gLoadGameMode
:= False;
624 g_CallPostLoadHooks();
628 on E1
: EInOutError
do
630 g_Console_Add(_lc
[I_GAME_ERROR_LOAD
]);
631 e_WriteLog('LoadState I/O Error: '+E1
.Message, MSG_WARNING
);
633 on E2
: EBinSizeError
do
635 g_Console_Add(_lc
[I_GAME_ERROR_LOAD
]);
636 e_WriteLog('LoadState Size Error: '+E2
.Message, MSG_WARNING
);