mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
777 lines
18 KiB
C
777 lines
18 KiB
C
|
||
#include "g_local.h"
|
||
#include "bot.h"
|
||
|
||
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},
|
||
{"pathtarget", FOFS(pathtarget), F_LSTRING},
|
||
{"deathtarget", FOFS(deathtarget), F_LSTRING},
|
||
{"killtarget", FOFS(killtarget), F_LSTRING},
|
||
{"combattarget", FOFS(combattarget), F_LSTRING},
|
||
{"message", FOFS(message), F_LSTRING},
|
||
{"team", FOFS(team), F_LSTRING},
|
||
{"wait", FOFS(wait), F_FLOAT},
|
||
{"delay", FOFS(delay), F_FLOAT},
|
||
{"random", FOFS(random), F_FLOAT},
|
||
{"move_origin", FOFS(move_origin), F_VECTOR},
|
||
{"move_angles", FOFS(move_angles), F_VECTOR},
|
||
{"style", FOFS(style), F_INT},
|
||
{"count", FOFS(count), F_INT},
|
||
{"health", FOFS(health), 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},
|
||
// Knightmare- hack for setting alpha, allows mappers to specify
|
||
// an entity's alpha value with the key "salpha"
|
||
#ifdef KMQUAKE2_ENGINE_MOD
|
||
{"salpha", FOFS(s.alpha), F_FLOAT},
|
||
#endif
|
||
|
||
{"mass", FOFS(mass), F_INT},
|
||
{"volume", FOFS(volume), F_FLOAT},
|
||
{"attenuation", FOFS(attenuation), F_FLOAT},
|
||
{"map", FOFS(map), F_LSTRING},
|
||
// arena
|
||
{"arena", FOFS(arena),F_INT},
|
||
// end arena
|
||
|
||
// Knightmare added
|
||
{"musictrack", FOFS(musictrack), F_LSTRING}, // Knightmare- for specifying OGG or CD track
|
||
// model_spawn stuff
|
||
{"usermodel", FOFS(usermodel), F_LSTRING},
|
||
{"startframe", FOFS(startframe), F_INT},
|
||
{"framenumbers", FOFS(framenumbers), F_INT},
|
||
{"solidstate", FOFS(solidstate), F_INT},
|
||
{"renderfx", FOFS(renderfx), F_INT},
|
||
{"effects", FOFS(effects), F_INT},
|
||
{"skinnum", FOFS(skinnum), F_INT},
|
||
{"bleft", FOFS(bleft), F_VECTOR},
|
||
{"tright", FOFS(tright), F_VECTOR},
|
||
// rotating train stuff
|
||
{"pitch_speed", FOFS(pitch_speed), F_FLOAT},
|
||
{"yaw_speed", FOFS(yaw_speed), F_FLOAT},
|
||
{"roll_speed", FOFS(roll_speed), F_FLOAT},
|
||
{"roll", FOFS(roll), F_FLOAT},
|
||
{"from", FOFS(from), F_EDICT},
|
||
{"to", FOFS(to), F_EDICT},
|
||
{"smooth_movement", FOFS(smooth_movement), F_INT},
|
||
{"turn_rider", FOFS(turn_rider), F_INT},
|
||
{"origin_offset", FOFS(origin_offset), F_VECTOR},
|
||
// Knightmare- these are needed to update func_door_secret's positions
|
||
{"width", FOFS(width), F_FLOAT},
|
||
{"length", FOFS(length), F_FLOAT},
|
||
{"side", FOFS(side), F_FLOAT},
|
||
// Knightmare- new function pointers
|
||
{"play", FOFS(play), F_FUNCTION, FFL_NOSPAWN},
|
||
// end Knightmare
|
||
|
||
// 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}
|
||
};
|
||
|
||
// -------- 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(targetname), 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},
|
||
{"", FOFS(groundentity), F_EDICT},
|
||
{"", 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},
|
||
|
||
{NULL, 0, F_INT}
|
||
};
|
||
|
||
field_t levelfields[] =
|
||
{
|
||
{"", LLOFS(changemap), F_LSTRING},
|
||
|
||
{"", LLOFS(sight_client), F_EDICT},
|
||
{"", LLOFS(sight_entity), F_EDICT},
|
||
{"", LLOFS(sound_entity), F_EDICT},
|
||
{"", LLOFS(sound2_entity), F_EDICT},
|
||
|
||
{NULL, 0, F_INT}
|
||
};
|
||
|
||
field_t clientfields[] =
|
||
{
|
||
{"", CLOFS(pers.weapon), F_ITEM},
|
||
{"", CLOFS(pers.lastweapon), F_ITEM},
|
||
{"", CLOFS(newweapon), F_ITEM},
|
||
|
||
{NULL, 0, F_INT}
|
||
};
|
||
|
||
/*
|
||
============
|
||
InitGame
|
||
|
||
This will be called when the dll is first loaded, which
|
||
only happens when a new game is started or a save game
|
||
is loaded.
|
||
============
|
||
*/
|
||
void SetBotFlag1(edict_t *ent); //<2F>`<60>[<5B><>1<EFBFBD>̊<EFBFBD>
|
||
void SetBotFlag2(edict_t *ent); //<2F>`<60>[<5B><>2<EFBFBD>̊<EFBFBD>
|
||
void InitGame (void)
|
||
{
|
||
gi.dprintf ("==== InitGame ====\n");
|
||
|
||
bot_team_flag1 = NULL;
|
||
bot_team_flag2 = NULL;
|
||
// SetBotFlag1(NULL);
|
||
// SetBotFlag2(NULL);
|
||
|
||
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", "2000", 0);
|
||
sv_gravity = gi.cvar ("sv_gravity", "800", 0);
|
||
|
||
// 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", va("%i",MAX_EDICTS), CVAR_LATCH);
|
||
//maplist value
|
||
maplist = gi.cvar ("maplist", "default", CVAR_SERVERINFO | CVAR_LATCH);
|
||
//autospawn
|
||
autospawn = gi.cvar ("autospawn", "0", CVAR_SERVERINFO | CVAR_LATCH);
|
||
//chain edit flag
|
||
chedit = gi.cvar ("chedit", "0", CVAR_LATCH);
|
||
//vwep support
|
||
vwep = gi.cvar ("vwep", "1", CVAR_LATCH);
|
||
//game mode
|
||
zigmode = gi.cvar ("zigmode", "0", CVAR_SERVERINFO| CVAR_LATCH);
|
||
// Knightmare- Octavian's modified gibs
|
||
mega_gibs = gi.cvar ("mega_gibs", "0", 0);
|
||
//ZOID
|
||
//This game.dll only supports deathmatch
|
||
if (!deathmatch->value) {
|
||
gi.dprintf("Forcing deathmatch.\n");
|
||
gi.cvar_set("deathmatch", "1");
|
||
}
|
||
//force coop off
|
||
if (coop->value)
|
||
gi.cvar_set("coop", "0");
|
||
//ZOID
|
||
|
||
// change anytime vars
|
||
dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO);
|
||
fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO);
|
||
timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO);
|
||
//ZOID
|
||
capturelimit = gi.cvar ("capturelimit", "0", CVAR_SERVERINFO);
|
||
//ZOID
|
||
password = gi.cvar ("password", "", CVAR_USERINFO);
|
||
spectator_password = gi.cvar ("spectator_password", "", CVAR_USERINFO);
|
||
maxspectators = gi.cvar ("maxspectators", "4", CVAR_SERVERINFO);
|
||
g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE);
|
||
|
||
filterban = gi.cvar ("filterban", "1", 0);
|
||
|
||
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);
|
||
|
||
turn_rider = gi.cvar("turn_rider", "1", CVAR_SERVERINFO); // Knightmare added
|
||
|
||
// items
|
||
InitItems ();
|
||
|
||
Com_sprintf (game.helpmessage1, sizeof(game.helpmessage1), "");
|
||
|
||
Com_sprintf (game.helpmessage2, sizeof(game.helpmessage2), "");
|
||
|
||
// 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;
|
||
|
||
//ZOID
|
||
CTFInit();
|
||
//ZOID
|
||
Load_BotInfo(); //<2F>R<EFBFBD><52><EFBFBD>t<EFBFBD>B<EFBFBD>O<EFBFBD>ǂݍ<C782><DD8D><EFBFBD>3ZBConfig.cfg
|
||
}
|
||
|
||
//=========================================================
|
||
|
||
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 = (int)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 - 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 = (int)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 = &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];
|
||
|
||
if (!autosave)
|
||
SaveClientData ();
|
||
|
||
f = fopen (filename, "wb");
|
||
if (!f)
|
||
gi.error ("Couldn't open %s", filename);
|
||
|
||
memset (str, 0, sizeof(str));
|
||
Com_strcpy (str, sizeof(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]);
|
||
|
||
fclose (f);
|
||
}
|
||
|
||
void ReadGame (char *filename)
|
||
{
|
||
FILE *f;
|
||
int i;
|
||
char str[16];
|
||
|
||
gi.FreeTags (TAG_GAME);
|
||
|
||
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");
|
||
}
|
||
|
||
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]);
|
||
|
||
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;
|
||
|
||
// 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);
|
||
}
|
||
|
||
// 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);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
==============
|
||
ReadEdict
|
||
|
||
All pointer variables (except function pointers) must be handled specially.
|
||
==============
|
||
*/
|
||
void ReadEdict (FILE *f, edict_t *ent)
|
||
{
|
||
field_t *field;
|
||
|
||
fread (ent, sizeof(*ent), 1, f);
|
||
|
||
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;
|
||
|
||
fread (&level, sizeof(level), 1, f);
|
||
|
||
for (field=levelfields ; field->name ; field++)
|
||
{
|
||
ReadField (f, field, (byte *)&level);
|
||
}
|
||
}
|
||
|
||
/*
|
||
=================
|
||
WriteLevel
|
||
|
||
=================
|
||
*/
|
||
void WriteLevel (char *filename)
|
||
{
|
||
int i;
|
||
edict_t *ent;
|
||
FILE *f;
|
||
void *base;
|
||
|
||
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 entities
|
||
for (i=0 ; i<globals.num_edicts ; i++)
|
||
{
|
||
ent = &g_edicts[i];
|
||
if (!ent->inuse)
|
||
continue;
|
||
fwrite (&i, sizeof(i), 1, f);
|
||
WriteEdict (f, ent);
|
||
}
|
||
i = -1;
|
||
fwrite (&i, sizeof(i), 1, f);
|
||
|
||
fclose (f);
|
||
}
|
||
|
||
|
||
/*
|
||
=================
|
||
ReadLevel
|
||
|
||
SpawnEntities will already 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.
|
||
=================
|
||
*/
|
||
void ReadLevel (char *filename)
|
||
{
|
||
int entnum;
|
||
FILE *f;
|
||
int i;
|
||
void *base;
|
||
edict_t *ent;
|
||
|
||
f = fopen (filename, "rb");
|
||
if (!f)
|
||
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
|
||
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");
|
||
}
|
||
|
||
// check function pointer base address
|
||
fread (&base, sizeof(base), 1, f);
|
||
if (base != (void *)InitGame)
|
||
{
|
||
fclose (f);
|
||
gi.error ("ReadLevel: function pointers have moved");
|
||
}
|
||
|
||
// 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
|
||
memset (&ent->area, 0, sizeof(ent->area));
|
||
gi.linkentity (ent);
|
||
}
|
||
|
||
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->pers.connected = false;
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
}
|