//----------------------------------------------------------------------------- // // $Logfile:: /EF2/Code/DLLs/game/level.cpp $ // $Revision:: 90 $ // $Date:: 9/26/03 2:36p $ // // Copyright (C) 1999 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // // DESCRIPTION: // #include "_pch_cpp.h" #include "level.h" #include "scriptmaster.h" #include "navigate.h" #include "helper_node.h" #include "gravpath.h" #include "g_spawn.h" #include "player.h" #include "characterstate.h" #include "mp_manager.hpp" #include "armor.h" #include "CinematicArmature.h" #include "earthquake.h" #include "teammateroster.hpp" Level level; extern Container SpecialPathNodes; CLASS_DECLARATION( Class, Level, NULL ) { { NULL, NULL } }; Level::Level() { Init(); } Level::~Level() { _earthquakes.FreeObjectList(); } void Level::Init( void ) { spawn_entnum = -1; restart = false;; framenum = 0; time = 0; frametime = 0; level_name = ""; mapname = ""; spawnpoint = ""; nextmap = ""; playerfrozen = false; intermissiontime = 0; exitintermission = 0; next_edict = NULL; total_secrets = 0; found_secrets = 0; total_specialItems = 0; found_specialItems = 0; _totalEnemiesSpawned = 0; memset( &impact_trace, 0, sizeof( impact_trace ) ); cinematic = false; ai_on = true; mission_failed = false; died_already = false; near_exit = false; started = false; water_color = vec_zero; water_alpha = 0; slime_color = vec_zero; slime_alpha = 0; lava_color = vec_zero; lava_alpha = 0; saved_soundtrack = ""; current_soundtrack = ""; consoleThread = NULL; // clear out automatic cameras automatic_cameras.ClearObjectList(); // init level script variables levelVars.ClearList(); m_fade_time_start = 0; m_fade_time = -1; m_fade_color = vec_zero; m_fade_alpha = 0; m_fade_style = additive; m_fade_type = fadein; m_letterbox_fraction = 0; m_letterbox_time = -1; m_letterbox_time_start = 0; m_letterbox_dir = letterbox_out; hNodeController = NULL; _cleanup = false; _showIntermission = true; _saveOrientation = true; currentInstanceNumber = 0; } //----------------------------------------------------- // // Name: update // Class: Level // // Description: Updates the level variables. This is called on the frame update. // // Parameters: levelTime - the game clock time. // frameTime - the time the frame occured. // // Returns: None //----------------------------------------------------- void Level::update( int levelTime, int frameTime ) { setTime(levelTime, frameTime); if ( level.intermissiontime && dedicated->integer ) { if ( g_endintermission->integer > 0 ) { g_endintermission->integer = 0; level.exitintermission = true; } // can exit intermission after 10 seconds (default) if ( ( ( level.time - level.intermissiontime ) > level.intermission_advancetime ) && ( level.intermission_advancetime != 0 ) ) { if ( multiplayerManager.inMultiplayer() ) { level.exitintermission = true; } } } } void Level::SetIntermissionAdvanceTime(float time) { intermission_advancetime = time; } void Level::EndIntermission() { exitintermission = true; } void Level::CleanUp( qboolean restart ) { _cleanup = true; if ( multiplayerManager.inMultiplayer() ) { multiplayerManager.cleanup( restart ); } else { Player* p = GetPlayer(0); if( p ) p->LevelCleanup(); } ClearCachedStatemaps(); ClearCachedFuzzyEngines(); _playerDeathThread = ""; #ifndef DEDICATED gi.MObjective_ClearObjectiveList(); #endif AdaptiveArmor::ClearAdaptionList(); theCinematicArmature.clearCinematicsList(); HelperNode::CleanupHelperNodeList(); assert( active_edicts.next ); assert( active_edicts.next->prev == &active_edicts ); assert( active_edicts.prev ); assert( active_edicts.prev->next == &active_edicts ); assert( free_edicts.next ); assert( free_edicts.next->prev == &free_edicts ); assert( free_edicts.prev ); assert( free_edicts.prev->next == &free_edicts ); while( active_edicts.next != &active_edicts ) { assert( active_edicts.next != &free_edicts ); assert( active_edicts.prev != &free_edicts ); assert( active_edicts.next ); assert( active_edicts.next->prev == &active_edicts ); assert( active_edicts.prev ); assert( active_edicts.prev->next == &active_edicts ); assert( free_edicts.next ); assert( free_edicts.next->prev == &free_edicts ); assert( free_edicts.prev ); assert( free_edicts.prev->next == &free_edicts ); if ( active_edicts.next->entity ) { delete active_edicts.next->entity; } else { FreeEdict( active_edicts.next ); } } cinematic = false; ai_on = true; mission_failed = false; died_already = false; near_exit = false; started = false; globals.num_entities = game.maxclients + 1; // clear up all AI node information thePathManager.ResetNodes(); // Reset the gravity paths gravPathManager.Reset(); if ( consoleThread ) { Director.KillThread( consoleThread->ThreadNum() ); consoleThread = NULL; } // close all the scripts Director.CloseScript(); // invalidate player readiness Director.PlayerNotReady(); // clear out automatic cameras automatic_cameras.ClearObjectList(); // clear out level script variables levelVars.ClearList(); // initialize the game variables // these get restored by the persistant data, so we can safely clear them here gameVars.ClearList(); // clearout any waiting events L_ClearEventList(); ResetEdicts(); // Reset the boss health cvar gi.cvar_set( "bosshealth", "0" ); _earthquakes.ClearObjectList(); _cleanup = false; } /* ============== ResetEdicts ============== */ void Level::ResetEdicts( void ) { int i; memset( g_entities, 0, game.maxentities * sizeof( g_entities[ 0 ] ) ); // Add all the edicts to the free list LL_Reset( &free_edicts, next, prev ); LL_Reset( &active_edicts, next, prev ); for( i = 0; i < game.maxentities; i++ ) { LL_Add( &free_edicts, &g_entities[ i ], next, prev ); } for( i = 0; i < game.maxclients; i++ ) { //char savedTeamName[ 16 ]; // set client fields on player ents g_entities[ i ].client = game.clients + i; //strcpy( savedTeamName, game.clients[i].pers.lastTeam ); G_InitClientPersistant (&game.clients[i]); //strcpy( game.clients[i].pers.lastTeam, savedTeamName ); } globals.num_entities = game.maxclients; } /* ============== Start Does all post-spawning setup. This is NOT called for savegames. ============== */ void Level::Start( void ) { CThread *gamescript; // initialize secrets levelVars.SetVariable( "total_secrets", total_secrets ); levelVars.SetVariable( "found_secrets", found_secrets ); levelVars.SetVariable( "total_specialItems" , total_specialItems ); levelVars.SetVariable( "found_specialItems" , found_specialItems ); levelVars.SetVariable( "total_enemies_spawned", _totalEnemiesSpawned ); FindTeams(); // call the precache scripts Precache(); // start executing the game script if ( game_script.length() ) { gi.ProcessLoadingScreen( "$$LoadingScript$$" ); program.Load( game_script ); gi.ProcessLoadingScreen( "$$DoneLoadingScript$$" ); // Create the main thread gamescript = Director.CreateThread( "main" ); if ( gamescript ) { // Run the precache thread if it exists if ( gamescript->labelExists( "precache" ) ) { CThread *precache_script; precache_script = Director.CreateThread( "precache" ); if ( precache_script ) precache_script->DelayedStart( 0.0f ); } // Run the main thread gamescript->DelayedStart( 0.0f ); } } loadLevelStrings(); started = true; } //---------------------------------------------------------------- // Name: postLoad // Class: // // Description: Does everything necessary to the level after a load has happened // // Parameters: None // // Returns: none //---------------------------------------------------------------- void Level::postLoad( void ) { thePathManager.FindAllTargets(); TeammateRoster::getInstance()->clearTeammates(); //thePathManager.InsertNodesIntoGrid(); //thePathManager.ConnectPathNodes(); //thePathManager.OptimizeNodes( NULL ); thePathManager.SavePaths(); } //---------------------------------------------------------------- // Name: postSublevelLoad // Class: Level // // Description: Does everything necessary to the level after a sublevel has been loaded // // Parameters: const char *mapName - map name string (also possibly contains spawn position) // // Returns: none //---------------------------------------------------------------- void Level::postSublevelLoad( const char *spawnPosName ) { int i; gentity_t *ent; Player *player; // Save off the spawn position spawnpoint = spawnPosName; // Make sure the player starts in the correct place for( i = 0; i < game.maxclients; i++ ) { ent = &g_entities[ i ]; if ( !ent->inuse || !ent->client || !ent->entity ) continue; if ( ent->entity->isSubclassOf( Player ) ) { player = (Player *)ent->entity; player->ChooseSpawnPoint(); } } // Get rid of any fading G_ClearFade(); } qboolean Level::inhibitEntity( int spawnflags ) { if ( !developer->integer && ( spawnflags & SPAWNFLAG_DEVELOPMENT ) ) { return true; } if ( !detail->integer && ( spawnflags & SPAWNFLAG_DETAIL ) ) { return true; } if ( multiplayerManager.inMultiplayer() ) { if ( spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { return true; } return false; } switch( skill->integer ) { case 0 : return ( spawnflags & SPAWNFLAG_NOT_EASY ) != 0; break; case 1 : return ( spawnflags & SPAWNFLAG_NOT_MEDIUM ) != 0; break; case 2 : case 3 : return ( spawnflags & SPAWNFLAG_NOT_HARD ); break; } return false; } void Level::setSkill( int value ) { int skill_level; skill_level = static_cast(floor(static_cast(value))); skill_level = bound( skill_level, 0, 3 ); gi.cvar_set( "skill", va( "%d", skill_level ) ); gameVars.SetVariable( "skill", skill_level ); } int Level::getSkill( void ) { ScriptVariable* skill_var = gameVars.GetVariable("skill"); return skill_var->intValue(); } void Level::setTime( int levelTime, int frameTime ) { inttime = levelTime; fixedframetime = 1.0f / sv_fps->value; frametime = ( ( float )frameTime / 1000.0f ); time = ( ( float )levelTime / 1000.0f ); if(intermissiontime == 0.0f && mission_failed == false) timeInLevel = time; } /* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void Level::SpawnEntities( const char *themapname, const char *entities, int levelTime ) { int inhibit,count=0; const char *value; SpawnArgs args; char *spawnpos; // Init the level variables Init(); spawnpos = const_cast(strchr( themapname, '$' )); if ( spawnpos ) { mapname = str( themapname, 0, spawnpos - themapname ); spawnpoint = spawnpos + 1; } else { mapname = themapname; spawnpoint = ""; } // set up time so functions still have valid times setTime( levelTime, 1000 / 20 ); if ( !LoadingServer ) { // Get rid of anything left over from the last level //CleanUp( false ); // Set up for a new map thePathManager.Init( mapname ); } setSkill( skill->integer ); // reset out count of the number of game traces sv_numtraces = 0; // parse world entities = args.Parse( entities ); spawn_entnum = ENTITYNUM_WORLD; args.Spawn(); if ( !world ) Com_Error( ERR_FATAL, "No world\n" ); if ( g_gametype->integer == GT_MULTIPLAYER || g_gametype->integer == GT_BOT_SINGLE_PLAYER ) { multiplayerManager.initMultiplayerGame(); } // parse ents inhibit = 0; for( entities = args.Parse( entities ); entities != NULL; entities = args.Parse( entities ) ) { // remove things (except the world) from different skill levels or deathmatch spawnflags = 0; value = args.getArg( "spawnflags" ); if ( value ) { spawnflags = atoi( value ); if ( inhibitEntity( spawnflags ) ) { inhibit++; continue; } } args.Spawn(); count++; gi.ProcessLoadingScreen( "$$SpawningEntities$$" ); } gi.DPrintf( "%i entities spawned\n", count ); gi.DPrintf( "%i entities inhibited\n", inhibit ); // Process the spawn events L_ProcessPendingEvents(); if ( multiplayerManager.inMultiplayer() ) { multiplayerManager.initItems(); } // Setup bots if ( gi.Cvar_VariableIntegerValue( "bot_enable" ) && multiplayerManager.inMultiplayer() ) { BotAIShutdown( 0 ); BotAISetup( 0 ); BotAILoadMap( 0 ); G_InitBots( 0 ); } if ( !LoadingServer || game.autosaved ) { Start(); } postLoad(); //------------------------------------------------------------------------------- // // Deletion Note: // Since hNodeController is an Entity, it is deleted // when all the other entities are deleted in the clean up function // specifically the line // // // if ( active_edicts.next->entity ) // { // delete active_edicts.next->entity; // } // // // Since it is already being deleted like this // We do not need to explcitily delete the controller... In fact // you will error out if you try to. //-------------------------------------------------------------------------------- hNodeController = new HelperNodeController; if ( hNodeController ) hNodeController->SetTargetName( "HelperNodeController" ); // // if this is a single player game, spawn the single player in now // this allows us to read persistant data into the player before the client // is completely ready // if ( game.maxclients == 1 ) { spawn_entnum = 0; new Player; } } void Level::NewMap( const char *mapname, const char *entities, int levelTime ) { theCinematicArmature.deleteAllCinematics(); current_map = mapname; current_entities = entities; SpawnEntities( current_map, current_entities, levelTime ); } void Level::Restart( void ) { theCinematicArmature.deleteAllCinematics(); SpawnEntities( current_map, current_entities, inttime ); L_ProcessPendingEvents(); G_ClientConnect( 0, true, false, true ); G_ClientBegin( &g_entities[ 0 ], NULL ); } void Level::PlayerRestart( void ) { // we need to restart through the server code gi.SendConsoleCommand( "restart\n" ); //restart = true; level.mission_failed = false; } void Level::Archive( Archiver &arc ) { int num; int i; Class::Archive( arc ); if ( arc.Saving() ) { SafePtr ent; num = _earthquakes.NumObjects(); arc.ArchiveInteger( &num ); for ( i = 1 ; i <= num ; i++ ) { ent = _earthquakes.ObjectAt( i ); arc.ArchiveSafePointer( &ent ); } } else { SafePtr ent; SafePtr *entityPointer; arc.ArchiveInteger( &num ); _earthquakes.ClearObjectList(); _earthquakes.Resize( num ); for ( i = 1 ; i <= num ; i++ ) { _earthquakes.AddObject( ent ); entityPointer = &_earthquakes.ObjectAt( i ); arc.ArchiveSafePointer( entityPointer ); } } arc.ArchiveInteger( &_totalEnemiesSpawned ); // Don't archive these //const char *current_map; //const char *current_entities; //int spawn_entnum; arc.ArchiveInteger( ¤tInstanceNumber ); //int spawnflags; arc.ArchiveInteger( &framenum ); arc.ArchiveInteger( &inttime ); arc.ArchiveFloat( &time ); arc.ArchiveFloat( &timeInLevel ); arc.ArchiveFloat( &frametime ); arc.ArchiveFloat( &fixedframetime ); arc.ArchiveInteger( &startTime ); arc.ArchiveString( &level_name ); arc.ArchiveString( &mapname ); arc.ArchiveString( &spawnpoint ); arc.ArchiveString( &nextmap ); arc.ArchiveBoolean( &restart ); arc.ArchiveBoolean( &started ); arc.ArchiveBoolean( &playerfrozen ); arc.ArchiveFloat( &intermissiontime ); arc.ArchiveInteger( &exitintermission ); arc.ArchiveFloat( &intermission_advancetime ); arc.ArchiveBool( &_showIntermission ); arc.ArchiveBool( &_saveOrientation ); // Don't archive //gentity_s *next_edict; arc.ArchiveInteger( &total_secrets ); arc.ArchiveInteger( &found_secrets ); arc.ArchiveInteger( &total_specialItems ); arc.ArchiveInteger( &found_specialItems ); arc.ArchiveString( &game_script ); // Don't archive //trace_t impact_trace; arc.ArchiveBoolean( &cinematic ); arc.ArchiveBoolean( &ai_on ); arc.ArchiveBoolean( &mission_failed ); arc.ArchiveBoolean( &died_already ); arc.ArchiveBoolean( &near_exit ); arc.ArchiveVector( &water_color ); arc.ArchiveFloat( &water_alpha ); arc.ArchiveVector( &slime_color ); arc.ArchiveFloat( &slime_alpha ); arc.ArchiveVector( &lava_color ); arc.ArchiveFloat( &lava_alpha ); arc.ArchiveString( ¤t_soundtrack ); arc.ArchiveString( &saved_soundtrack ); arc.ArchiveObjectPointer( ( Class ** )&consoleThread ); arc.ArchiveVector( &m_fade_color ); arc.ArchiveFloat( &m_fade_alpha ); arc.ArchiveFloat( &m_fade_time ); arc.ArchiveFloat( & m_fade_time_start ); ArchiveEnum( m_fade_type, fadetype_t ); ArchiveEnum( m_fade_style, fadestyle_t ); arc.ArchiveFloat( &m_letterbox_fraction ); arc.ArchiveFloat( &m_letterbox_time ); arc.ArchiveFloat( &m_letterbox_time_start ); ArchiveEnum( m_letterbox_dir, letterboxdir_t ); arc.ArchiveBool( &_cleanup ); arc.ArchiveString( &_playerDeathThread ); arc.ArchiveObjectPointer( ( Class ** )&hNodeController ); // Don't archive, will already be setup from camera code // Container automatic_cameras; arc.ArchiveVector( & m_intermission_origin ); arc.ArchiveVector( & m_intermission_angle ); if ( arc.Loading() ) { str temp_soundtrack; // Change the sound track to the one just loaded temp_soundtrack = saved_soundtrack; ChangeSoundtrack( current_soundtrack.c_str() ); saved_soundtrack = temp_soundtrack; // not archived since we can't save mid-frame next_edict = NULL; // not archived since we can't save mid-frame memset( &impact_trace, 0, sizeof( impact_trace ) ); loadLevelStrings(); } } //----------------------------------------------------- // // Name: loadLevelStrings // Class: Level // // Description: Loads the string resource file for the level. // // Parameters: None // // Returns: //----------------------------------------------------- void Level::loadLevelStrings( void ) { const char* sublevelName; const char* levelName; const char* environmentName; gi.GetLevelDefs(mapname, &environmentName, &levelName, &sublevelName); gi.SR_LoadLevelStrings(environmentName); } /* ============== Precache Calls precache scripts ============== */ void Level::Precache( void ) { str filename; const char *environmentName; const char *levelName; const char *sublevelName; int i; str mapName; const char *spawnPoint; // // load in global0-9.scr // for( i = 0; i < 100; i++ ) { filename = va( "global/global%i.scr", i ); if ( gi.FS_ReadFile( filename.c_str(), NULL, true ) != -1 ) level.consoleThread->Parse( filename.c_str() ); else break; } // Get the level defs if( !current_map ) return; mapName = current_map; spawnPoint = strstr( mapName.c_str(), "$" ); if ( spawnPoint ) { mapName.CapLength( spawnPoint - mapName.c_str() ); } gi.GetLevelDefs( mapName, &environmentName, &levelName, &sublevelName ); // Precache global stuff for( i = 0 ; i < 10 ; i++ ) { if ( i == 0 ) filename = va( "precache/server/global.txt" ); else filename = va( "precache/server/global%d.txt", i ); if ( gi.FS_ReadFile( filename.c_str(), NULL, true ) != -1 ) level.consoleThread->Parse( filename.c_str() ); else break; } // Precache environment stuff if ( strlen( environmentName ) ) { for( i = 0 ; i < 10 ; i++ ) { if ( i == 0 ) filename = va( "precache/server/%s.txt", environmentName ); else filename = va( "precache/server/%s%d.txt", environmentName, i ); if ( gi.FS_ReadFile( filename.c_str(), NULL, true ) != -1 ) level.consoleThread->Parse( filename.c_str() ); else break; } } // Precache level stuff if ( strlen( levelName ) ) { for( i = 0 ; i < 10 ; i++ ) { if ( i == 0 ) filename = va( "precache/server/%s.txt", levelName ); else filename = va( "precache/server/%s%d.txt", levelName, i ); if ( gi.FS_ReadFile( filename.c_str(), NULL, true ) != -1 ) level.consoleThread->Parse( filename.c_str() ); else break; } } // Precache sublevel stuff if ( strlen( sublevelName ) && ( stricmp( levelName, sublevelName ) != 0 ) ) { for( i = 0 ; i < 10 ; i++ ) { if ( i == 0 ) filename = va( "precache/server/%s.txt", sublevelName ); else filename = va( "precache/server/%s%d.txt", sublevelName, i ); if ( gi.FS_ReadFile( filename.c_str(), NULL, true ) != -1 ) level.consoleThread->Parse( filename.c_str() ); else break; } } // load in universal_script.scr //G_LoadAndExecScript( "global/universal_script.scr", "precache:", true ); } /* ================ FindTeams Chain together all entities with a matching team field. All but the first will have the FlagTeamslave flag set. All but the last will have the teamchain field set to the next one ================ */ void Level::FindTeams( void ) { gentity_t *e; gentity_t *e2; gentity_t *next; gentity_t *next2; Entity *chain; Entity *ent; Entity *ent2; int c; int c2; c = 0; c2 = 0; for( e = active_edicts.next; e != &active_edicts; e = next ) { assert( e ); assert( e->inuse ); assert( e->entity ); next = e->next; ent = e->entity; if ( !ent->bind_info || ( ent->bind_info->moveteam.length() == 0 ) ) { continue; } if ( ent->flags & FlagTeamslave ) { continue; } chain = ent; ent->bind_info->teammaster = ent; c++; c2++; for( e2 = next; e2 != &active_edicts; e2 = next2 ) { assert( e2 ); assert( e2->inuse ); assert( e2->entity ); next2 = e2->next; ent2 = e2->entity; if ( !ent2->bind_info || ( ent2->bind_info->moveteam.length() == 0 ) ) { continue; } if ( ent2->flags & FlagTeamslave ) { continue; } if ( ent->bind_info->moveteam == ent2->bind_info->moveteam ) { c2++; chain->bind_info->teamchain = ent2; ent2->bind_info->teammaster = ent; chain = ent2; ent2->flags |= FlagTeamslave; } } } gi.DPrintf( "%i teams with %i entities\n", c, c2 ); } /* ================= AllocEdict Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ gentity_t *Level::AllocEdict( Entity *ent ) { int i; gentity_t *edict; if ( spawn_entnum >= 0 ) { edict = &g_entities[ spawn_entnum ]; spawn_entnum = -1; assert( !edict->inuse && !edict->entity ); // free up the entity pointer in case we took one that still exists if ( edict->inuse && edict->entity ) { delete edict->entity; } } else { edict = &g_entities[ maxclients->integer ]; for ( i = maxclients->integer; i < globals.num_entities; i++, edict++ ) { // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if ( !edict->inuse && ( ( edict->freetime < ( 2.0f + startTime ) ) || ( time - edict->freetime > 0.5f ) ) ) { break; } } // allow two spots for none and world if ( i == game.maxentities - 2.0f ) { // Try one more time before failing, relax timing completely edict = &g_entities[ maxclients->integer ]; for ( i = maxclients->integer; i < globals.num_entities; i++, edict++ ) { if ( !edict->inuse ) { break; } } if ( i == game.maxentities - 2.0f ) { gi.Error( ERR_DROP, "Level::AllocEdict: no free edicts" ); } } } assert( edict->next ); assert( edict->prev ); LL_Remove( edict, next, prev ); InitEdict( edict ); assert( active_edicts.next ); assert( active_edicts.prev ); LL_Add( &active_edicts, edict, next, prev ); assert( edict->next ); assert( edict->prev ); assert( edict->next != &free_edicts ); assert( edict->prev != &free_edicts ); // Tell the server about our data since we just spawned something if ( ( edict->s.number < ENTITYNUM_WORLD ) && ( globals.num_entities <= edict->s.number ) ) { globals.num_entities = edict->s.number + 1; gi.LocateGameData( g_entities, globals.num_entities, sizeof( gentity_t ), &game.clients[ 0 ].ps, sizeof( game.clients[ 0 ] ) ); } edict->entity = ent; edict->s.instanceNumber = currentInstanceNumber; currentInstanceNumber++; if ( currentInstanceNumber < 0 ) currentInstanceNumber = 0; return edict; } /* ================= FreeEdict Marks the edict as free ================= */ void Level::FreeEdict( gentity_t *ed ) { gclient_t *client; assert( ed != &free_edicts ); // unlink from world gi.unlinkentity ( ed ); assert( ed->next ); assert( ed->prev ); if ( next_edict == ed ) { next_edict = ed->next; } LL_Remove( ed, next, prev ); assert( ed->next == ed ); assert( ed->prev == ed ); assert( free_edicts.next ); assert( free_edicts.prev ); client = ed->client; memset( ed, 0, sizeof( *ed ) ); ed->client = client; ed->freetime = time; ed->inuse = false; ed->s.number = ed - g_entities; assert( free_edicts.next ); assert( free_edicts.prev ); LL_Add( &free_edicts, ed, next, prev ); assert( ed->next ); assert( ed->prev ); } void Level::InitEdict( gentity_t *e ) { int i; e->inuse = true; e->s.number = e - g_entities; // make sure a default scale gets set e->s.scale = 1.0f; // make sure the default constantlight gets set, initalize to r 1.0, g 1.0, b 1.0, r 0 e->s.constantLight = 0xffffff; e->s.renderfx |= RF_FRAMELERP; e->spawntime = time; e->s.frame = 0; e->svflags = 0; for( i = 0; i < NUM_BONE_CONTROLLERS; i++ ) { e->s.bone_tag[ i ] = -1; VectorClear( e->s.bone_angles[ i ] ); EulerToQuat( e->s.bone_angles[ i ], e->s.bone_quat[ i ] ); } for( i = 0; i < NUM_MORPH_CONTROLLERS; i++ ) { e->s.morph_controllers[ i ].index = -1; e->s.morph_controllers[ i ].percent = 0.0; } e->s.animationRate = 1.0f; } void Level::AddAutomaticCamera( Camera *cam ) { automatic_cameras.AddUniqueObject( cam ); } void Level::SetGameScript( const char *scriptname ) { game_script = scriptname; } //---------------------------------------------------------------- // Name: addEarthQuake // Class: Level // // Description: Adds an earthquake to the list // // Parameters: Entity *earthquake - earthquake to add to the list // // Returns: none //---------------------------------------------------------------- void Level::addEarthquake( Earthquake *earthquake ) { if ( !_earthquakes.ObjectInList( earthquake ) ) { _earthquakes.AddObject( earthquake ); } } //---------------------------------------------------------------- // Name: removeEarthQuake // Class: Level // // Description: Removes an earthquake from the list // // Parameters: Entity *earthquake - earthquake to remove from the list // // Returns: none //---------------------------------------------------------------- void Level::removeEarthquake( Earthquake *earthquake ) { if ( _earthquakes.ObjectInList( earthquake ) ) { _earthquakes.RemoveObject( earthquake ); } } //---------------------------------------------------------------- // Name: getEarthquakeMagnitudeAtPosition // Class: Level // // Description: Gets the total magnitude of all earthquakes from a particular position // // Parameters: const Vector &origin - position to test against // // Returns: float - total magnitude of all the earthquakes at the given position //---------------------------------------------------------------- float Level::getEarthquakeMagnitudeAtPosition( const Vector &origin ) { int i; float totalMagnitude; Earthquake *earthquake; totalMagnitude = 0.0f; // Add up all of the earthquakes magnitudes to get the total for( i = 1 ; i <= _earthquakes.NumObjects() ; i++ ) { // This should be a safe cast since only Earthquakes are allowed to be added to this list earthquake = _earthquakes.ObjectAt( i ); if ( earthquake ) { // Add this earthquake to the total totalMagnitude += earthquake->getMagnitudeAtPosition( origin ); } } // Return the total magnitude of all the earthquakes return totalMagnitude; } void Level::enemySpawned( Entity *enemy ) { Q_UNUSED(enemy); _totalEnemiesSpawned++; levelVars.SetVariable( "total_enemies_spawned", _totalEnemiesSpawned ); } void Level::setPlayerDeathThread( const str &threadName ) { _playerDeathThread = threadName; } str Level::getPlayerDeathThread( void ) { return _playerDeathThread; }