mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2025-01-31 04:30:39 +00:00
95295401a4
Added weapon balancing and tech control cvars to 3ZB2 DLL. Added Vampire and Ammogen techs to 3ZB2 game DLL. Fixed func_door_secret movement bug (when built with origin brush) in Zaero DLL. Fixed armor stacking bug in default Lazarus, missionpack, and Zaero DLLs. Removed unused tech cvars in default Lazarus DLL. Changed parsing of TE_BLASTER_COLORED tempent.
780 lines
18 KiB
C
780 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");
|
||
|
||
// Knightmare- init lithium cvars
|
||
InitLithiumVars ();
|
||
|
||
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;
|
||
}
|
||
}
|