summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author/dev/humancontroller <devhc@example.com>2015-06-16 12:42:17 +0200
committer/dev/humancontroller <devhc@example.com>2017-03-09 13:51:17 +0100
commit196c6a82fd4d44cbd0b72c578dee3448c3da4e66 (patch)
treefe05b3198abb4e4b0ad5186fdcafe1c9347e9c36
parentcce1bc5d95958eb84746a4f72f5003cb4b5eab0a (diff)
implement a function to safely remove any entity
-rw-r--r--src/game/g_local.h1
-rw-r--r--src/game/g_mover.c1
-rw-r--r--src/game/g_utils.c175
3 files changed, 177 insertions, 0 deletions
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
@@ -507,6 +507,181 @@ void G_FreeEntity( gentity_t *ent )
/*
=================
+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
Spawns an event entity that will be auto-removed