📄 g_save.pas
字号:
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure ReadClient(f: integer; client: gclient_p);
var
field: field_p;
begin
FileRead(f, client^, sizeof(client^));
field := @clientfields[0];
while field^.name <> Nil do
begin
ReadField(f, field, PByte(client));
inc(field);
end;
end;
(*
============
WriteGame
This will be called whenever the game goes to a new level,
and when the user explicitly saves the game.
Game information include cross level data, like multi level
triggers, help computer info, and all client states.
A single player death will automatically restore from the
last save position.
============
*)
procedure WriteGame(filename: PChar; autosave: qboolean); cdecl;
var
f: integer;
i: Integer;
str: array[0..15] of Char;
begin
if not autosave then
SaveClientData;
f := FileCreate(FileName);
if f = 0 then
gi.error('Couldn''t open %s', filename);
FillChar(str[0], SizeOf(str), 0);
strcopy(str, __DATE__);
FileWrite(f, str, SizeOf(str));
game.autosaved := autosave;
FileWrite(f, game, sizeof(game));
game.autosaved := false;
for i := 0 to game.maxclients-1 do
WriteClient(f, @gclient_a(game.clients)^[i]);
FileClose(f);
end;
procedure ReadGame(filename: PChar); cdecl;
var
f: integer;
i: Integer;
str: array[0..15] of Char;
begin
gi.FreeTags(TAG_GAME);
f := FileOpen(FileName, fmOpenRead);
if f = 0 then
gi.error('Couldn''t open %s', filename);
FileRead(f, str[0], SizeOf(str));
if strcomp(str, __DATE__) <> 0 then
begin
FileClose(f);
gi.error('Savegame from an older version.'#10);
end;
g_edicts := gi.TagMalloc(game.maxentities * sizeof(g_edicts^[0]), TAG_GAME);
globals.edicts := @g_edicts^[0];
FileRead(f, game, SizeOf(game));
game.clients := gi.TagMalloc(game.maxclients * sizeof(gclient_t), TAG_GAME);
for i := 0 to game.maxclients-1 do
ReadClient(f, @gclient_a(game.clients)^[i]);
FileClose(f);
end;
//==========================================================
(*
==============
WriteEdict
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure WriteEdict(f: integer; ent: edict_p);
var
field: field_p;
temp: edict_t;
begin
// all of the ints, floats, and vectors stay as they are
temp := ent^;
// change the pointers to lengths or indexes
field := @fields[0];
while (field^.name <> Nil) do
begin
WriteField1(f, field, PByte(@temp));
Inc(field);
end;
// write the block
FileWrite(f, temp, SizeOf(temp));
// now write any allocated data following the edict
field := @fields[0];
while field^.name <> Nil do
begin
WriteField2(f, field, PByte(ent));
Inc(field);
end;
end;
(*
==============
WriteLevelLocals
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure WriteLevelLocals(f: integer);
var
field: field_p;
temp: level_locals_t;
begin
// all of the ints, floats, and vectors stay as they are
temp := level;
// change the pointers to lengths or indexes
field := @levelfields[0];
While field^.name <> Nil do
begin
WriteField1(f, field, PByte(@temp));
Inc(field);
end;
// write the block
FileWrite(f, temp, SizeOf(temp));
// now write any allocated data following the edict
field := @levelfields[0];
While field^.name <> Nil do
begin
WriteField2(f, field, PByte(@level));
inc(field);
end;
end;
(*
==============
ReadEdict
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure ReadEdict(f: integer; ent: edict_p);
var
field: field_p;
begin
FileRead(f, ent^, SizeOf(ent^));
field := @fields[0];
While field^.name <> Nil do
begin
ReadField(f, field, PByte(ent));
Inc(field);
end;
end;
(*
==============
ReadLevelLocals
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure ReadLevelLocals(f: integer);
var
field: field_p;
begin
FileRead(f, level, SizeOf(level));
field := @levelfields[0];
While field^.name <> Nil do
begin
ReadField(f, field, PByte(@level));
Inc(field);
end;
end;
(*
=================
WriteLevel
=================
*)
procedure WriteLevel(filename: PChar); cdecl;
var
i: Integer;
ent: edict_p;
f: integer;
base: Pointer;
begin
f := FileCreate(filename);
if f = 0 then
gi.error('Couldn''t open %s', filename);
// write out edict size for checking
i := SizeOf(edict_t);
FileWrite(f, i, SizeOf(i));
// write out a function pointer for checking
base := @InitGame;
FileWrite(f, base, SizeOf(base));
// write out level_locals_t
WriteLevelLocals(f);
// write out all the entities
for i := 0 to globals.num_edicts - 1 do
begin
ent := @g_edicts^[i];
if not ent^.inuse then
Continue;
FileWrite(f, i, SizeOf(i));
WriteEdict(f, ent);
end;
i := -1;
FileWrite(f, i, SizeOf(i));
FileClose(f);
end;
(*
=================
ReadLevel
SpawnEntities will allready have been called on the
level the same way it was when the level was saved.
That is necessary to get the baselines
set up identically.
The server will have cleared all of the world links before
calling ReadLevel.
No clients are connected yet.
=================
*)
procedure ReadLevel(filename: PChar); cdecl;
var
entnum: Integer;
f: integer;
i: Integer;
base: Pointer;
ent: edict_p;
BytesRead: Integer;
begin
f := FileOpen(FileName, fmOpenRead);
if f = 0 then
gi.error('Couldn''t open %s', filename);
// free any dynamic memory allocated by loading the level
// base state
gi.FreeTags(TAG_LEVEL);
// wipe all the entities
FillChar(g_edicts^, game.maxentities*SizeOf(g_edicts^[0]), 0);
globals.num_edicts := Trunc(maxclients^.value)+1;
// check edict size
FileRead(f, i, SizeOf(i));
if i <> SizeOf(edict_t) then
begin
FileClose(f);
gi.error('ReadLevel: mismatched edict size');
end;
// check function pointer base address
FileRead(f, base, SizeOf(base));
{$ifdef WIN32}
if base <> @InitGame then
begin
FileClose(f);
gi.error('ReadLevel: function pointers have moved');
end;
{$else}
gi_dprintf('Function offsets %d'#10, [LongInt(base) - LongInt(@InitGame)]);
{$endif}
// load the level locals
ReadLevelLocals(f);
// load all the entities
while true do
begin
BytesRead := FileRead(f, entnum, SizeOf(entnum));
if BytesRead <> sizeof(entnum) then
begin
FileClose(f);
gi.error('ReadLevel: failed to read entnum');
end;
if entnum = -1 then
Break;
if entnum >= globals.num_edicts then
globals.num_edicts := entnum+1;
ent := @g_edicts^[entnum];
ReadEdict(f, ent);
// let the server rebuild world links for this ent
FillChar(ent^.area, sizeof(ent^.area), 0);
gi.linkentity(ent);
end;
FileClose(f);
// mark all clients as unconnected
for i := 0 to Trunc(maxclients^.value) - 1 do
begin
ent := @g_edicts^[i+1];
ent^.client := @gclient_a(game.clients)^[i];
ent^.client^.pers.connected := false;
end;
// do any load time things at this point
for i := 0 to globals.num_edicts-1 do
begin
ent := @g_edicts^[i];
if not ent^.inuse then
Continue;
// fire any cross-level triggers
if ent^.classname <> Nil then
if (strcomp(ent^.classname, 'target_crosslevel_target') = 0) then
ent^.nextthink := level.time + ent^.delay;
end;
end;
Initialization
begin
SetFields;
SetLevelFields;
SetClientFields;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -