From 196c6a82fd4d44cbd0b72c578dee3448c3da4e66 Mon Sep 17 00:00:00 2001 From: /dev/humancontroller Date: Tue, 16 Jun 2015 12:42:17 +0200 Subject: implement a function to safely remove any entity --- src/game/g_local.h | 1 + src/game/g_mover.c | 1 + src/game/g_utils.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) diff --git a/src/game/g_local.h b/src/game/g_local.h index 6d3c8853..35fafb69 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -859,6 +859,7 @@ gentity_t *G_Spawn( void ); gentity_t *G_TempEntity( const vec3_t origin, int event ); void G_Sound( gentity_t *ent, int channel, int soundIndex ); void G_FreeEntity( gentity_t *e ); +void G_RemoveEntity( gentity_t *ent ); qboolean G_EntitiesFree( void ); char *G_CopyString( const char *str ); diff --git a/src/game/g_mover.c b/src/game/g_mover.c index a4f52867..cae97ea2 100644 --- a/src/game/g_mover.c +++ b/src/game/g_mover.c @@ -1705,6 +1705,7 @@ void SP_func_door_model( gentity_t *ent ) //brush model clipBrush = ent->clipBrush = G_Spawn( ); clipBrush->classname = "func_door_model_clip_brush"; + clipBrush->clipBrush = ent; // link back clipBrush->model = ent->model; trap_SetBrushModel( clipBrush, clipBrush->model ); clipBrush->s.eType = ET_INVISIBLE; diff --git a/src/game/g_utils.c b/src/game/g_utils.c index 495a6fa9..e70f345f 100644 --- a/src/game/g_utils.c +++ b/src/game/g_utils.c @@ -505,6 +505,181 @@ void G_FreeEntity( gentity_t *ent ) ent->inuse = qfalse; } +/* +================= +G_RemoveEntity + +Safely remove an entity, perform reasonable cleanup logic +================= +*/ +void G_RemoveEntity( gentity_t *ent ) +{ + gentity_t *e; + + if( ent->client ) + { + // removing a player causes the player to "unspawn" + class_t class = ent->client->pers.classSelection; // back up the spawn queue choice + weapon_t weapon = ent->client->pers.humanItemSelection; // back up + ent->client->pers.classSelection = PCL_NONE; + ent->client->pers.humanItemSelection = WP_NONE; + ent->suicideTime = 0; // cancel any timed suicides + ClientSpawn( ent, NULL, NULL, NULL ); + ent->client->pers.classSelection = class; // restore the spawn queue choice + ent->client->pers.humanItemSelection = weapon; // restore + return; + } + else if( ent->s.eType == ET_RANGE_MARKER ) + { + for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e ) + { + if( e->rangeMarker == ent ) + { + // clear the buildable's reference to this range marker + e->rangeMarker = NULL; + break; + } + } + } + else if( ent->s.eType == ET_BUILDABLE ) + { + // the range marker (if any) goes away with the buildable + G_RemoveRangeMarkerFrom( ent ); + } + else if( !strcmp( ent->classname, "lev2zapchain" ) ) + { + zap_t *z; + for( z = &zaps[ 0 ]; z < &zaps[ MAX_ZAPS ]; ++z ) + { + if( z->used && z->effectChannel == ent ) + { + // free the zap slot occupied by this zap effect + z->used = qfalse; + break; + } + } + } + else if( ent->s.eType == ET_MOVER ) + { + if( !strcmp( ent->classname, "func_door" ) || + !strcmp( ent->classname, "func_door_rotating" ) || + !strcmp( ent->classname, "func_door_model" ) || + !strcmp( ent->classname, "func_door_model_clip_brush" ) || + !strcmp( ent->classname, "func_plat" ) ) + { + // each func_door_model entity is paired with a clip brush, remove the other + if( ent->clipBrush != NULL ) + G_FreeEntity( ent->clipBrush ); + for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e ) + { + if( e->parent == ent ) + { + // this mover has a trigger area brush + if( ent->teammaster != NULL && ent->teammaster->teamchain != NULL ) + { + // the mover is part of a team of at least 2 + e->parent = ent->teammaster->teamchain; // hand the brush over to the next mover in command + } + else + G_FreeEntity( e ); // remove the teamless or to-be-orphaned brush + break; + } + } + } + // removing a mover opens the relevant portal + trap_AdjustAreaPortalState( ent, qtrue ); + } + else if( !strcmp( ent->classname, "path_corner" ) ) + { + for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e ) + { + if( e->nextTrain == ent ) + e->nextTrain = ent->nextTrain; // redirect func_train and path_corner entities + } + } + else if( !strcmp( ent->classname, "info_player_intermission" ) || + !strcmp( ent->classname, "info_player_deathmatch" ) ) + { + for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e ) + { + if( e->inuse && e != ent && + ( !strcmp( e->classname, "info_player_intermission" ) || + !strcmp( e->classname, "info_player_deathmatch" ) ) ) + { + break; + } + } + // refuse to remove the last info_player_intermission/info_player_deathmatch entity + // (because it is required for initial camera placement) + if( e >= &g_entities[ level.num_entities ] ) + return; + } + else if( !strcmp( ent->classname, "target_location" ) ) + { + if( ent == level.locationHead ) + level.locationHead = ent->nextTrain; + else + { + for( e = level.locationHead; e != NULL; e = e->nextTrain ) + { + if( e->nextTrain == ent ) + { + e->nextTrain = ent->nextTrain; + break; + } + } + } + } + else if( !Q_stricmp( ent->classname, "misc_portal_camera" ) ) + { + for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e ) + { + if( e->r.ownerNum == ent - g_entities ) + { + // disown the surface + e->r.ownerNum = ENTITYNUM_NONE; + } + } + } + + if( ent->teammaster != NULL ) + { + // this entity is part of a mover team + if( ent == ent->teammaster ) + { + // the entity is the master + gentity_t *snd = ent->teamchain; + for( e = snd; e != NULL; e = e->teamchain ) + e->teammaster = snd; // put the 2nd entity (if any) in command + if( snd ) + { + if( !strcmp( ent->classname, snd->classname ) ) + { + // transfer certain activity properties + snd->think = ent->think; + snd->nextthink = ent->nextthink; + } + snd->flags &= ~FL_TEAMSLAVE; // put the 2nd entity (if any) in command + } + } + else + { + // the entity is a slave + for( e = ent->teammaster; e != NULL; e = e->teamchain ) + { + if( e->teamchain == ent ) + { + // unlink it from the chain + e->teamchain = ent->teamchain; + break; + } + } + } + } + + G_FreeEntity( ent ); +} + /* ================= G_TempEntity -- cgit