📄 g_save.pas
字号:
WriteField2(f, field, PByte(client));
inc(field);
end;
end;
(*
==============
ReadClient
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure ReadClient(Var f: File; client: gclient_p);
Var field: field_p;
begin
BlockRead(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: File; i: Integer; str: Array[0..15] of Char;
begin
if not autosave then
SaveClientData;
try
AssignFile(f, filename);
Rewrite(f, 1);
except
gi_error('Couldn''t open %s', [filename]);
end;
FillChar(str[0],sizeof(str),0);
strcopy(str, __DATE__);
BlockWrite(f, str[0], sizeof(str));
game.autosaved := autosave;
BlockWrite(f, game, sizeof(game));
game.autosaved := false;
for i:=0 to game.maxclients-1 do begin
WriteClient(f, @gclient_a(game.clients)[i]);
end;
CloseFile(f);
end;
procedure ReadGame(filename: PChar); cdecl;
Var f: File; i: Integer; str: Array[0..15] of Char;
begin
gi.FreeTags(TAG_GAME);
try
AssignFile(f, filename);
Reset(f,1);
except
gi_error('Couldn''t open %s', [filename]);
end;
BlockRead(f, str[0], sizeof(str));
if strcomp(str, __DATE__) <> 0 then begin
CloseFile(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];
BlockRead(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]);
CloseFile(f);
end;
//==========================================================
(*
==============
WriteEdict
All pointer variables (except function pointers) must be handled specially.
==============
*)
procedure WriteEdict(Var f: File; 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
BlockWrite(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(Var f: File);
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
BlockWrite(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(Var f: File; ent: edict_p);
Var field: field_p;
begin
BlockRead(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(Var f: File);
Var field: field_p;
begin
BlockRead(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: File; base: Pointer;
begin
Try
AssignFile(f, filename);
Rewrite(f, 1);
Except
gi_error('Couldn''t open %s', [filename]);
end;
// write out edict size for checking
i := sizeof(edict_t);
BlockWrite(f, i, sizeof(i));
// write out a function pointer for checking
base := @InitGame;
BlockWrite(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;
BlockWrite(f, i, sizeof(i));
WriteEdict(f, ent);
end;
i := -1;
BlockWrite(f, i, sizeof(i));
CloseFile(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: File; i: Integer; base: Pointer; ent: edict_p;
BytesRead: Integer;
begin
Try
AssignFile(f, filename);
Reset(f, 1);
Except
gi_error('Couldn''t open %s', [filename]);
end;
// 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
BlockRead(f, i, sizeof(i));
if i <> sizeof(edict_t) then begin
CloseFile(f);
gi.error('ReadLevel: mismatched edict size');
end;
// check function pointer base address
BlockRead(f, base, sizeof(base));
{$ifdef WIN32}
if base <> @InitGame then begin
CloseFile(f);
gi.error('ReadLevel: function pointers have moved');
end;
{$else}
gi_dprintf('Function offsets %d\n', [LongInt(base) - LongInt(@InitGame)]);
{$endif}
// load the level locals
ReadLevelLocals(f);
// load all the entities
while true do begin
BlockRead(f, entnum, sizeof(entnum), BytesRead);
if BytesRead <> sizeof(entnum) then begin
CloseFile(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;
CloseFile(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
SetFields;
SetLevelFields;
SetClientFields;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -