heretic2-sdk/Toolkit/Programming/GameCode/game/g_save.c
1999-04-30 00:00:00 +00:00

1113 lines
29 KiB
C

#include "g_local.h"
#include "g_Skeletons.h"
#include "ArrayedList.h"
#include "Message.h"
#include "q_ClientServer.h"
#include "q_Physics.h"
#include "g_playstats.h"
#include "utilities.h"
#include "p_anims.h"
#include "FX.h"
extern void InitPlayerinfo(edict_t *ent);
extern void LoadPersistantEffects(FILE *f);
extern void SavePersistantEffects(FILE *f);
extern player_export_t playerExport; // interface to player DLL.
field_t fields[] = {
{"classname", FOFS(classname), F_LSTRING},
{"origin", FOFS(s.origin), F_VECTOR},
{"model", FOFS(model), F_LSTRING},
{"spawnflags", FOFS(spawnflags), F_INT},
{"speed", FOFS(speed), F_FLOAT},
{"accel", FOFS(accel), F_FLOAT},
{"decel", FOFS(decel), F_FLOAT},
{"target", FOFS(target), F_LSTRING},
{"targetname", FOFS(targetname), F_LSTRING},
{"scripttarget", FOFS(scripttarget), F_LSTRING},
{"pathtarget", FOFS(pathtarget), F_LSTRING},
{"jumptarget", FOFS(jumptarget), F_LSTRING},
{"deathtarget", FOFS(deathtarget), F_LSTRING},
{"killtarget", FOFS(killtarget), F_LSTRING},
{"combattarget", FOFS(combattarget), F_LSTRING},
{"message", FOFS(message), F_LSTRING},
{"text_msg", FOFS(text_msg), F_LSTRING},
{"team", FOFS(team), F_LSTRING},
{"wait", FOFS(wait), F_FLOAT},
{"delay", FOFS(delay), F_FLOAT},
{"time", FOFS(time), F_FLOAT},
{"random", FOFS(random), F_FLOAT},
{"style", FOFS(style), F_INT},
{"count", FOFS(count), F_INT},
{"health", FOFS(health), F_INT},
{"skinnum", FOFS(s.skinnum), F_INT},
{"sounds", FOFS(sounds), F_INT},
{"light", 0, F_IGNORE},
{"dmg", FOFS(dmg), F_INT},
{"angles", FOFS(s.angles), F_VECTOR},
{"angle", FOFS(s.angles), F_ANGLEHACK},
{"mass", FOFS(mass), F_INT},
{"volume", FOFS(volume), F_FLOAT},
{"attenuation", FOFS(attenuation), F_FLOAT},
{"map", FOFS(map), F_LSTRING},
{"materialtype", FOFS(materialtype), F_INT},
{"scale", FOFS(s.scale), F_FLOAT},
{"color", FOFS(s.color), F_RGBA},
{"absLight", FOFS(s.absLight), F_RGB},
{"frame", FOFS(s.frame), F_INT},
{"mintel", FOFS(mintel), F_INT},
{"melee_range", FOFS(melee_range), F_FLOAT},
{"missile_range", FOFS(missile_range), F_FLOAT},
{"min_missile_range", FOFS(min_missile_range), F_FLOAT},
{"bypass_missile_chance", FOFS(bypass_missile_chance), F_INT},
{"jump_chance", FOFS(jump_chance), F_INT},
{"wakeup_distance", FOFS(wakeup_distance), F_FLOAT},
{"c_mode", FOFS(monsterinfo.c_mode), F_INT, F_INT},
{"homebuoy", FOFS(homebuoy), F_LSTRING},
{"wakeup_target", FOFS(wakeup_target), F_LSTRING},
{"pain_target", FOFS(pain_target), F_LSTRING},
// temp spawn vars -- only valid when the spawn function is called
{"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP},
{"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP},
{"height", STOFS(height), F_INT, FFL_SPAWNTEMP},
{"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP},
{"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP},
{"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP},
{"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP},
{"sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP},
{"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP},
{"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP},
{"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP},
{"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP},
{"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP},
{"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP},
{"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP},
{"rotate", STOFS(rotate), F_INT, FFL_SPAWNTEMP},
{"target2", FOFS(target2), F_LSTRING},
{"pathtargetname", FOFS(pathtargetname), F_LSTRING},
{"zangle", STOFS(zangle), F_FLOAT, FFL_SPAWNTEMP},
{"file", STOFS(file), F_LSTRING, FFL_SPAWNTEMP},
{"radius", STOFS(radius), F_INT, FFL_SPAWNTEMP},
{"offensive", STOFS(offensive), F_INT, FFL_SPAWNTEMP},
{"defensive", STOFS(defensive), F_INT, FFL_SPAWNTEMP},
{"spawnflags2", STOFS(spawnflags2), F_INT, FFL_SPAWNTEMP},
{"cooptimeout", STOFS(cooptimeout), F_INT, FFL_SPAWNTEMP},
{"script", STOFS(script), F_LSTRING, FFL_SPAWNTEMP},
{"parm1", STOFS(parms[0]), F_LSTRING, FFL_SPAWNTEMP},
{"parm2", STOFS(parms[1]), F_LSTRING, FFL_SPAWNTEMP},
{"parm3", STOFS(parms[2]), F_LSTRING, FFL_SPAWNTEMP},
{"parm4", STOFS(parms[3]), F_LSTRING, FFL_SPAWNTEMP},
{"parm5", STOFS(parms[4]), F_LSTRING, FFL_SPAWNTEMP},
{"parm6", STOFS(parms[5]), F_LSTRING, FFL_SPAWNTEMP},
{"parm7", STOFS(parms[6]), F_LSTRING, FFL_SPAWNTEMP},
{"parm8", STOFS(parms[7]), F_LSTRING, FFL_SPAWNTEMP},
{"parm9", STOFS(parms[8]), F_LSTRING, FFL_SPAWNTEMP},
{"parm10", STOFS(parms[9]), F_LSTRING, FFL_SPAWNTEMP},
};
// -------- just for savegames ----------
// all pointer fields should be listed here, or savegames
// won't work properly (they will crash and burn).
// this wasn't just tacked on to the fields array, because
// these don't need names, we wouldn't want map fields using
// some of these, and if one were accidentally present twice
// it would double swizzle (fuck) the pointer.
field_t savefields[] =
{
{"", FOFS(classname), F_LSTRING},
{"", FOFS(target), F_LSTRING},
{"", FOFS(target2), F_LSTRING},
{"", FOFS(targetname), F_LSTRING},
{"", FOFS(scripttarget), F_LSTRING},
{"", FOFS(killtarget), F_LSTRING},
{"", FOFS(team), F_LSTRING},
{"", FOFS(pathtarget), F_LSTRING},
{"", FOFS(deathtarget), F_LSTRING},
{"", FOFS(combattarget), F_LSTRING},
{"", FOFS(model), F_LSTRING},
{"", FOFS(map), F_LSTRING},
{"", FOFS(message), F_LSTRING},
{"", FOFS(client), F_CLIENT},
{"", FOFS(item), F_ITEM},
{"", FOFS(goalentity), F_EDICT},
{"", FOFS(movetarget), F_EDICT},
{"", FOFS(enemy), F_EDICT},
{"", FOFS(oldenemy), F_EDICT},
{"", FOFS(activator), F_EDICT},
#ifdef G_TRANSITION
{"", FOFS(groundentity), F_EDICT},
#endif
{"", FOFS(teamchain), F_EDICT},
{"", FOFS(teammaster), F_EDICT},
{"", FOFS(owner), F_EDICT},
{"", FOFS(mynoise), F_EDICT},
{"", FOFS(mynoise2), F_EDICT},
{"", FOFS(target_ent), F_EDICT},
{"", FOFS(chain), F_EDICT},
{"", FOFS(blockingEntity), F_EDICT},
{"", FOFS(last_buoyed_enemy), F_EDICT},
{"", FOFS(placeholder), F_EDICT},
{"", FOFS(fire_damage_enemy), F_EDICT},
{NULL, 0, F_INT}
};
field_t levelfields[] =
{
{"", LLOFS(changemap), F_LSTRING},
{"", LLOFS(sight_client), F_EDICT},
{"", LLOFS(sight_entity), F_EDICT},
{NULL, 0, F_INT}
};
field_t bouyfields[] =
{
{"", BYOFS(pathtarget), F_LSTRING},
{"", BYOFS(target), F_LSTRING},
{"", BYOFS(targetname), F_LSTRING},
{"", BYOFS(jump_target), F_LSTRING},
{NULL, 0, F_INT}
};
field_t clientfields[] =
{
{"", CLOFS(playerinfo.pers.weapon), F_ITEM},
{"", CLOFS(playerinfo.pers.lastweapon), F_ITEM},
{"", CLOFS(playerinfo.pers.defence), F_ITEM},
{"", CLOFS(playerinfo.pers.lastdefence), F_ITEM},
{"", CLOFS(playerinfo.pers.newweapon), F_ITEM},
{NULL, 0, F_INT}
};
trig_message_t message_text[MAX_MESSAGESTRINGS];
unsigned *messagebuf;
int LoadTextFile(char *name, char **addr)
{
int length;
char *buffer;
length = gi.FS_LoadFile(name, &buffer);
if(length <= 0)
{
Sys_Error("Unable to load %s", name);
return(0);
}
*addr = (char *)gi.TagMalloc(length + 1, 0);
memcpy(*addr, buffer, length);
*(*addr + length) = 0;
gi.FS_FreeFile(buffer);
return(length + 1);
}
void Load_Strings(void)
{
char *buffer;
int i,length;
char *p,*startp,*return_p;
length = LoadTextFile ("levelmsg.txt", &buffer);
messagebuf = (unsigned *) buffer;
startp = buffer;
p =0;
for (i=1; p < (buffer + length) ;++i)
{
if (i> MAX_MESSAGESTRINGS)
{
Com_Printf ("Too many strings\n");
return;
}
// Read in string up to return
return_p = strchr(startp,13); // Search for return characters 13 10
if (!return_p) // At end of file
{
break;
}
else
*return_p = 0;
// Search string for #
p = strchr(startp,'#'); // Search for # which signifies a wav file
if ((p) && (p < return_p))
{
*p = 0;
message_text[i].wav = ++p; // Save stuff after #
}
// Save stuff before #
message_text[i].string = startp;
do
{
p = strchr(startp,'@'); // Search for # which signifies a wav file
if (p)
*p = '\n';
} while(p);
return_p +=2; // Hop over 13 10
startp = return_p; // Advance to next string
}
}
/*
============
InitGame
This will be called when the dll is first loaded, which
only happens when a new game is begun
============
*/
void InitGame (void)
{
void G_InitResourceManagers();
gi.dprintf ("==== InitGame ====\n");
G_InitResourceManagers();
gun_x = gi.cvar ("gun_x", "0", 0);
gun_y = gi.cvar ("gun_y", "0", 0);
gun_z = gi.cvar ("gun_z", "0", 0);
//FIXME: sv_ prefix is wrong for these.
sv_rollspeed = gi.cvar ("sv_rollspeed", "200", 0);
sv_rollangle = gi.cvar ("sv_rollangle", "2", 0);
sv_maxvelocity = gi.cvar ("sv_maxvelocity", MAX_VELOCITY_STRING, 0);
sv_gravity = gi.cvar ("sv_gravity", GRAVITY_STRING, 0); // GRAVITY FOR ALL GAMES
sv_friction = gi.cvar ("sv_friction", FRICTION_STRING, 0); // FRICTION FOR ALL GAMES
// Noset vars.
dedicated = gi.cvar ("dedicated", "0", CVAR_NOSET);
// Latched vars.
sv_cheats = gi.cvar ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
gi.cvar ("gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_LATCH);
gi.cvar ("gamedate", __DATE__ , CVAR_SERVERINFO | CVAR_LATCH);
maxclients = gi.cvar ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
deathmatch = gi.cvar ("deathmatch", "0", CVAR_LATCH);
coop = gi.cvar ("coop", "0", CVAR_LATCH);
skill = gi.cvar ("skill", "1", CVAR_LATCH);
maxentities = gi.cvar ("maxentities", G_MAX_ENTITIES, CVAR_LATCH);
sv_nomonsters = gi.cvar ("nomonsters", "0", CVAR_SERVERINFO|CVAR_LATCH);
sv_freezemonsters = gi.cvar ("freezemonsters", "0", 0);
// Change anytime vars.
dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO);
advancedstaff = gi.cvar ("advancedstaff", "1", CVAR_SERVERINFO);
fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO);
timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO);
password = gi.cvar ("password", "", CVAR_USERINFO);
filterban = gi.cvar ("filterban", "1", 0);
g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE);
run_pitch = gi.cvar ("run_pitch", "0.002", 0);
run_roll = gi.cvar ("run_roll", "0.005", 0);
bob_up = gi.cvar ("bob_up", "0.005", 0);
bob_pitch = gi.cvar ("bob_pitch", "0.002", 0);
bob_roll = gi.cvar ("bob_roll", "0.002", 0);
autorotate = gi.cvar("autorotate", "0", 0);
blood = gi.cvar("blood", "1", 0);
checkanim = gi.cvar("checkanim", "0", 0);
allowillegalskins = gi.cvar("allowillegalskins", "0", CVAR_ARCHIVE);
pvs_cull = gi.cvar("pvs_cull", "1", 0);
showbuoys = gi.cvar("showbuoys", "0", 0);
showlitebuoys = gi.cvar("showlitebuoys", "0", 0);
mgai_debug = gi.cvar("mgai_debug", "0", 0);
deactivate_buoys = gi.cvar("deactivate_buoys", "0", 0);
anarchy = gi.cvar("anarchy", "0", 0);
impact_damage = gi.cvar("impact_damage", "1", 0);
cheating_monsters = gi.cvar("cheating_monsters", "1", 0);
singing_ogles = gi.cvar("singing_ogles", "0", 0);
no_runshrine = gi.cvar("no_runshrine", "0", 0);
no_tornado = gi.cvar("no_tornado", "0", 0);
no_teleport = gi.cvar("no_teleport","0",0);
no_phoenix = gi.cvar("no_phoenix","0",0);
no_irondoom = gi.cvar("no_irondoom","0",0);
no_morph = gi.cvar("no_morph","0",0);
no_shield = gi.cvar("no_shield","0",0);
game_test = gi.cvar("game_test", "0", 0);
flood_msgs = gi.cvar ("flood_msgs", "4", 0);
flood_persecond = gi.cvar ("flood_persecond", "4", 0);
flood_waitdelay = gi.cvar ("flood_waitdelay", "10", 0);
flood_killdelay = gi.cvar ("flood_killdelay", "10", 0);
sv_maplist = gi.cvar ("sv_maplist", "", 0);
player_dll = Cvar_Get("player_dll", DEFAULT_PLAYER_LIB, 0);
sv_cinematicfreeze = gi.cvar("sv_cinematicfreeze", "0", 0);
sv_jumpcinematic = gi.cvar("sv_jumpcinematic", "0", 0);
log_file_name = gi.cvar("log_file_name", "", CVAR_ARCHIVE);
log_file_footer = gi.cvar("log_file_footer", "", CVAR_ARCHIVE);
log_file_header = gi.cvar("log_file_header", "", CVAR_ARCHIVE);
log_file_line_header = gi.cvar("log_file_line_header", "", CVAR_ARCHIVE);
blood_level = gi.cvar ("blood_level", VIOLENCE_DEFAULT_STR, CVAR_ARCHIVE);
dm_no_bodies = gi.cvar ("dm_no_bodies", "0", CVAR_ARCHIVE);
gi.cvar("flash_screen", "1", 0);
P_Load(player_dll->string);
// ********************************************************************************************
// Initialise the inventory items.
// ********************************************************************************************
// Server side only elements.
G_InitItems();
// ********************************************************************************************
// Initialise hep messages.
// ********************************************************************************************
Com_sprintf (game.helpmessage1, sizeof(game.helpmessage1), "No help message1");
Com_sprintf (game.helpmessage2, sizeof(game.helpmessage2), "No help message2");
// ********************************************************************************************
// Initialize all entities for this game.
// ********************************************************************************************
game.maxentities = maxentities->value;
g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
globals.edicts = g_edicts;
globals.max_edicts = game.maxentities;
// ********************************************************************************************
// Initialize all clients for this game.
// ********************************************************************************************
game.maxclients = maxclients->value;
game.clients = gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);
globals.num_edicts = game.maxclients+1;
level.cinActive = false;
Load_Strings();
}
//=========================================================
void WriteField1 (FILE *f, field_t *field, byte *base)
{
void *p;
int len;
int index;
p = (void *)(base + field->ofs);
switch (field->type)
{
case F_INT:
case F_FLOAT:
case F_ANGLEHACK:
case F_VECTOR:
case F_IGNORE:
break;
case F_LSTRING:
case F_GSTRING:
if ( *(char **)p )
len = strlen(*(char **)p) + 1;
else
len = 0;
*(int *)p = len;
break;
case F_EDICT:
if ( *(edict_t **)p == NULL)
index = -1;
else
index = *(edict_t **)p - g_edicts;
*(int *)p = index;
break;
case F_CLIENT:
if ( *(gclient_t **)p == NULL)
index = -1;
else
index = *(gclient_t **)p - game.clients;
*(int *)p = index;
break;
case F_ITEM:
if ( *(edict_t **)p == NULL)
index = -1;
else
index = *(gitem_t **)p - playerExport.p_itemlist;
*(int *)p = index;
break;
default:
gi.error ("WriteEdict: unknown field type");
}
}
void WriteField2 (FILE *f, field_t *field, byte *base)
{
int len;
void *p;
p = (void *)(base + field->ofs);
switch (field->type)
{
case F_LSTRING:
case F_GSTRING:
if ( *(char **)p )
{
len = strlen(*(char **)p) + 1;
fwrite (*(char **)p, len, 1, f);
}
break;
}
}
void ReadField (FILE *f, field_t *field, byte *base)
{
void *p;
int len;
int index;
p = (void *)(base + field->ofs);
switch (field->type)
{
case F_INT:
case F_FLOAT:
case F_ANGLEHACK:
case F_VECTOR:
case F_IGNORE:
break;
case F_LSTRING:
len = *(int *)p;
if (!len)
*(char **)p = NULL;
else
{
*(char **)p = gi.TagMalloc (len, TAG_LEVEL);
fread (*(char **)p, len, 1, f);
}
break;
case F_GSTRING:
len = *(int *)p;
if (!len)
*(char **)p = NULL;
else
{
*(char **)p = gi.TagMalloc (len, TAG_GAME);
fread (*(char **)p, len, 1, f);
}
break;
case F_EDICT:
index = *(int *)p;
if ( index == -1 )
*(edict_t **)p = NULL;
else
*(edict_t **)p = &g_edicts[index];
break;
case F_CLIENT:
index = *(int *)p;
if ( index == -1 )
*(gclient_t **)p = NULL;
else
*(gclient_t **)p = &game.clients[index];
break;
case F_ITEM:
index = *(int *)p;
if ( index == -1 )
*(gitem_t **)p = NULL;
else
*(gitem_t **)p = &playerExport.p_itemlist[index];
break;
default:
gi.error ("ReadEdict: unknown field type");
}
}
//=========================================================
/*
==============
WriteClient
All pointer variables (except function pointers) must be handled specially.
==============
*/
void WriteClient (FILE *f, gclient_t *client)
{
field_t *field;
gclient_t temp;
// all of the ints, floats, and vectors stay as they are
temp = *client;
// change the pointers to lengths or indexes
for (field=clientfields ; field->name ; field++)
{
WriteField1 (f, field, (byte *)&temp);
}
// write the block
fwrite (&temp, sizeof(temp), 1, f);
// now write any allocated data following the edict
for (field=clientfields ; field->name ; field++)
{
WriteField2 (f, field, (byte *)client);
}
}
/*
==============
ReadClient
All pointer variables (except function pointers) must be handled specially.
==============
*/
void ReadClient (FILE *f, gclient_t *client)
{
field_t *field;
fread (client, sizeof(*client), 1, f);
for (field=clientfields ; field->name ; field++)
{
ReadField (f, field, (byte *)client);
}
}
/*
============
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.
============
*/
void WriteGame (char *filename, qboolean autosave)
{
FILE *f;
int i;
char str[16];
PerEffectsBuffer_t *peffect;
SaveClientData ();
f = fopen (filename, "wb");
if (!f)
gi.error ("Couldn't open %s", filename);
memset (str, 0, sizeof(str));
strcpy (str, __DATE__);
fwrite (str, sizeof(str), 1, f);
game.autosaved = autosave;
fwrite (&game, sizeof(game), 1, f);
game.autosaved = false;
for (i=0 ; i<game.maxclients ; i++)
WriteClient (f, &game.clients[i]);
SaveScripts(f, true);
// this is a bit bogus - search through the client effects and kill all the FX_PLAYER_EFFECTS before saving, since they will be re-created
// upon players re-joining the game after a load anyway.
peffect = (PerEffectsBuffer_t*) gi.Persistant_Effects_Array;
for (i=0; i<MAX_PERSISTANT_EFFECTS; i++, peffect++)
{
if (peffect->fx_num == FX_PLAYER_PERSISTANT)
peffect->numEffects = 0;
}
// save all the current persistant effects
fwrite (gi.Persistant_Effects_Array, (sizeof(PerEffectsBuffer_t) * MAX_PERSISTANT_EFFECTS), 1, f);
fclose (f);
// this is a bit bogus - search through the client effects and renable all FX_PLAYER_EFFECTS
peffect = (PerEffectsBuffer_t*) gi.Persistant_Effects_Array;
for (i=0; i<MAX_PERSISTANT_EFFECTS; i++, peffect++)
{
if (peffect->fx_num == FX_PLAYER_PERSISTANT)
peffect->numEffects = 1;
}
}
void ReadGame (char *filename)
{
FILE *f;
int i;
char str[16];
f = fopen (filename, "rb");
if (!f)
gi.error ("Couldn't open %s", filename);
fread (str, sizeof(str), 1, f);
if (strcmp (str, __DATE__))
{
fclose (f);
gi.error ("Savegame from an older version.\n");
return;
}
gi.FreeTags (TAG_GAME);
g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
globals.edicts = g_edicts;
fread (&game, sizeof(game), 1, f);
game.clients = gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);
for (i=0 ; i<game.maxclients ; i++)
ReadClient (f, &game.clients[i]);
LoadScripts(f, true);
fclose (f);
}
//==========================================================
/*
==============
WriteEdict
All pointer variables (except function pointers) must be handled specially.
==============
*/
void WriteEdict (FILE *f, edict_t *ent)
{
field_t *field;
edict_t temp;
// all of the ints, floats, and vectors stay as they are
temp = *ent;
// change the pointers to lengths or indexes
for (field=savefields ; field->name ; field++)
{
WriteField1 (f, field, (byte *)&temp);
}
// write the block
fwrite (&temp, sizeof(temp), 1, f);
// now write any allocated data following the edict
for (field=savefields ; field->name ; field++)
{
WriteField2 (f, field, (byte *)ent);
}
}
/*
==============
WriteLevelLocals
All pointer variables (except function pointers) must be handled specially.
==============
*/
void WriteLevelLocals (FILE *f)
{
field_t *field;
level_locals_t temp;
cvar_t *r_farclipdist;
cvar_t *r_fog;
cvar_t *r_fog_density;
int i;
// set up some console vars as level save variables
r_farclipdist = gi.cvar("r_farclipdist", FAR_CLIP_DIST, 0);
level.far_clip_dist_f = r_farclipdist->value;
r_fog = Cvar_Get ("r_fog", "0", 0);
level.fog = r_fog->value;
r_fog_density = Cvar_Get ("r_fog_density", "0", 0);
level.fog_density = r_fog_density->value;
// all of the ints, floats, and vectors stay as they are
temp = level;
// change the pointers to lengths or indexes
for (field=levelfields ; field->name ; field++)
{
WriteField1 (f, field, (byte *)&temp);
}
for (i = 0; i< level.active_buoys; i++)
{
// change the pointers to lengths or indexes
for (field=bouyfields ; field->name ; field++)
{
WriteField1 (f, field, (byte *)&temp.buoy_list[i]);
}
}
// write the block
fwrite (&temp, sizeof(temp), 1, f);
// now write any allocated data following the edict
for (field=levelfields ; field->name ; field++)
{
WriteField2 (f, field, (byte *)&level);
}
for (i = 0; i< level.active_buoys; i++)
{
// change the pointers to lengths or indexes
for (field=bouyfields ; field->name ; field++)
{
WriteField2 (f, field, (byte *)&level.buoy_list[i]);
}
}
}
/*
==============
ReadEdict
All pointer variables (except function pointers) must be handled specially.
==============
*/
void ReadEdict (FILE *f, edict_t *ent)
{
field_t *field;
SinglyLinkedList_t msgs;
char *temp;
void *s;
if(ent->s.clientEffects.buf)
{
temp = ent->s.clientEffects.buf; // buffer needs to be stored to be cleared by the engine
}
else
{
temp = NULL;
}
msgs = ent->msgQ.msgs;
s=ent->Script;
fread (ent, sizeof(*ent), 1, f);
ent->Script=s;
ent->s.clientEffects.buf = temp;
ent->msgQ.msgs = msgs;
ent->last_alert = NULL;
/*
// Only clients need skeletons - these are set up when all else is done. -MW.
if(ent->s.skeletalType != SKEL_NULL)
{
CreateSkeleton(ent->s.skeletalType);
}
*/
for (field=savefields ; field->name ; field++)
{
ReadField (f, field, (byte *)ent);
}
}
/*
==============
ReadLevelLocals
All pointer variables (except function pointers) must be handled specially.
==============
*/
void ReadLevelLocals (FILE *f)
{
field_t *field;
char temp[20];
int i;
fread (&level, sizeof(level), 1, f);
for (field=levelfields ; field->name ; field++)
{
ReadField (f, field, (byte *)&level);
}
for (i = 0; i< level.active_buoys; i++)
{
// change the pointers to lengths or indexes
for (field=bouyfields ; field->name ; field++)
{
ReadField (f, field, (byte *)&level.buoy_list[i]);
}
}
// set those console vars we should
sprintf(temp, "%f", level.far_clip_dist_f);
gi.cvar_set("r_farclipdist", temp);
sprintf(temp, "%f", level.fog);
gi.cvar_set("r_fog", temp);
sprintf(temp, "%f", level.fog_density);
gi.cvar_set("r_fog_density", temp);
// these are pointers and should be reset.
level.alert_entity = NULL;
level.last_alert = NULL;
for (i=0; i<MAX_ALERT_ENTS; i++)
{
level.alertents[i].inuse = false;
level.alertents[i].prev_alert = NULL;
level.alertents[i].next_alert = NULL;
}
}
/*
=================
WriteLevel
=================
*/
void WriteLevel (char *filename)
{
int i;
edict_t *ent;
FILE *f;
void *base;
PerEffectsBuffer_t *peffect;
f = fopen (filename, "wb");
if (!f)
gi.error ("Couldn't open %s", filename);
// write out edict size for checking
i = sizeof(edict_t);
fwrite (&i, sizeof(i), 1, f);
// write out a function pointer for checking
base = (void *)InitGame;
fwrite (&base, sizeof(base), 1, f);
// write out level_locals_t
WriteLevelLocals (f);
// write out all the configstrings
// fwrite (sv.configstrings, sizeof(sv.configstrings), 1, f);
// write out all the entities
for (i=0 ; i<globals.num_edicts ; i++)
{
ent = &g_edicts[i];
// we don't want to not save player entities, even if they are not in use, since when we go from
// level to a level we've already been to, there maybe monsters that are targeting the player,
// and they have problems if they are targeted at a player that has no data in them, even if the player is
// not inuse.
if (!ent->inuse && !ent->client)
continue;
fwrite (&i, sizeof(i), 1, f);
WriteEdict (f, ent);
}
i = -1;
fwrite (&i, sizeof(i), 1, f);
SaveScripts(f, false);
// this is a bit bogus - search through the client effects and kill all the FX_PLAYER_EFFECTS before saving, since they will be re-created
// upon players re-joining the game after a load anyway.
peffect = (PerEffectsBuffer_t*) gi.Persistant_Effects_Array;
for (i=0; i<MAX_PERSISTANT_EFFECTS; i++, peffect++)
{
if (peffect->fx_num == FX_PLAYER_PERSISTANT)
peffect->numEffects = 0;
}
// save all the current persistant effects
fwrite (gi.Persistant_Effects_Array, (sizeof(PerEffectsBuffer_t) * MAX_PERSISTANT_EFFECTS), 1, f);
fclose (f);
// this is a bit bogus - search through the client effects and renable all FX_PLAYER_EFFECTS
peffect = (PerEffectsBuffer_t*) gi.Persistant_Effects_Array;
for (i=0; i<MAX_PERSISTANT_EFFECTS; i++, peffect++)
{
if (peffect->fx_num == FX_PLAYER_PERSISTANT)
peffect->numEffects = 1;
}
}
/*
=================
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.
=================
*/
void ReadLevel (char *filename)
{
void ClearMessageQueues();
int entnum;
FILE *f;
int i;
void *base;
edict_t *ent;
f = fopen (filename, "rb");
if (!f)
gi.error ("Couldn't open %s", filename);
// ClearMessageQueues();
// Free any dynamic memory allocated by loading the level base state.
gi.FreeTags (TAG_LEVEL);
// Wipe all the entities.
memset (g_edicts, 0, game.maxentities*sizeof(g_edicts[0]));
globals.num_edicts = maxclients->value+1;
// Check edict size.
fread (&i, sizeof(i), 1, f);
if (i != sizeof(edict_t))
{
fclose (f);
gi.error ("ReadLevel: mismatched edict size");
}
fread (&base, sizeof(base), 1, f);
if (base != (void *)InitGame)
{
fclose (f);
gi.error ("ReadLevel: function pointers have moved - file was saved on different version.");
}
// Load the level locals.
ReadLevelLocals (f);
// Load all the entities.
while (1)
{
if (fread (&entnum, sizeof(entnum), 1, f) != 1)
{
fclose (f);
gi.error ("ReadLevel: failed to read entnum");
}
if (entnum == -1)
break;
if (entnum >= globals.num_edicts)
globals.num_edicts = entnum+1;
ent = &g_edicts[entnum];
ReadEdict (f, ent);
// Let the server rebuild world links for this ent.
ent->last_alert = NULL;
memset (&ent->area, 0, sizeof(ent->area));
// NOTE NOTE
// Missiles must be linked in specially. G_LinkMissile links as a SOLID_NOT, even though the entity is SOLID_BBOX
if (ent->movetype == MOVETYPE_FLYMISSILE && ent->solid == SOLID_BBOX)
{
G_LinkMissile (ent);
}
else
{
gi.linkentity (ent);
}
// Force the monsters just loaded to point at the right anim.
if((ent->classID > 0) && (!Cid_init[ent->classID]) && (ent->classID < NUM_CLASSIDS)) // Need to call once per level that item is on
{
classStaticsInits[ent->classID]();
Cid_init[ent->classID] = -1;
}
if ( ((ent->classname) && (*ent->classname)) && strcmp(ent->classname, "player") && ent->classID && classStatics[ent->classID].resInfo && ent->curAnimID)
SetAnim(ent, ent->curAnimID);
}
LoadScripts(f, false);
// Load up all the persistant effects and fire them off.
fread (gi.Persistant_Effects_Array, (sizeof(PerEffectsBuffer_t) * MAX_PERSISTANT_EFFECTS), 1, f);
gi.ClearPersistantEffects();
fclose (f);
// Mark all clients as unconnected.
for (i=0 ; i<maxclients->value ; i++)
{
ent = &g_edicts[i+1];
ent->client = game.clients + i;
ent->client->playerinfo.pers.connected = false;
InitPlayerinfo(ent);
SetupPlayerinfo(ent);
P_PlayerBasicAnimReset(&ent->client->playerinfo);
}
// Do any load time things at this point.
for (i=0 ; i<globals.num_edicts ; i++)
{
ent = &g_edicts[i];
if (!ent->inuse)
continue;
// Fire any cross-level triggers.
if (ent->classname)
if (strcmp(ent->classname, "target_crosslevel_target") == 0)
ent->nextthink = level.time + ent->delay;
}
}