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
;
25 function g_GetSaveName(n
: Integer): String;
26 function g_SaveGame(n
: Integer; Name
: String): Boolean;
27 function g_LoadGame(n
: Integer): Boolean;
28 procedure Obj_SaveState(o
: PObj
; var Mem
: TBinMemoryWriter
);
29 procedure Obj_LoadState(o
: PObj
; var Mem
: TBinMemoryReader
);
35 g_game
, g_items
, g_map
, g_monsters
, g_triggers
,
36 g_basic
, g_main
, SysUtils
, Math
, wadreader
,
37 MAPDEF
, g_weapons
, g_player
, g_console
,
41 SAVE_SIGNATURE
= $56534644; // 'DFSV'
43 END_MARKER_STRING
= 'END';
44 PLAYER_VIEW_SIGNATURE
= $57564C50; // 'PLVW'
45 OBJ_SIGNATURE
= $4A424F5F; // '_OBJ'
48 procedure Obj_SaveState(o
: PObj
; var Mem
: TBinMemoryWriter
);
56 sig
:= OBJ_SIGNATURE
; // '_OBJ'
58 // Ïîëîæåíèå ïî-ãîðèçîíòàëè:
60 // Ïîëîæåíèå ïî-âåðòèêàëè:
62 // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê:
63 Mem
.WriteInt(o
^.Rect
.X
);
64 Mem
.WriteInt(o
^.Rect
.Y
);
65 Mem
.WriteWord(o
^.Rect
.Width
);
66 Mem
.WriteWord(o
^.Rect
.Height
);
68 Mem
.WriteInt(o
^.Vel
.X
);
69 Mem
.WriteInt(o
^.Vel
.Y
);
70 // Ïðèáàâêà ê ñêîðîñòè:
71 Mem
.WriteInt(o
^.Accel
.X
);
72 Mem
.WriteInt(o
^.Accel
.Y
);
75 procedure Obj_LoadState(o
: PObj
; var Mem
: TBinMemoryReader
);
84 if sig
<> OBJ_SIGNATURE
then // '_OBJ'
86 raise EBinSizeError
.Create('Obj_LoadState: Wrong Object Signature');
88 // Ïîëîæåíèå ïî-ãîðèçîíòàëè:
90 // Ïîëîæåíèå ïî-âåðòèêàëè:
92 // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê:
93 Mem
.ReadInt(o
^.Rect
.X
);
94 Mem
.ReadInt(o
^.Rect
.Y
);
95 Mem
.ReadWord(o
^.Rect
.Width
);
96 Mem
.ReadWord(o
^.Rect
.Height
);
98 Mem
.ReadInt(o
^.Vel
.X
);
99 Mem
.ReadInt(o
^.Vel
.Y
);
100 // Ïðèáàâêà ê ñêîðîñòè:
101 Mem
.ReadInt(o
^.Accel
.X
);
102 Mem
.ReadInt(o
^.Accel
.Y
);
105 function g_GetSaveName(n
: Integer): String;
107 bFile
: TBinFileReader
;
108 bMem
: TBinMemoryReader
;
117 // Îòêðûâàåì ôàéë ñîõðàíåíèé:
118 bFile
:= TBinFileReader
.Create();
119 if bFile
.OpenFile(DataDir
+ 'SAVGAME' + IntToStr(n
) + '.DAT',
120 SAVE_SIGNATURE
, SAVE_VERSION
) then
122 // ×èòàåì ïåðâûé áëîê - ñîñòîÿíèå èãðû:
123 bMem
:= TBinMemoryReader
.Create();
124 bFile
.ReadMemory(bMem
);
126 bMem
.ReadString(str
);
133 on E1
: EInOutError
do
134 e_WriteLog('GetSaveName I/O Error: '+E1
.Message, MSG_WARNING
);
135 on E2
: EBinSizeError
do
136 e_WriteLog('GetSaveName Size Error: '+E2
.Message, MSG_WARNING
);
145 function g_SaveGame(n
: Integer; Name
: String): Boolean;
147 bFile
: TBinFileWriter
;
148 bMem
: TBinMemoryWriter
;
160 // Ñîçäàåì ôàéë ñîõðàíåíèÿ:
161 bFile
:= TBinFileWriter
.Create();
162 bFile
.OpenFile(DataDir
+ 'SAVGAME' + IntToStr(n
) + '.DAT',
163 SAVE_SIGNATURE
, SAVE_VERSION
);
165 ///// Ïîëó÷àåì ñîñòîÿíèå èãðû: /////
166 bMem
:= TBinMemoryWriter
.Create(256);
168 bMem
.WriteString(Name
, 32);
169 // Ïîëíûé ïóòü ê âàäó è êàðòà
170 if (Length(gCurrentMapFileName
) <> 0) then e_LogWritefln('SAVE: current map is ''%s''...', [gCurrentMapFileName
]);
171 bMem
.WriteString(gCurrentMapFileName
);
173 str
:= gGameSettings
.WAD
;
174 bMem
.WriteString(str
, 128);
176 str
:= g_ExtractFileName(gMapInfo
.Map
);
177 bMem
.WriteString(str
, 32);
178 // Êîëè÷åñòâî èãðîêîâ:
179 nPlayers
:= g_Player_GetCount();
180 bMem
.WriteByte(nPlayers
);
182 bMem
.WriteDWORD(gTime
);
184 bMem
.WriteByte(gGameSettings
.GameType
);
186 bMem
.WriteByte(gGameSettings
.GameMode
);
188 bMem
.WriteWord(gGameSettings
.TimeLimit
);
190 bMem
.WriteWord(gGameSettings
.GoalLimit
);
192 bMem
.WriteByte(gGameSettings
.MaxLives
);
194 bMem
.WriteDWORD(gGameSettings
.Options
);
196 bMem
.WriteWord(gCoopMonstersKilled
);
197 bMem
.WriteWord(gCoopSecretsFound
);
198 bMem
.WriteWord(gCoopTotalMonstersKilled
);
199 bMem
.WriteWord(gCoopTotalSecretsFound
);
200 bMem
.WriteWord(gCoopTotalMonsters
);
201 bMem
.WriteWord(gCoopTotalSecrets
);
202 // Ñîõðàíÿåì ñîñòîÿíèå èãðû:
203 bFile
.WriteMemory(bMem
);
208 ///// Ñîõðàíÿåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: /////
209 bMem
:= TBinMemoryWriter
.Create(8);
210 sig
:= PLAYER_VIEW_SIGNATURE
;
211 bMem
.WriteDWORD(sig
); // 'PLVW'
214 if gPlayer1
<> nil then
215 PID1
:= gPlayer1
.UID
;
216 if gPlayer2
<> nil then
217 PID2
:= gPlayer2
.UID
;
218 bMem
.WriteWord(PID1
);
219 bMem
.WriteWord(PID2
);
220 bFile
.WriteMemory(bMem
);
225 ///// Ïîëó÷àåì ñîñòîÿíèå êàðòû: /////
226 g_Map_SaveState(bMem
);
227 // Ñîõðàíÿåì ñîñòîÿíèå êàðòû:
228 bFile
.WriteMemory(bMem
);
233 ///// Ïîëó÷àåì ñîñòîÿíèå ïðåäìåòîâ: /////
234 g_Items_SaveState(bMem
);
235 // Ñîõðàíÿåì ñîñòîÿíèå ïðåäìåòîâ:
236 bFile
.WriteMemory(bMem
);
241 ///// Ïîëó÷àåì ñîñòîÿíèå òðèããåðîâ: /////
242 g_Triggers_SaveState(bMem
);
243 // Ñîõðàíÿåì ñîñòîÿíèå òðèããåðîâ:
244 bFile
.WriteMemory(bMem
);
249 ///// Ïîëó÷àåì ñîñòîÿíèå îðóæèÿ: /////
250 g_Weapon_SaveState(bMem
);
251 // Ñîõðàíÿåì ñîñòîÿíèå îðóæèÿ:
252 bFile
.WriteMemory(bMem
);
257 ///// Ïîëó÷àåì ñîñòîÿíèå ìîíñòðîâ: /////
258 g_Monsters_SaveState(bMem
);
259 // Ñîõðàíÿåì ñîñòîÿíèå ìîíñòðîâ:
260 bFile
.WriteMemory(bMem
);
265 ///// Ïîëó÷àåì ñîñòîÿíèå òðóïîâ: /////
266 g_Player_Corpses_SaveState(bMem
);
267 // Ñîõðàíÿåì ñîñòîÿíèå òðóïîâ:
268 bFile
.WriteMemory(bMem
);
273 ///// Ñîõðàíÿåì èãðîêîâ (â òîì ÷èñëå áîòîâ): /////
277 for i
:= 0 to High(gPlayers
) do
278 if gPlayers
[i
] <> nil then
280 // Ïîëó÷àåì ñîñòîÿíèå èãðîêà:
281 gPlayers
[i
].SaveState(bMem
);
282 // Ñîõðàíÿåì ñîñòîÿíèå èãðîêà:
283 bFile
.WriteMemory(bMem
);
289 // Âñå ëè èãðîêè íà ìåñòå:
290 if k
<> nPlayers
then
292 raise EInOutError
.Create('g_SaveGame: Wrong Players Count');
297 ///// Ìàðêåð îêîí÷àíèÿ: /////
298 bMem
:= TBinMemoryWriter
.Create(4);
299 // Ñòðîêà - îáîçíà÷åíèå êîíöà:
300 str
:= END_MARKER_STRING
; // 'END'
301 bMem
.WriteString(str
, 3);
302 // Ñîõðàíÿåì ìàðêåð îêîí÷àíèÿ:
303 bFile
.WriteMemory(bMem
);
308 // Çàêðûâàåì ôàéë ñîõðàíåíèÿ:
313 on E1
: EInOutError
do
315 g_Console_Add(_lc
[I_GAME_ERROR_SAVE
]);
316 e_WriteLog('SaveState I/O Error: '+E1
.Message, MSG_WARNING
);
318 on E2
: EBinSizeError
do
320 g_Console_Add(_lc
[I_GAME_ERROR_SAVE
]);
321 e_WriteLog('SaveState Size Error: '+E2
.Message, MSG_WARNING
);
329 function g_LoadGame(n
: Integer): Boolean;
331 bFile
: TBinFileReader
;
332 bMem
: TBinMemoryReader
;
334 str
, WAD_Path
, Map_Name
: String;
335 nPlayers
, Game_Type
, Game_Mode
, Game_MaxLives
: Byte;
336 Game_TimeLimit
, Game_GoalLimit
: Word;
337 Game_Time
, Game_Options
: Cardinal;
338 Game_CoopMonstersKilled
,
339 Game_CoopSecretsFound
,
340 Game_CoopTotalMonstersKilled
,
341 Game_CoopTotalSecretsFound
,
342 Game_CoopTotalMonsters
,
343 Game_CoopTotalSecrets
,
346 gameCleared
: Boolean = false;
347 curmapfile
: AnsiString = '';
354 // Îòêðûâàåì ôàéë ñ ñîõðàíåíèåì:
355 bFile
:= TBinFileReader
.Create();
356 if not bFile
.OpenFile(DataDir
+ 'SAVGAME' + IntToStr(n
) + '.DAT',
357 SAVE_SIGNATURE
, SAVE_VERSION
) then
363 e_WriteLog('Loading saved game...', MSG_NOTIFY
);
364 //g_Game_Free(false); // don't free textures for the same map
366 g_Game_ClearLoading();
367 g_Game_SetLoadingText(_lc
[I_LOAD_SAVE_FILE
], 0, False);
368 gLoadGameMode
:= True;
370 ///// Çàãðóæàåì ñîñòîÿíèå èãðû: /////
371 bMem
:= TBinMemoryReader
.Create();
372 bFile
.ReadMemory(bMem
);
374 bMem
.ReadString(str
);
376 // Ïîëíûé ïóòü ê âàäó è êàðòà
377 bMem
.ReadString(curmapfile
);
379 if (Length(gCurrentMapFileName
) <> 0) then e_LogWritefln('LOAD: previous map was ''%s''...', [gCurrentMapFileName
]);
380 if (Length(curmapfile
) <> 0) then e_LogWritefln('LOAD: new map is ''%s''...', [curmapfile
]);
381 // À âîò òóò, íàêîíåö, ÷èñòèì ðåñóðñû
382 g_Game_Free(curmapfile
<> gCurrentMapFileName
); // don't free textures for the same map
386 bMem
.ReadString(WAD_Path
);
388 bMem
.ReadString(Map_Name
);
389 // Êîëè÷åñòâî èãðîêîâ:
390 bMem
.ReadByte(nPlayers
);
392 bMem
.ReadDWORD(Game_Time
);
394 bMem
.ReadByte(Game_Type
);
396 bMem
.ReadByte(Game_Mode
);
398 bMem
.ReadWord(Game_TimeLimit
);
400 bMem
.ReadWord(Game_GoalLimit
);
402 bMem
.ReadByte(Game_MaxLives
);
404 bMem
.ReadDWORD(Game_Options
);
406 bMem
.ReadWord(Game_CoopMonstersKilled
);
407 bMem
.ReadWord(Game_CoopSecretsFound
);
408 bMem
.ReadWord(Game_CoopTotalMonstersKilled
);
409 bMem
.ReadWord(Game_CoopTotalSecretsFound
);
410 bMem
.ReadWord(Game_CoopTotalMonsters
);
411 bMem
.ReadWord(Game_CoopTotalSecrets
);
412 // Cîñòîÿíèå èãðû çàãðóæåíî:
417 ///// Çàãðóæàåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: /////
418 bMem
:= TBinMemoryReader
.Create();
419 bFile
.ReadMemory(bMem
);
421 if sig
<> PLAYER_VIEW_SIGNATURE
then // 'PLVW'
423 raise EInOutError
.Create('g_LoadGame: Wrong Player View Signature');
432 ZeroMemory(@gGameSettings
, SizeOf(TGameSettings
));
435 if (Game_Type
= GT_NONE
) or (Game_Type
= GT_SINGLE
) then
438 gGameSettings
.GameType
:= GT_SINGLE
;
439 gGameSettings
.MaxLives
:= 0;
440 gGameSettings
.Options
:= gGameSettings
.Options
+ GAME_OPTION_ALLOWEXIT
;
441 gGameSettings
.Options
:= gGameSettings
.Options
+ GAME_OPTION_MONSTERS
;
442 gGameSettings
.Options
:= gGameSettings
.Options
+ GAME_OPTION_BOTVSMONSTER
;
443 gSwitchGameMode
:= GM_SINGLE
;
448 gGameSettings
.GameType
:= GT_CUSTOM
;
449 gGameSettings
.GameMode
:= Game_Mode
;
450 gSwitchGameMode
:= Game_Mode
;
451 gGameSettings
.TimeLimit
:= Game_TimeLimit
;
452 gGameSettings
.GoalLimit
:= Game_GoalLimit
;
453 gGameSettings
.MaxLives
:= IfThen(Game_Mode
= GM_CTF
, 0, Game_MaxLives
);
454 gGameSettings
.Options
:= Game_Options
;
456 g_Game_ExecuteEvent('ongamestart');
458 // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
459 g_Game_SetupScreenSize();
461 // Çàãðóçêà è çàïóñê êàðòû:
462 if not g_Game_StartMap(WAD_Path
+ ':\' + Map_Name
, True, curmapfile
) then
464 g_FatalError(Format(_lc
[I_GAME_ERROR_MAP_LOAD
], [WAD_Path
+ ':\' + Map_Name
]));
468 // Íàñòðîéêè èãðîêîâ è áîòîâ:
471 // Óñòàíàâëèâàåì âðåìÿ:
474 gCoopMonstersKilled
:= Game_CoopMonstersKilled
;
475 gCoopSecretsFound
:= Game_CoopSecretsFound
;
476 gCoopTotalMonstersKilled
:= Game_CoopTotalMonstersKilled
;
477 gCoopTotalSecretsFound
:= Game_CoopTotalSecretsFound
;
478 gCoopTotalMonsters
:= Game_CoopTotalMonsters
;
479 gCoopTotalSecrets
:= Game_CoopTotalSecrets
;
481 ///// Çàãðóæàåì ñîñòîÿíèå êàðòû: /////
482 bMem
:= TBinMemoryReader
.Create();
483 bFile
.ReadMemory(bMem
);
485 g_Map_LoadState(bMem
);
490 ///// Çàãðóæàåì ñîñòîÿíèå ïðåäìåòîâ: /////
491 bMem
:= TBinMemoryReader
.Create();
492 bFile
.ReadMemory(bMem
);
493 // Ñîñòîÿíèå ïðåäìåòîâ:
494 g_Items_LoadState(bMem
);
499 ///// Çàãðóæàåì ñîñòîÿíèå òðèããåðîâ: /////
500 bMem
:= TBinMemoryReader
.Create();
501 bFile
.ReadMemory(bMem
);
502 // Ñîñòîÿíèå òðèããåðîâ:
503 g_Triggers_LoadState(bMem
);
508 ///// Çàãðóæàåì ñîñòîÿíèå îðóæèÿ: /////
509 bMem
:= TBinMemoryReader
.Create();
510 bFile
.ReadMemory(bMem
);
512 g_Weapon_LoadState(bMem
);
517 ///// Çàãðóæàåì ñîñòîÿíèå ìîíñòðîâ: /////
518 bMem
:= TBinMemoryReader
.Create();
519 bFile
.ReadMemory(bMem
);
520 // Ñîñòîÿíèå ìîíñòðîâ:
521 g_Monsters_LoadState(bMem
);
526 ///// Çàãðóæàåì ñîñòîÿíèå òðóïîâ: /////
527 bMem
:= TBinMemoryReader
.Create();
528 bFile
.ReadMemory(bMem
);
530 g_Player_Corpses_LoadState(bMem
);
535 ///// Çàãðóæàåì èãðîêîâ (â òîì ÷èñëå áîòîâ): /////
539 for i
:= 0 to nPlayers
-1 do
541 // Çàãðóæàåì ñîñòîÿíèå èãðîêà:
542 bMem
:= TBinMemoryReader
.Create();
543 bFile
.ReadMemory(bMem
);
544 // Ñîñòîÿíèå èãðîêà/áîòà:
545 g_Player_CreateFromState(bMem
);
550 // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà:
551 gPlayer1
:= g_Player_Get(PID1
);
552 gPlayer2
:= g_Player_Get(PID2
);
553 if gPlayer1
<> nil then
555 gPlayer1
.Name
:= gPlayer1Settings
.Name
;
556 gPlayer1
.FPreferredTeam
:= gPlayer1Settings
.Team
;
557 gPlayer1
.FActualModelName
:= gPlayer1Settings
.Model
;
558 gPlayer1
.SetModel(gPlayer1
.FActualModelName
);
559 gPlayer1
.SetColor(gPlayer1Settings
.Color
);
561 if gPlayer2
<> nil then
563 gPlayer2
.Name
:= gPlayer2Settings
.Name
;
564 gPlayer2
.FPreferredTeam
:= gPlayer2Settings
.Team
;
565 gPlayer2
.FActualModelName
:= gPlayer2Settings
.Model
;
566 gPlayer2
.SetModel(gPlayer2
.FActualModelName
);
567 gPlayer2
.SetColor(gPlayer2Settings
.Color
);
571 ///// Ìàðêåð îêîí÷àíèÿ: /////
572 bMem
:= TBinMemoryReader
.Create();
573 bFile
.ReadMemory(bMem
);
574 // Ñòðîêà - îáîçíà÷åíèå êîíöà:
575 bMem
.ReadString(str
);
576 if str
<> END_MARKER_STRING
then // 'END'
578 raise EInOutError
.Create('g_LoadGame: No END Marker');
580 // Ìàðêåð îêîí÷àíèÿ çàãðóæåí:
585 // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ:
586 if {(gMonsters <> nil) and} (gTriggers
<> nil) then
587 g_Map_ReAdd_DieTriggers();
589 // Çàêðûâàåì ôàéë çàãðóçêè:
591 gLoadGameMode
:= False;
595 on E1
: EInOutError
do
597 g_Console_Add(_lc
[I_GAME_ERROR_LOAD
]);
598 e_WriteLog('LoadState I/O Error: '+E1
.Message, MSG_WARNING
);
599 if not gameCleared
then g_Game_Free();
601 on E2
: EBinSizeError
do
603 g_Console_Add(_lc
[I_GAME_ERROR_LOAD
]);
604 e_WriteLog('LoadState Size Error: '+E2
.Message, MSG_WARNING
);
605 if not gameCleared
then g_Game_Free();