1280 lines
33 KiB
C
1280 lines
33 KiB
C
|
|
#include "g_local.h"
|
|
|
|
#define Function(f) {#f, f}
|
|
|
|
mmove_t mmove_reloc;
|
|
|
|
field_t fields[] = {
|
|
{"classname", FOFS(classname), F_LSTRING},
|
|
{"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", FOFS(light_level), F_INT}, // Ridah, used by model lighting code
|
|
{"_color", FOFS(rotate), F_VECTOR}, // Ridah, used by model lighting code
|
|
{"radius", FOFS(dmg_radius), F_VECTOR}, // Ridah, used by model lighting code
|
|
|
|
{"dmg", FOFS(dmg), F_INT},
|
|
{"mass", FOFS(mass), F_INT},
|
|
{"volume", FOFS(volume), F_FLOAT},
|
|
{"attenuation", FOFS(attenuation), F_FLOAT},
|
|
{"map", FOFS(map), F_LSTRING},
|
|
{"origin", FOFS(s.origin), F_VECTOR},
|
|
{"angles", FOFS(s.angles), F_VECTOR},
|
|
{"angle", FOFS(s.angles), F_ANGLEHACK},
|
|
|
|
{"objectbounds_filename1", FOFS(s.model_parts[0].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename2", FOFS(s.model_parts[1].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename3", FOFS(s.model_parts[2].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename4", FOFS(s.model_parts[3].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename5", FOFS(s.model_parts[4].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename6", FOFS(s.model_parts[5].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename7", FOFS(s.model_parts[6].objectbounds_filename), F_LSTRING},
|
|
{"objectbounds_filename8", FOFS(s.model_parts[7].objectbounds_filename), F_LSTRING},
|
|
|
|
// JOSEPH 19-MAR-99
|
|
{"rotate", FOFS(rotate), F_VECTOR},
|
|
{"duration", FOFS(duration), F_FLOAT},
|
|
{"alphalevel", FOFS(alphalevel), F_INT},
|
|
{"fxdensity", FOFS(fxdensity), F_INT},
|
|
{"healspeed", FOFS(healspeed), F_INT},
|
|
{"deadticks", FOFS(deadticks), F_INT},
|
|
{"missteam", FOFS(missteam), F_INT},
|
|
{"misstime", FOFS(misstime), F_INT},
|
|
{"cameraangle", FOFS(cameraangle), F_VECTOR},
|
|
{"cameraorigin", FOFS(cameraorigin), F_VECTOR},
|
|
{"cameravel", FOFS(cameravel), F_VECTOR},
|
|
{"cameravelrel", FOFS(cameravelrel), F_VECTOR},
|
|
{"debugprint", FOFS(debugprint), F_INT},
|
|
{"target2", FOFS(target2), F_LSTRING},
|
|
{"localteam", FOFS(localteam), F_LSTRING},
|
|
{"reactdelay", FOFS(reactdelay), F_FLOAT},
|
|
{"currentcash", FOFS(currentcash), F_INT},
|
|
{"type", FOFS(type), F_LSTRING},
|
|
{"head", FOFS(head), F_INT},
|
|
{"key", FOFS(key), F_INT},
|
|
{"target2_ent", FOFS(target2_ent), F_EDICT},
|
|
{"missent", FOFS(missent), F_EDICT},
|
|
{"handle", FOFS(handle), F_EDICT},
|
|
{"handle2", FOFS(handle2), F_EDICT},
|
|
{"save_self", FOFS(save_self), F_EDICT},
|
|
{"save_other", FOFS(save_other), F_EDICT},
|
|
{"deadticks", FOFS(deadticks), F_INT},
|
|
{"thudsnd", FOFS(thudsnd), F_INT},
|
|
{"head", FOFS(head), F_INT},
|
|
{"firetype", FOFS(firetype), F_INT},
|
|
{"thudsurf", FOFS(thudsurf), F_INT},
|
|
{"lightit", FOFS(lightit), F_INT},
|
|
{"option", FOFS(option), F_INT},
|
|
{"noshadow", FOFS(noshadow), F_INT},
|
|
// END JOSEPH
|
|
|
|
{"acc", FOFS (acc), F_INT},
|
|
{"cal", FOFS (cal), F_INT},
|
|
|
|
// Ridah, new stuff
|
|
|
|
{"cast_group", FOFS(cast_group), F_INT},
|
|
{"skin", FOFS(skin), F_INT},
|
|
{"moral", FOFS(moral), F_INT},
|
|
{"guard_radius", FOFS(guard_radius), F_INT},
|
|
{"guard_target", FOFS(guard_target), F_LSTRING},
|
|
{"name", FOFS(name), F_LSTRING},
|
|
{"episode", FOFS(count), F_INT}, // used by worldspawn
|
|
{"scriptname", FOFS(scriptname), F_LSTRING},
|
|
|
|
{"onfireent", FOFS(onfireent), F_EDICT},
|
|
{"leader", FOFS(leader), F_EDICT},
|
|
{"leader_target", FOFS(leader_target), F_LSTRING},
|
|
{"last_goal", FOFS(last_goal), F_EDICT},
|
|
|
|
{"order", FOFS(order), F_INT},
|
|
{"order_timestamp", FOFS(order_timestamp), F_FLOAT},
|
|
{"moveout_ent", FOFS(moveout_ent), F_EDICT},
|
|
{"character_index", FOFS(character_index), F_INT},
|
|
{"last_talk_time", FOFS(last_talk_time), F_FLOAT},
|
|
{"profanity_level", FOFS(profanity_level), F_INT},
|
|
{"guard_ent", FOFS(guard_ent), F_EDICT},
|
|
{"sight_target", FOFS(sight_target), F_LSTRING},
|
|
{"goal_ent", FOFS(goal_ent), F_EDICT},
|
|
{"combat_goalent", FOFS(combat_goalent), F_EDICT},
|
|
{"cover_ent", FOFS(cover_ent), F_EDICT},
|
|
|
|
{"episode_flags", FOFS(episode_flags), F_INT},
|
|
|
|
{"name_index", FOFS(name_index), F_INT},
|
|
{"last_territory_touched", FOFS(last_territory_touched), F_EDICT},
|
|
{"response_ent", FOFS(response_ent), F_EDICT},
|
|
{"last_response_time", FOFS(last_response_time), F_FLOAT},
|
|
{"last_response", FOFS(last_response), F_INT},
|
|
|
|
{"start_ent", FOFS(start_ent), F_EDICT},
|
|
{"holdpos_ent", FOFS(holdpos_ent), F_EDICT},
|
|
|
|
{"next_combattarget", FOFS(next_combattarget), F_LSTRING},
|
|
|
|
{"activate_flags", FOFS(activate_flags), F_INT},
|
|
{"biketime", FOFS(biketime), F_FLOAT},
|
|
{"bikestate", FOFS(bikestate), F_INT},
|
|
|
|
{"vehicle_index", FOFS(vehicle_index), F_INT},
|
|
|
|
{"art_skins", FOFS(art_skins), F_LSTRING},
|
|
|
|
{"aiflags", FOFS(cast_info.aiflags), F_INT},
|
|
|
|
{"gun_noise_delay", FOFS(gun_noise_delay), F_FLOAT},
|
|
|
|
{"scale", FOFS(cast_info.scale), F_FLOAT},
|
|
|
|
{"voice_pitch", FOFS(voice_pitch), F_FLOAT},
|
|
|
|
{"health_threshold", FOFS(health_threshold), F_INT},
|
|
{"health_target", FOFS(health_target), F_LSTRING},
|
|
{"health_threshold2", FOFS(health_threshold2), F_INT},
|
|
{"health_target2", FOFS(health_target2), F_LSTRING},
|
|
{"health_threshold3", FOFS(health_threshold3), F_INT},
|
|
{"health_target3", FOFS(health_target3), F_LSTRING},
|
|
// Ridah, done.
|
|
|
|
|
|
{"goalentity", FOFS(goalentity), F_EDICT, FFL_NOSPAWN},
|
|
{"movetarget", FOFS(movetarget), F_EDICT, FFL_NOSPAWN},
|
|
{"enemy", FOFS(enemy), F_EDICT, FFL_NOSPAWN},
|
|
{"oldenemy", FOFS(oldenemy), F_EDICT, FFL_NOSPAWN},
|
|
{"activator", FOFS(activator), F_EDICT, FFL_NOSPAWN},
|
|
{"groundentity", FOFS(groundentity), F_EDICT, FFL_NOSPAWN},
|
|
{"teamchain", FOFS(teamchain), F_EDICT, FFL_NOSPAWN},
|
|
{"teammaster", FOFS(teammaster), F_EDICT, FFL_NOSPAWN},
|
|
{"owner", FOFS(owner), F_EDICT, FFL_NOSPAWN},
|
|
{"mynoise", FOFS(mynoise), F_EDICT, FFL_NOSPAWN},
|
|
{"mynoise2", FOFS(mynoise2), F_EDICT, FFL_NOSPAWN},
|
|
{"target_ent", FOFS(target_ent), F_EDICT, FFL_NOSPAWN},
|
|
{"chain", FOFS(chain), F_EDICT, FFL_NOSPAWN},
|
|
|
|
{"prethink", FOFS(prethink), F_FUNCTION, FFL_NOSPAWN},
|
|
{"think", FOFS(think), F_FUNCTION, FFL_NOSPAWN},
|
|
{"blocked", FOFS(blocked), F_FUNCTION, FFL_NOSPAWN},
|
|
{"touch", FOFS(touch), F_FUNCTION, FFL_NOSPAWN},
|
|
{"use", FOFS(use), F_FUNCTION, FFL_NOSPAWN},
|
|
{"pain", FOFS(pain), F_FUNCTION, FFL_NOSPAWN},
|
|
{"die", FOFS(die), F_FUNCTION, FFL_NOSPAWN},
|
|
|
|
{"idle", FOFS(cast_info.idle), F_FUNCTION, FFL_NOSPAWN},
|
|
{"search", FOFS(cast_info.search), F_FUNCTION, FFL_NOSPAWN},
|
|
{"dodge", FOFS(cast_info.dodge), F_FUNCTION, FFL_NOSPAWN},
|
|
{"attack", FOFS(cast_info.attack), F_FUNCTION, FFL_NOSPAWN},
|
|
{"long_attack", FOFS(cast_info.long_attack), F_FUNCTION, FFL_NOSPAWN},
|
|
{"sight", FOFS(cast_info.sight), F_FUNCTION, FFL_NOSPAWN},
|
|
{"duck", FOFS(cast_info.duck), F_FUNCTION, FFL_NOSPAWN},
|
|
{"talk", FOFS(cast_info.talk), F_FUNCTION, FFL_NOSPAWN},
|
|
{"avoid", FOFS(cast_info.avoid), F_FUNCTION, FFL_NOSPAWN},
|
|
|
|
{"backoff", FOFS(cast_info.backoff), F_FUNCTION, FFL_NOSPAWN},
|
|
|
|
{"catch_fire", FOFS(cast_info.catch_fire), F_FUNCTION, FFL_NOSPAWN},
|
|
{"checkattack", FOFS(cast_info.checkattack), F_FUNCTION, FFL_NOSPAWN},
|
|
{"currentmove", FOFS(cast_info.currentmove), F_MMOVE, FFL_NOSPAWN},
|
|
|
|
{"oldcurrentmove", FOFS(cast_info.oldcurrentmove), F_MMOVE, FFL_NOSPAWN},
|
|
|
|
{"move_stand", FOFS(cast_info.move_stand), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_crstand", FOFS(cast_info.move_crstand), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_run", FOFS(cast_info.move_run), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_runwalk", FOFS(cast_info.move_runwalk), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_crwalk", FOFS(cast_info.move_crwalk), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_jump", FOFS(cast_info.move_jump), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_crouch_down", FOFS(cast_info.move_crouch_down), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_stand_up", FOFS(cast_info.move_stand_up), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_avoid_walk", FOFS(cast_info.move_avoid_walk), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_avoid_run", FOFS(cast_info.move_avoid_run), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_avoid_reverse_walk", FOFS(cast_info.move_avoid_reverse_walk), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_avoid_reverse_run", FOFS(cast_info.move_avoid_reverse_run), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_avoid_crwalk", FOFS(cast_info.move_avoid_crwalk), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_lside_step", FOFS(cast_info.move_lside_step), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_rside_step", FOFS(cast_info.move_rside_step), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_start_climb", FOFS(cast_info.move_start_climb), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_end_climb", FOFS(cast_info.move_end_climb), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_evade", FOFS(cast_info.move_evade), F_MMOVE, FFL_NOSPAWN},
|
|
{"move_stand_evade", FOFS(cast_info.move_stand_evade), F_MMOVE, FFL_NOSPAWN},
|
|
|
|
|
|
{"avoid_ent", FOFS(cast_info.avoid_ent), F_EDICT, FFL_NOSPAWN},
|
|
{"talk_ent", FOFS(cast_info.talk_ent), F_EDICT, FFL_NOSPAWN},
|
|
|
|
{"friend_memory", FOFS(cast_info.friend_memory), F_CAST_MEMORY, FFL_NOSPAWN},
|
|
{"neutral_memory", FOFS(cast_info.neutral_memory), F_CAST_MEMORY, FFL_NOSPAWN},
|
|
{"enemy_memory", FOFS(cast_info.enemy_memory), F_CAST_MEMORY, FFL_NOSPAWN},
|
|
|
|
{"endfunc", FOFS(moveinfo.endfunc), F_FUNCTION, FFL_NOSPAWN},
|
|
|
|
|
|
// 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},
|
|
|
|
//need for item field in edict struct, FFL_SPAWNTEMP item will be skipped on saves
|
|
{"item", FOFS(item), F_ITEM},
|
|
|
|
{"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP},
|
|
{"sky", STOFS(sky), F_LSTRING, 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},
|
|
{"fogdensity", STOFS(fogdensity), F_FLOAT, FFL_SPAWNTEMP},
|
|
{"fogval", STOFS(fogval), F_VECTOR, FFL_SPAWNTEMP},
|
|
|
|
{"fogdensity2", STOFS(fogdensity2), F_FLOAT, FFL_SPAWNTEMP},
|
|
{"fogval2", STOFS(fogval2), F_VECTOR, FFL_SPAWNTEMP},
|
|
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
field_t levelfields[] =
|
|
{
|
|
{"changemap", LLOFS(changemap), F_LSTRING},
|
|
|
|
{"sight_client", LLOFS(sight_client), F_EDICT},
|
|
{"sight_entity", LLOFS(sight_entity), F_EDICT},
|
|
{"sound_entity", LLOFS(sound_entity), F_EDICT},
|
|
{"sound2_entity", LLOFS(sound2_entity), F_EDICT},
|
|
|
|
{"characters", LLOFS(characters), F_IGNORE},
|
|
|
|
{NULL, 0, F_INT}
|
|
};
|
|
|
|
field_t clientfields[] =
|
|
{
|
|
{"pers.weapon", CLOFS(pers.weapon), F_ITEM},
|
|
{"pers.holsteredweapon", CLOFS(pers.holsteredweapon), F_ITEM},
|
|
{"pers.lastweapon", CLOFS(pers.lastweapon), F_ITEM},
|
|
{"newweapon", CLOFS(newweapon), F_ITEM},
|
|
{NULL, 0, F_INT}
|
|
};
|
|
|
|
field_t castmemoryfields[] =
|
|
{
|
|
{"response", CMOFS(response), F_FUNCTION},
|
|
{"next", CMOFS(next), F_CAST_MEMORY},
|
|
{"prev", CMOFS(prev), F_CAST_MEMORY},
|
|
|
|
{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 InitGame (void)
|
|
{
|
|
gi.dprintf ("==== InitGame ====\n");
|
|
|
|
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", "0", 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);
|
|
|
|
// JOSEPH 16-OCT-98
|
|
maxentities = gi.cvar ("maxentities", /*"1024"*/"2048", CVAR_LATCH);
|
|
|
|
// RAFAEL
|
|
// marines = gi.cvar ("marines", "0", CVAR_ARCHIVE);
|
|
|
|
// change anytime vars
|
|
dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO|CVAR_ARCHIVE);
|
|
fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO);
|
|
timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO);
|
|
cashlimit = gi.cvar ("cashlimit", "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);
|
|
|
|
// flood control
|
|
flood_msgs = gi.cvar ("flood_msgs", "4", 0);
|
|
flood_persecond = gi.cvar ("flood_persecond", "4", 0);
|
|
flood_waitdelay = gi.cvar ("flood_waitdelay", "10", 0);
|
|
|
|
// Ridah, new cvar's
|
|
developer = gi.cvar ("developer", "0", 0);
|
|
|
|
maxrate = gi.cvar ("maxrate", "25000", CVAR_SERVERINFO);
|
|
|
|
ai_debug_memory = gi.cvar ("ai_debug_memory", "0", 0);
|
|
|
|
g_vehicle_test = gi.cvar ("g_vehicle_test", "0", CVAR_LATCH); // Enables Hovercars for all players
|
|
|
|
dm_locational_damage = gi.cvar ("dm_locational_damage", "0", CVAR_SERVERINFO);
|
|
|
|
showlights = gi.cvar ("showlights", "0", 0);
|
|
|
|
r_directional_lighting = gi.cvar ("r_directional_lighting", "1", CVAR_ARCHIVE);
|
|
|
|
cl_captions = gi.cvar ("cl_captions", "0", CVAR_ARCHIVE); // Ridah, disabled this by default, is that cool?
|
|
|
|
sv_runscale = gi.cvar ("sv_runscale", "1.0", 0); // only effective in Deathmatch
|
|
|
|
burn_enabled = gi.cvar("burn_enabled", "0", 0);
|
|
burn_size = gi.cvar("burn_size", "48", 0);
|
|
burn_intensity = gi.cvar("burn_intensity", "0.03", 0);
|
|
burn_r = gi.cvar("burn_r", "1.0", 0);
|
|
burn_g = gi.cvar("burn_g", "1.0", 0);
|
|
burn_b = gi.cvar("burn_b", "1.0", 0);
|
|
|
|
timescale = gi.cvar("timescale", "1.0", 0);
|
|
|
|
teamplay = gi.cvar("teamplay", "0", CVAR_LATCH|CVAR_SERVERINFO);
|
|
g_cashspawndelay = gi.cvar("g_cashspawndelay", "5", CVAR_ARCHIVE|CVAR_LATCH);
|
|
|
|
// this is only used for single player games
|
|
cl_parental_lock = gi.cvar( "cl_parental_lock", "0", CVAR_NOSET);
|
|
cl_parental_override = gi.cvar( "cl_parental_override", "0", CVAR_NOSET);
|
|
|
|
dm_realmode = gi.cvar( "dm_realmode", "0", CVAR_LATCH|CVAR_SERVERINFO);
|
|
|
|
g_mapcycle_file = gi.cvar( "g_mapcycle_file", "", 0);
|
|
// Ridah, done.
|
|
|
|
// 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;
|
|
|
|
g_cast_memory = gi.TagMalloc (MAX_CHARACTERS * MAX_CHARACTERS * sizeof(cast_memory_t), TAG_GAME );
|
|
memset( g_cast_memory, 0, MAX_CHARACTERS * MAX_CHARACTERS * sizeof(cast_memory_t) );
|
|
|
|
g_cast_groups = gi.TagMalloc (MAX_CAST_GROUPS * sizeof(cast_group_t), TAG_GAME );
|
|
memset( g_cast_groups, 0, MAX_CAST_GROUPS * sizeof(cast_group_t) );
|
|
|
|
// 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;
|
|
}
|
|
|
|
//=========================================================
|
|
|
|
void WriteField1 (FILE *f, field_t *field, byte *base)
|
|
{
|
|
void *p;
|
|
int len;
|
|
int index;
|
|
|
|
if (field->flags & FFL_SPAWNTEMP)
|
|
return;
|
|
|
|
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 - itemlist;
|
|
*(int *)p = index;
|
|
break;
|
|
|
|
case F_CAST_MEMORY:
|
|
if ( *(cast_memory_t **)p == NULL)
|
|
index = -1;
|
|
else
|
|
index = *(cast_memory_t **)p - g_cast_memory;
|
|
*(int *)p = index;
|
|
break;
|
|
|
|
//relative to code segment
|
|
case F_FUNCTION:
|
|
if (*(byte **)p == NULL)
|
|
index = 0;
|
|
else
|
|
index = *(byte **)p - ((byte *)InitGame);
|
|
*(int *)p = index;
|
|
break;
|
|
|
|
//relative to data segment
|
|
case F_MMOVE:
|
|
if (*(byte **)p == NULL)
|
|
index = 0;
|
|
else
|
|
index = *(byte **)p - (byte *)&mmove_reloc;
|
|
*(int *)p = index;
|
|
break;
|
|
|
|
default:
|
|
gi.error ("WriteEdict: unknown field type");
|
|
}
|
|
}
|
|
|
|
|
|
void WriteField2 (FILE *f, field_t *field, byte *base)
|
|
{
|
|
int len;
|
|
void *p;
|
|
|
|
if (field->flags & FFL_SPAWNTEMP)
|
|
return;
|
|
|
|
p = (void *)(base + field->ofs);
|
|
switch (field->type)
|
|
{
|
|
case F_LSTRING:
|
|
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;
|
|
|
|
if (field->flags & FFL_SPAWNTEMP)
|
|
return;
|
|
|
|
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_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;
|
|
|
|
case F_CAST_MEMORY:
|
|
index = *(int *)p;
|
|
if ( index == -1 )
|
|
*(cast_memory_t **)p = NULL;
|
|
else
|
|
*(cast_memory_t **)p = &g_cast_memory[index];
|
|
break;
|
|
|
|
//relative to code segment
|
|
case F_FUNCTION:
|
|
index = *(int *)p;
|
|
if ( index == 0 )
|
|
*(byte **)p = NULL;
|
|
else
|
|
*(byte **)p = ((byte *)InitGame) + index;
|
|
break;
|
|
|
|
//relative to data segment
|
|
case F_MMOVE:
|
|
index = *(int *)p;
|
|
if (index == 0)
|
|
*(byte **)p = NULL;
|
|
else
|
|
*(byte **)p = (byte *)&mmove_reloc + 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));
|
|
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]);
|
|
|
|
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;
|
|
|
|
g_cast_memory = gi.TagMalloc (MAX_CHARACTERS * MAX_CHARACTERS * sizeof(cast_memory_t), TAG_GAME );
|
|
memset( g_cast_memory, 0, MAX_CHARACTERS * MAX_CHARACTERS * sizeof(cast_memory_t) );
|
|
|
|
g_cast_groups = gi.TagMalloc (MAX_CAST_GROUPS * sizeof(cast_group_t), TAG_GAME );
|
|
memset( g_cast_groups, 0, MAX_CAST_GROUPS * sizeof(cast_group_t) );
|
|
|
|
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=fields ; field->name ; field++)
|
|
{
|
|
WriteField1 (f, field, (byte *)&temp);
|
|
}
|
|
|
|
temp.last_voice = NULL; // this can't be saved
|
|
|
|
// write the block
|
|
fwrite (&temp, sizeof(temp), 1, f);
|
|
|
|
// now write any allocated data following the edict
|
|
for (field=fields ; 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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
WriteCastMemories
|
|
==============
|
|
*/
|
|
void WriteCastMemories (FILE *f)
|
|
{
|
|
field_t *field;
|
|
cast_memory_t temp;
|
|
int i, j, out;
|
|
|
|
for (i=0; i<MAX_CHARACTERS; i++)
|
|
{
|
|
for (j=0; j<MAX_CHARACTERS; j++)
|
|
{
|
|
if (!g_cast_memory[i * MAX_CHARACTERS + j].cast_ent)
|
|
continue;
|
|
|
|
memcpy( &temp, &(g_cast_memory[i * MAX_CHARACTERS + j]), sizeof(cast_memory_t) );
|
|
|
|
// write the index number
|
|
out = i * MAX_CHARACTERS + j;
|
|
fwrite (&out, sizeof(out), 1, f);
|
|
|
|
// change the pointers to lengths or indexes
|
|
for (field=castmemoryfields ; field->name ; field++)
|
|
{
|
|
WriteField1 (f, field, (byte *)(&temp));
|
|
}
|
|
|
|
// write the block
|
|
fwrite (&temp, sizeof(cast_memory_t), 1, f);
|
|
|
|
// now write any allocated data following the edict
|
|
for (field=castmemoryfields ; field->name ; field++)
|
|
{
|
|
WriteField2 (f, field, (byte *)(&(g_cast_memory[i * MAX_CHARACTERS + j])));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
out = -1;
|
|
fwrite (&out, sizeof(out), 1, f);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
WriteCastGroups
|
|
==============
|
|
*/
|
|
void WriteCastGroups (FILE *f)
|
|
{
|
|
// write the block
|
|
fwrite (g_cast_groups, sizeof(cast_group_t) * MAX_CAST_GROUPS, 1, f);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ReadCastGroups
|
|
==============
|
|
*/
|
|
void ReadCastGroups (FILE *f)
|
|
{
|
|
// write the block
|
|
fread (g_cast_groups, sizeof(cast_group_t) * MAX_CAST_GROUPS, 1, f);
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
ReadEdict
|
|
|
|
All pointer variables (except function pointers) must be handled specially.
|
|
==============
|
|
*/
|
|
void ReadEdict (FILE *f, edict_t *ent)
|
|
{
|
|
field_t *field;
|
|
|
|
// Ridah, save the object_bounds
|
|
int object_bounds[MAX_MODEL_PARTS][MAX_MODELPART_OBJECTS];
|
|
int i;
|
|
|
|
for (i=0; i<MAX_MODEL_PARTS; i++)
|
|
memcpy( object_bounds[i], ent->s.model_parts[i].object_bounds, sizeof(int)*MAX_MODELPART_OBJECTS );
|
|
|
|
fread (ent, sizeof(*ent), 1, f);
|
|
|
|
for (field=fields ; field->name ; field++)
|
|
{
|
|
ReadField (f, field, (byte *)ent);
|
|
}
|
|
|
|
// Ridah, restore object_bounds
|
|
for (i=0; i<MAX_MODEL_PARTS; i++)
|
|
memcpy( ent->s.model_parts[i].object_bounds, object_bounds[i], sizeof(int)*MAX_MODELPART_OBJECTS );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ReadLevelLocals
|
|
|
|
All pointer variables (except function pointers) must be handled specially.
|
|
==============
|
|
*/
|
|
void ReadLevelLocals (FILE *f)
|
|
{
|
|
field_t *field;
|
|
|
|
int i, j;
|
|
|
|
fread (&level, sizeof(level), 1, f);
|
|
|
|
for (field=levelfields ; field->name ; field++)
|
|
{
|
|
ReadField (f, field, (byte *)&level);
|
|
}
|
|
|
|
// setup the global cast memory
|
|
for (i=0; i<MAX_CHARACTERS; i++)
|
|
{
|
|
for (j=0; j<MAX_CHARACTERS; j++)
|
|
{
|
|
if (g_cast_memory[i * MAX_CHARACTERS + j].cast_ent)
|
|
{
|
|
level.global_cast_memory[i][j] = &(g_cast_memory[i * MAX_CHARACTERS + j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ReadCastMemories
|
|
|
|
All pointer variables (except function pointers) must be handled specially.
|
|
==============
|
|
*/
|
|
void ReadCastMemories (FILE *f)
|
|
{
|
|
field_t *field;
|
|
int i;
|
|
|
|
while (1)
|
|
{
|
|
fread (&i, sizeof(i), 1, f);
|
|
|
|
if (i < 0)
|
|
break;
|
|
|
|
fread (&(g_cast_memory[i]), sizeof(cast_memory_t), 1, f);
|
|
|
|
for (field=castmemoryfields ; field->name ; field++)
|
|
{
|
|
ReadField (f, field, (byte *)&(g_cast_memory[i]) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
WriteLevel
|
|
|
|
=================
|
|
*/
|
|
void WriteLevel (char *filename)
|
|
{
|
|
int i;
|
|
edict_t *ent;
|
|
FILE *f;
|
|
void *base;
|
|
// BEGIN: Xatrix/Ridah/Navigator/18-apr-1998
|
|
active_node_data_t *node_data;
|
|
// END: Xatrix/Ridah/Navigator/18-apr-1998
|
|
|
|
f = fopen (filename, "wb");
|
|
if (!f)
|
|
gi.error ("Couldn't open %s", filename);
|
|
|
|
// BEGIN: Xatrix/Ridah/Navigator/18-apr-1998
|
|
// don't save the nav data
|
|
node_data = level.node_data;
|
|
level.node_data = NULL;
|
|
// END: Xatrix/Ridah/Navigator/18-apr-1998
|
|
|
|
// 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 the team data
|
|
WriteCastGroups (f);
|
|
|
|
// write out the cast_memory data
|
|
WriteCastMemories (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);
|
|
|
|
|
|
// write the cast_memory data
|
|
|
|
|
|
fclose (f);
|
|
|
|
// BEGIN: Xatrix/Ridah/Navigator/18-apr-1998
|
|
// restore the nav data
|
|
level.node_data = node_data;
|
|
// END: Xatrix/Ridah/Navigator/18-apr-1998
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
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.
|
|
=================
|
|
*/
|
|
|
|
qboolean changing_levels=false;
|
|
|
|
void ReadLevel (char *filename)
|
|
{
|
|
int entnum;
|
|
FILE *f;
|
|
int i;
|
|
void *base;
|
|
edict_t *ent;
|
|
|
|
// BEGIN: Xatrix/Ridah/Navigator/18-apr-1998
|
|
// restore nav data
|
|
active_node_data_t *node_data;
|
|
// END: Xatrix/Ridah/Navigator/18-apr-1998
|
|
|
|
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);
|
|
|
|
gi.ClearObjectBoundsCached(); // make sure we wipe the cached list
|
|
|
|
num_object_bounds = 0;
|
|
memset (g_objbnds, 0, sizeof(g_objbnds));
|
|
|
|
// wipe all the entities
|
|
memset (g_edicts, 0, game.maxentities*sizeof(g_edicts[0]));
|
|
globals.num_edicts = maxclients->value+1;
|
|
|
|
memset( g_cast_memory, 0, MAX_CHARACTERS * MAX_CHARACTERS * sizeof(cast_memory_t) );
|
|
memset( g_cast_groups, 0, MAX_CAST_GROUPS * sizeof(cast_group_t) );
|
|
|
|
// 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);
|
|
#ifdef _WIN32
|
|
if (base != (void *)InitGame)
|
|
{
|
|
fclose (f);
|
|
gi.error ("ReadLevel: function pointers have moved");
|
|
}
|
|
#else
|
|
gi.dprintf("Function offsets %d\n", ((byte *)base) - ((byte *)InitGame));
|
|
#endif
|
|
|
|
// BEGIN: Xatrix/Ridah/Navigator/18-apr-1998
|
|
// save nav data
|
|
node_data = level.node_data;
|
|
level.node_data = NULL;
|
|
// END: Xatrix/Ridah/Navigator/18-apr-1998
|
|
|
|
// read the team data
|
|
ReadCastGroups (f);
|
|
|
|
// load the cast memories
|
|
ReadCastMemories (f);
|
|
|
|
// load the level locals
|
|
ReadLevelLocals (f);
|
|
|
|
// BEGIN: Xatrix/Ridah/Navigator/18-apr-1998
|
|
// restore nav data
|
|
level.node_data = node_data;
|
|
|
|
// upon changing levels, the nav data is cleared, but when loading or starting a new game,
|
|
// the node data is loading in SpawnEntities()
|
|
if (!node_data)
|
|
{
|
|
level.node_data = gi.TagMalloc (sizeof (active_node_data_t), TAG_GAME);
|
|
NAV_ReadActiveNodes (level.node_data, level.mapname);
|
|
}
|
|
// END: Xatrix/Ridah/Navigator/18-apr-1998
|
|
|
|
// 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);
|
|
|
|
// Ridah, restore the object bounds data
|
|
for (i=0; i<MAX_MODEL_PARTS; i++)
|
|
{
|
|
if (ent->s.model_parts[i].objectbounds_filename)
|
|
{
|
|
gi.GetObjectBounds( ent->s.model_parts[i].objectbounds_filename, &ent->s.model_parts[i] );
|
|
}
|
|
}
|
|
|
|
// BEGIN: Xatrix/Ridah/Navigator/16-apr-1998
|
|
// Init Navigational data for this entity
|
|
ent->active_node_data = level.node_data;
|
|
ent->nav_data.cache_node = -1;
|
|
ent->nav_build_data = NULL; // make sure it's null, since it'll be set in ClientConnect() anyway
|
|
// END: Xatrix/Ridah/Navigator/16-apr-1998
|
|
|
|
// 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;
|
|
}
|
|
|
|
// init the characters array (we'll set it manually
|
|
memset( level.characters, 0, 4 * MAX_CHARACTERS );
|
|
|
|
// always set the client first
|
|
level.characters[0] = &g_edicts[1];
|
|
|
|
// do any load time things at this point
|
|
for (i=0 ; i<globals.num_edicts ; i++)
|
|
{
|
|
ent = &g_edicts[i];
|
|
|
|
if (!ent->inuse)
|
|
continue;
|
|
|
|
// set the character array
|
|
if (((ent->svflags & SVF_MONSTER) && (ent->character_index > 0)) || ent->client)
|
|
{
|
|
level.characters[ent->character_index] = ent;
|
|
}
|
|
|
|
// fire any cross-level triggers
|
|
if (ent->classname)
|
|
if (strcmp(ent->classname, "target_crosslevel_target") == 0)
|
|
ent->nextthink = level.time + ent->delay;
|
|
|
|
// JOSEPH 19-JAN-99
|
|
// Restore rotating train absmax absmin
|
|
if (!strcmp(ent->classname, "func_train_rotating"))
|
|
{
|
|
float max, v;
|
|
int i;
|
|
|
|
max = 0;
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
v =fabs(ent->mins[i]);
|
|
if (v > max)
|
|
max = v;
|
|
v =fabs(ent->maxs[i]);
|
|
if (v > max)
|
|
max = v;
|
|
}
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
ent->absmin[i] = ent->s.origin[i] - max;
|
|
ent->absmax[i] = ent->s.origin[i] + max;
|
|
}
|
|
}
|
|
// END JOSEPH
|
|
|
|
// Ridah, restore nextthink times for rain/snow clouds so they send the message to clients (this is ugly, but should work)
|
|
if ( !strcmp( ent->classname, "elements_raincloud" )
|
|
|| !strcmp( ent->classname, "elements_snowcloud" ))
|
|
{
|
|
ent->nextthink = level.time + (10 * FRAMETIME);
|
|
}
|
|
}
|
|
|
|
|
|
if (changing_levels)
|
|
{
|
|
int i;
|
|
edict_t *e;
|
|
|
|
// kill any followers
|
|
for (i=0; i<level.num_characters; i++)
|
|
{
|
|
e = level.characters[i];
|
|
|
|
if (!e)
|
|
continue;
|
|
|
|
if (e->flags & FL_FOLLOWING)
|
|
{
|
|
// vanish!
|
|
AI_UnloadCastMemory( e );
|
|
G_FreeEdict( e );
|
|
}
|
|
}
|
|
|
|
}
|
|
else // clear any following flags
|
|
{
|
|
int i;
|
|
edict_t *e;
|
|
|
|
// kill any followers
|
|
for (i=0; i<level.num_characters; i++)
|
|
{
|
|
e = level.characters[i];
|
|
|
|
if (!e)
|
|
continue;
|
|
|
|
e->flags &= ~FL_FOLLOWING;
|
|
}
|
|
}
|
|
|
|
}
|