Beispiel #1
0
/*
* body_think
*/
static void body_think( edict_t *self )
{
	self->health = GIB_HEALTH - 1;

	//effect: small gibs, and only when it is still a body, not a gibbed head.
	if( self->s.type == ET_CORPSE )
		ThrowSmallPileOfGibs( self, 25 );

	//disallow interaction with the world.
	self->takedamage = DAMAGE_NO;
	self->r.solid = SOLID_NOT;
	self->s.sound = 0;
	self->flags |= FL_NO_KNOCKBACK;
	self->s.type = ET_GENERIC;
	self->r.svflags &= ~SVF_CORPSE;
	self->r.svflags |= SVF_NOCLIENT;
	self->s.modelindex = 0;
	self->s.modelindex2 = 0;
	VectorClear( self->velocity );
	VectorClear( self->avelocity );
	self->movetype = MOVETYPE_NONE;
	self->think = NULL;
	//memset( &self->snap, 0, sizeof(self->snap) );
	GClip_UnlinkEntity( self );
}
Beispiel #2
0
/*
* G_TeleportPlayer
*/
void G_TeleportPlayer( edict_t *player, edict_t *dest )
{
	int i;
	vec3_t velocity;
	mat3_t axis;
	float speed;
	gclient_t *client = player->r.client;

	if( !dest ) {
		return;
	}
	if( !client ) {
		return;
	}

	// draw the teleport entering effect
	G_TeleportEffect( player, false );

	//
	// teleport the player
	//

	// from racesow - use old pmove velocity
	VectorCopy( client->old_pmove.velocity, velocity );

	velocity[2] = 0; // ignore vertical velocity
	speed = VectorLengthFast( velocity );

	AnglesToAxis( dest->s.angles, axis );
	VectorScale( &axis[AXIS_FORWARD], speed, client->ps.pmove.velocity );

	VectorCopy( dest->s.angles, client->ps.viewangles );
	VectorCopy( dest->s.origin, client->ps.pmove.origin );

	// set the delta angle
	for ( i = 0; i < 3; i++ )
		client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i];

	client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
	client->ps.pmove.pm_time = 1; // force the minimum no control delay
	player->s.teleported = true;

	// update the entity from the pmove
	VectorCopy( client->ps.viewangles, player->s.angles );
	VectorCopy( client->ps.pmove.origin, player->s.origin );
	VectorCopy( client->ps.pmove.origin, player->s.old_origin );
	VectorCopy( client->ps.pmove.origin, player->olds.origin );
	VectorCopy( client->ps.pmove.velocity, player->velocity );

	// unlink to make sure it can't possibly interfere with KillBox
	GClip_UnlinkEntity( player );

	// kill anything at the destination
	KillBox( player );

	GClip_LinkEntity( player );

	// add the teleport effect at the destination
	G_TeleportEffect( player, true );
}
Beispiel #3
0
/*
* TVM_ClientDisconnect
* 
* Called when a player drops from the server.
* Will not be called between levels.
*/
void TVM_ClientDisconnect( tvm_relay_t *relay, edict_t *ent )
{
	assert( ent && ent->local && ent->r.client );

	//TVM_Printf( "Disconnect: %s\n", ent->r.client->pers.netname );

	ent->r.inuse = false;
	ent->r.svflags = SVF_NOCLIENT;
	memset( ent->r.client, 0, sizeof( *ent->r.client ) );
	ent->r.client->ps.playerNum = PLAYERNUM( ent );

	GClip_UnlinkEntity( ent->relay, ent );
}
Beispiel #4
0
static void use_target_spawner( edict_t *self, edict_t *other, edict_t *activator )
{
	edict_t	*ent;

	ent = G_Spawn();
	ent->classname = self->target;
	VectorCopy( self->s.origin, ent->s.origin );
	VectorCopy( self->s.angles, ent->s.angles );
	G_CallSpawn( ent );
	GClip_UnlinkEntity( ent );
	KillBox( ent );
	GClip_LinkEntity( ent );
	if( self->speed )
		VectorCopy( self->moveinfo.movedir, ent->velocity );
}
Beispiel #5
0
/*
* ClientDisconnect
* Called when a player drops from the server.
* Will not be called between levels.
*/
void ClientDisconnect( edict_t *ent, const char *reason )
{
	int team;

	if( !ent->r.client || !ent->r.inuse )
		return;

	// always report in RACE mode
	if( GS_RaceGametype() 
		|| ( ent->r.client->team != TEAM_SPECTATOR && ( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) ) )
		G_AddPlayerReport( ent, GS_MatchState() == MATCH_STATE_POSTMATCH );

	for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ )
		G_Teams_UnInvitePlayer( team, ent );

	if( !level.gametype.disableObituaries || !(ent->r.svflags & SVF_FAKECLIENT ) )
	{
		if( !reason )
			G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected\n", ent->r.client->netname );
		else
			G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected (%s" S_COLOR_WHITE ")\n", ent->r.client->netname, reason );
	}

	// send effect
	if( ent->s.team > TEAM_SPECTATOR )
		G_TeleportEffect( ent, false );

	ent->r.client->team = TEAM_SPECTATOR;
	G_ClientRespawn( ent, true ); // respawn as ghost
	ent->movetype = MOVETYPE_NOCLIP; // allow freefly

	// let the gametype scripts know this client just disconnected
	G_Gametype_ScoreEvent( ent->r.client, "disconnect", NULL );

	G_FreeAI( ent );
	AI_EnemyRemoved( ent );

	ent->r.inuse = false;
	ent->r.svflags = SVF_NOCLIENT;

	memset( ent->r.client, 0, sizeof( *ent->r.client ) );
	ent->r.client->ps.playerNum = PLAYERNUM( ent );

	trap_ConfigString( CS_PLAYERINFOS+PLAYERNUM( ent ), "" );
	GClip_UnlinkEntity( ent );

	G_Match_CheckReadys();
}
Beispiel #6
0
/*
* GClip_SetBrushModel
* 
* Also sets mins and maxs for inline bmodels
*/
void GClip_SetBrushModel( edict_t *ent, char *name )
{
	struct cmodel_s *cmodel;

	if( !name )
	{
		//racesow
		G_Printf( "Warning: GClip_SetBrushModel: NULL model in '%s'", ent->classname ? ent->classname : "no classname\n" );
		GClip_UnlinkEntity( ent );
		G_FreeEdict( ent );
		return;
		//!racesow
	}

	if( !name[0] )
	{
		ent->s.modelindex = 0;
		return;
	}

	if( name[0] != '*' )
	{
		ent->s.modelindex = trap_ModelIndex( name );
		return;
	}

	// if it is an inline model, get the size information for it

	// world model is special
	if( !strcmp( name, "*0" ) )
	{
		ent->s.modelindex = 0;
		cmodel = trap_CM_InlineModel( 0 );
		trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs );
		return;
	}

	// racesow: THIS IS A VERY DIRTY "FIX": it assigns normally unreachable models (the ones over MAX_MODELS) to unrelated models..
	// normally this only affects buggy maps that otherwise wouldn't load (like amt-freestyle3)
	// brush model
	//ent->s.modelindex = trap_ModelIndex( name ); // <- This is the unmodified version 
	ent->s.modelindex = atoi( name+1 );
	// !racesow
	assert( ent->s.modelindex == (unsigned int)atoi( name + 1 ) );
	cmodel = trap_CM_InlineModel( ent->s.modelindex );
	trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs );
	GClip_LinkEntity( ent );
}
Beispiel #7
0
/*
* GClip_SetBrushModel
* 
* Also sets mins and maxs for inline bmodels
*/
void GClip_SetBrushModel( edict_t *ent, const char *name )
{
	struct cmodel_s *cmodel;

	if( !name )
	{
		G_Error( "GClip_SetBrushModel: NULL model in '%s'", 
		ent->classname ? ent->classname : "no classname" );
		// racesow
		GClip_UnlinkEntity( ent );
		G_FreeEdict( ent );
		return;
		// !racesow
	}

	if( !name[0] )
	{
		ent->s.modelindex = 0;
		return;
	}

	if( name[0] != '*' )
	{
		ent->s.modelindex = trap_ModelIndex( name );
		return;
	}

	// if it is an inline model, get the size information for it

	// world model is special
	if( !strcmp( name, "*0" ) )
	{
		ent->s.modelindex = 0;
		cmodel = trap_CM_InlineModel( 0 );
		trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs );
		return;
	}

	// brush model
	ent->s.modelindex = trap_ModelIndex( name );
	assert( ent->s.modelindex == (unsigned int)atoi( name + 1 ) );
	cmodel = trap_CM_InlineModel( ent->s.modelindex );
	trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs );
	GClip_LinkEntity( ent );
}
/*
* G_Match_FreeBodyQueue
*/
void G_Match_FreeBodyQueue( void )
{
	edict_t *ent;
	int i;

	ent = &game.edicts[gs.maxclients + 1];
	for( i = 0; i < BODY_QUEUE_SIZE; ent++, i++ )
	{
		if( !ent->r.inuse )
			continue;

		if( ent->classname && !Q_stricmp( ent->classname, "body" ) )
		{
			GClip_UnlinkEntity( ent );

			ent->deadflag = DEAD_NO;
			ent->movetype = MOVETYPE_NONE;
			ent->r.solid = SOLID_NOT;
			ent->r.svflags = SVF_NOCLIENT;

			ent->s.type = ET_GENERIC;
			ent->s.skinnum = 0;
			ent->s.frame = 0;
			ent->s.modelindex = 0;
			ent->s.sound = 0;
			ent->s.effects = 0;

			ent->takedamage = DAMAGE_NO;
			ent->flags |= FL_NO_KNOCKBACK;

			GClip_LinkEntity( ent );
		}
	}

	level.body_que = 0;
}
Beispiel #9
0
void GClip_LinkEntity( edict_t *ent )
{
	areanode_t *node;
	int leafs[MAX_TOTAL_ENT_LEAFS];
	int clusters[MAX_TOTAL_ENT_LEAFS];
	int num_leafs;
	int i, j, k;
	int area;
	int topnode;

	if( ent->r.area.prev )
		GClip_UnlinkEntity( ent ); // unlink from old position

	if( ent == game.edicts )
		return; // don't add the world

	if( !ent->r.inuse )
		return;

	// set the size
	VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size );

	if( ent->r.solid == SOLID_NOT || ( ent->r.svflags & SVF_PROJECTILE ) )
	{
		ent->s.solid = 0;
	}
	else if( ISBRUSHMODEL( ent->s.modelindex ) )
	{
		// the only predicted SOLID_TRIGGER entity is ET_PUSH_TRIGGER
		if( ent->r.solid != SOLID_TRIGGER || ent->s.type == ET_PUSH_TRIGGER )
			ent->s.solid = SOLID_BMODEL;
		else
			ent->s.solid = 0;
	}
	else // encode the size into the entity_state for client prediction
	{
		if( ent->r.solid == SOLID_TRIGGER )
		{
			ent->s.solid = 0;
		}
		else
		{
			// assume that x/y are equal and symetric
			i = ent->r.maxs[0]/8;
			clamp( i, 1, 31 );

			// z is not symetric
			j = ( -ent->r.mins[2] )/8;
			clamp( j, 1, 31 );

			// and z maxs can be negative...
			k = ( ent->r.maxs[2]+32 )/8;
			clamp( k, 1, 63 );

			ent->s.solid = ( k<<10 ) | ( j<<5 ) | i;
		}
	}

	// set the abs box
	if( ISBRUSHMODEL( ent->s.modelindex ) &&
		( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) )
	{ 
		// expand for rotation
		float radius;

		radius = RadiusFromBounds( ent->r.mins, ent->r.maxs );

		for( i = 0; i < 3; i++ )
		{
			ent->r.absmin[i] = ent->s.origin[i] - radius;
			ent->r.absmax[i] = ent->s.origin[i] + radius;
		}
	}
	else // axis aligned
	{ 
		VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin );
		VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax );
	}

	// because movement is clipped an epsilon away from an actual edge,
	// we must fully check even when bounding boxes don't quite touch
	ent->r.absmin[0] -= 1;
	ent->r.absmin[1] -= 1;
	ent->r.absmin[2] -= 1;
	ent->r.absmax[0] += 1;
	ent->r.absmax[1] += 1;
	ent->r.absmax[2] += 1;

	// link to PVS leafs
	ent->r.num_clusters = 0;
	ent->r.areanum = ent->r.areanum2 = -1;

	// get all leafs, including solids
	num_leafs = trap_CM_BoxLeafnums( ent->r.absmin, ent->r.absmax,
		leafs, MAX_TOTAL_ENT_LEAFS, &topnode );

	// set areas
	for( i = 0; i < num_leafs; i++ )
	{
		clusters[i] = trap_CM_LeafCluster( leafs[i] );
		area = trap_CM_LeafArea( leafs[i] );
		if( area > -1 )
		{
			// doors may legally straggle two areas,
			// but nothing should ever need more than that
			if( ent->r.areanum > -1 && ent->r.areanum != area )
			{
				if( ent->r.areanum2 > -1 && ent->r.areanum2 != area )
				{
					if( developer->integer )
						G_Printf( "Object %s touching 3 areas at %f %f %f\n",
						( ent->classname ? ent->classname : "" ),
						ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] );
				}
				ent->r.areanum2 = area;
			}
			else
				ent->r.areanum = area;
		}
	}

	if( num_leafs >= MAX_TOTAL_ENT_LEAFS )
	{ // assume we missed some leafs, and mark by headnode
		ent->r.num_clusters = -1;
		ent->r.headnode = topnode;
	}
	else
	{
		ent->r.num_clusters = 0;
		for( i = 0; i < num_leafs; i++ )
		{
			if( clusters[i] == -1 )
				continue; // not a visible leaf
			for( j = 0; j < i; j++ )
				if( clusters[j] == clusters[i] )
					break;
			if( j == i )
			{
				if( ent->r.num_clusters == MAX_ENT_CLUSTERS )
				{
					// assume we missed some leafs, and mark by headnode
					ent->r.num_clusters = -1;
					ent->r.headnode = topnode;
					break;
				}

				ent->r.clusternums[ent->r.num_clusters++] = clusters[i];
			}
		}
	}

	// if first time, make sure old_origin is valid
	if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) )
	{
		VectorCopy( ent->s.origin, ent->s.old_origin );
		ent->olds = ent->s;
	}
	ent->r.linkcount++;
	ent->linked = qtrue;

	if( ent->r.solid == SOLID_NOT )
		return;

	// find the first node that the ent's box crosses
	node = sv_areanodes;
	while( 1 )
	{
		if( node->axis == -1 )
			break;
		if( ent->r.absmin[node->axis] > node->dist )
			node = node->children[0];
		else if( ent->r.absmax[node->axis] < node->dist )
			node = node->children[1];
		else
			break; // crosses the node
	}

	// link it in
	if( ent->r.solid == SOLID_TRIGGER )
		GClip_InsertLinkBefore( &ent->r.area, &node->trigger_edicts, NUM_FOR_EDICT( ent ) );
	else
		GClip_InsertLinkBefore( &ent->r.area, &node->solid_edicts, NUM_FOR_EDICT( ent ) );
}
Beispiel #10
0
//target_give wait classname weapon_xxx
static void target_give_use( edict_t *self, edict_t *other, edict_t *activator )
{
	edict_t *give;
	const gsitem_t *item;
	int i, numsounds;
	float attenuation;
	const char *pickup_sound;
	int prev_pickup = -1;
	gclient_t *aclient = activator && activator->r.client ? activator->r.client : NULL;
	const gsitem_t *sounds[MAX_GIVE_SOUNDS];

	give = NULL;
	numsounds = 0;

	// more than one item can be given
	while( ( give = G_Find( give, FOFS( targetname ), self->target ) ) != NULL )
	{
		// sanity
		item = give->item;
		if( !item )
			continue;

		if( !( item->flags & ITFLAG_PICKABLE ) )
			continue;

		if( aclient ) {
			prev_pickup = aclient->ps.stats[STAT_PICKUP_ITEM];
		}
		pickup_sound = item->pickup_sound;

		// disable pickup sound, we'll play it later
		attenuation = give->attenuation;
		give->attenuation = 0;

		Touch_Item( give, activator, NULL, 0 );

		if( give->r.inuse ) {
			give->nextThink = 0;
			give->think = 0;
			give->attenuation = attenuation;
			GClip_UnlinkEntity( give );
		}

		// a hacky way to check for successful item pickup
		if( aclient && aclient->ps.stats[STAT_PICKUP_ITEM] == item->tag && prev_pickup != item->tag )
		{
			prev_pickup = item->tag;

			// see if we don't know this pickup sound yet
			if( pickup_sound ) {
				for( i = 0; i < numsounds; i++ ) {
					if( !Q_stricmp( sounds[i]->pickup_sound, pickup_sound ) )
						break;
				}

				if( i == numsounds && numsounds < MAX_GIVE_SOUNDS ) {
					sounds[numsounds++] = item;
				}
			}
		}
	}

	// play unique pickup sounds
	for( i = 0; i < numsounds; i++ ) {
		Touch_ItemSound( activator, sounds[i] );
	}
}
Beispiel #11
0
static void old_teleporter_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags )
{
	edict_t	*dest;
	int i;
	vec3_t velocity, angles;
	mat3_t axis;
	float speed;
	vec3_t org;

	if( !other->r.client )
		return;
	if( self->s.team && self->s.team != other->s.team )
		return;
	if( other->r.client->ps.pmove.pm_type > PM_SPECTATOR )
		return;
	if( self->spawnflags & 1 && other->r.client->ps.pmove.pm_type != PM_SPECTATOR )
		return;

	// match countdown
	if( GS_MatchState() == MATCH_STATE_COUNTDOWN )
		return;

	// wait delay
	if( self->timeStamp > level.time )
		return;

	self->timeStamp = level.time + ( self->wait * 1000 );

	dest = G_Find( NULL, FOFS( targetname ), self->target );
	if( !dest )
	{
		if( developer->integer )
			G_Printf( "Couldn't find destination.\n" );
		return;
	}

	if( self->s.modelindex )
	{
		org[0] = self->s.origin[0] + 0.5 * ( self->r.mins[0] + self->r.maxs[0] );
		org[1] = self->s.origin[1] + 0.5 * ( self->r.mins[1] + self->r.maxs[1] );
		org[2] = self->s.origin[2] + 0.5 * ( self->r.mins[2] + self->r.maxs[2] );
	}
	else
		VectorCopy( self->s.origin, org );

	// play custom sound if any (played from the teleporter entrance)
	if( self->noise_index )
		G_PositionedSound( org, CHAN_AUTO, self->noise_index, ATTN_NORM );

	// draw the teleport entering effect
	G_TeleportEffect( other, false );

	//
	// teleport the player
	//

	VectorCopy( other->r.client->ps.pmove.velocity, velocity );

	velocity[2] = 0; // ignore vertical velocity
	speed = VectorLengthFast( velocity );

	// if someone enters a portal backwards, inverse the destination YAW angle
#if 0
	VectorCopy( other->s.angles, angles );
	angles[PITCH] = 0;
	AngleVectors( angles, axis[0], NULL, NULL );
	VectorSubtract( org, other->s.origin, org );

	VectorCopy( dest->s.angles, angles );
	if( DotProduct( org, axis[0] ) < 0 )
		angles[YAW] = anglemod( angles[YAW] - 180 );
#else
	VectorCopy( dest->s.angles, angles );
#endif

	AnglesToAxis( dest->s.angles, axis );
	VectorScale( &axis[AXIS_FORWARD], speed, other->r.client->ps.pmove.velocity );

	VectorCopy( angles, other->r.client->ps.viewangles );
	VectorCopy( dest->s.origin, other->r.client->ps.pmove.origin );

	// set the delta angle
	for( i = 0; i < 3; i++ )
		other->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( other->r.client->ps.viewangles[i] ) - other->r.client->ucmd.angles[i];

	other->r.client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
	other->s.teleported = qtrue;
	other->r.client->ps.pmove.pm_time = 1; // force the minimum no control delay

	// update the entity from the pmove
	VectorCopy( other->r.client->ps.viewangles, other->s.angles );
	VectorCopy( other->r.client->ps.pmove.origin, other->s.origin );
	VectorCopy( other->r.client->ps.pmove.origin, other->s.old_origin );
	VectorCopy( other->r.client->ps.pmove.origin, other->olds.origin );
	VectorCopy( other->r.client->ps.pmove.velocity, other->velocity );

	// unlink to make sure it can't possibly interfere with KillBox
	GClip_UnlinkEntity( other );

	// kill anything at the destination
	if( !KillBox( other ) )
	{
	}

	GClip_LinkEntity( other );

	// add the teleport effect at the destination
	G_TeleportEffect( other, true );
}
Beispiel #12
0
/*
* G_ClientRespawn
*/
void G_ClientRespawn( edict_t *self, bool ghost )
{
	int i;
	edict_t *spawnpoint;
	vec3_t hull_mins, hull_maxs;
	vec3_t spawn_origin, spawn_angles;
	gclient_t *client;
	int old_team;

	G_DeathAwards( self );

	G_SpawnQueue_RemoveClient( self );

	self->r.svflags &= ~SVF_NOCLIENT;

	//if invalid be spectator
	if( self->r.client->team < 0 || self->r.client->team >= GS_MAX_TEAMS )
		self->r.client->team = TEAM_SPECTATOR;

	// force ghost always to true when in spectator team
	if( self->r.client->team == TEAM_SPECTATOR )
		ghost = true;

	old_team = self->s.team;
	if( self->r.client->teamstate.is_coach )
		ghost = true;

	GClip_UnlinkEntity( self );

	client = self->r.client;

	memset( &client->resp, 0, sizeof( client->resp ) );
	memset( &client->ps, 0, sizeof( client->ps ) );
	client->resp.timeStamp = level.time;
	client->ps.playerNum = PLAYERNUM( self );

	// clear entity values
	memset( &self->snap, 0, sizeof( self->snap ) );
	memset( &self->s, 0, sizeof( self->s ) );
	memset( &self->olds, 0, sizeof( self->olds ) );
	memset( &self->invpak, 0, sizeof( self->invpak ) );

	self->s.number = self->olds.number = ENTNUM( self );

	// relink client struct
	self->r.client = &game.clients[PLAYERNUM( self )];

	// update team
	self->s.team = client->team;

	self->deadflag = DEAD_NO;
	self->s.type = ET_PLAYER;
	self->groundentity = NULL;
	self->takedamage = DAMAGE_AIM;
	self->think = player_think;
	self->pain = player_pain;
	self->die = player_die;
	self->viewheight = playerbox_stand_viewheight;
	self->r.inuse = true;
	self->mass = PLAYER_MASS;
	self->air_finished = level.time + ( 12 * 1000 );
	self->r.clipmask = MASK_PLAYERSOLID;
	self->waterlevel = 0;
	self->watertype = 0;
	self->flags &= ~FL_NO_KNOCKBACK;
	self->r.svflags &= ~SVF_CORPSE;
	self->enemy = NULL;
	self->r.owner = NULL;
	self->max_health = 100;
	self->health = self->max_health;

	if( AI_GetType( self->ai ) == AI_ISBOT )
	{
		self->think = NULL;
		self->classname = "bot";
	}
	else if( self->r.svflags & SVF_FAKECLIENT )
		self->classname = "fakeclient";
	else
		self->classname = "player";

	VectorCopy( playerbox_stand_mins, self->r.mins );
	VectorCopy( playerbox_stand_maxs, self->r.maxs );
	VectorClear( self->velocity );
	VectorClear( self->avelocity );

	VectorCopy( self->r.mins, hull_mins );
	VectorCopy( self->r.maxs, hull_maxs );
	trap_CM_RoundUpToHullSize( hull_mins, hull_maxs, NULL );
	if( self->r.maxs[2] > hull_maxs[2] )
		self->viewheight -= (self->r.maxs[2] - hull_maxs[2]);

	client->ps.POVnum = ENTNUM( self );

	// set movement info
	client->ps.pmove.stats[PM_STAT_MAXSPEED] = (short)DEFAULT_PLAYERSPEED;
	client->ps.pmove.stats[PM_STAT_JUMPSPEED] = (short)DEFAULT_JUMPSPEED;
	client->ps.pmove.stats[PM_STAT_DASHSPEED] = (short)DEFAULT_DASHSPEED;

	if( ghost )
	{
		self->r.solid = SOLID_NOT;
		self->movetype = MOVETYPE_NOCLIP;
		if( self->s.team == TEAM_SPECTATOR )
			self->r.svflags |= SVF_NOCLIENT;
	}
	else
	{
		self->r.client->resp.takeStun = true;
		self->r.solid = SOLID_YES;
		self->movetype = MOVETYPE_PLAYER;
		client->ps.pmove.stats[PM_STAT_FEATURES] = static_cast<unsigned short>(PMFEAT_DEFAULT);
		if( !g_allow_bunny->integer )
			client->ps.pmove.stats[PM_STAT_FEATURES] &= ~( PMFEAT_AIRCONTROL|PMFEAT_FWDBUNNY );
	}

	ClientUserinfoChanged( self, client->userinfo );

	if( old_team != self->s.team )
		G_Teams_UpdateMembersList();

	SelectSpawnPoint( self, &spawnpoint, spawn_origin, spawn_angles );
	VectorCopy( spawn_origin, client->ps.pmove.origin );
	VectorCopy( spawn_origin, self->s.origin );
	VectorCopy( self->s.origin, self->s.old_origin );

	// set angles
	self->s.angles[PITCH] = 0;
	self->s.angles[YAW] = anglemod( spawn_angles[YAW] );
	self->s.angles[ROLL] = 0;
	VectorCopy( self->s.angles, client->ps.viewangles );

	// set the delta angle
	for( i = 0; i < 3; i++ )
		client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i];

	// don't put spectators in the game
	if( !ghost )
	{
		if( KillBox( self ) )
		{
		}
	}

	self->s.attenuation = ATTN_NORM;

	self->s.teleported = true;

	// hold in place briefly
	client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
	client->ps.pmove.pm_time = 14;
	client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] = CLIENT_RESPAWN_FREEZE_DELAY;
	client->ps.pmove.stats[PM_STAT_NOAUTOATTACK] = 1000;

	// set race stats to invisible
	client->ps.stats[STAT_TIME_SELF] = STAT_NOTSET;
	client->ps.stats[STAT_TIME_BEST] = STAT_NOTSET;
	client->ps.stats[STAT_TIME_RECORD] = STAT_NOTSET;
	client->ps.stats[STAT_TIME_ALPHA] = STAT_NOTSET;
	client->ps.stats[STAT_TIME_BETA] = STAT_NOTSET;

	BOT_Respawn( self );

	self->r.client->level.respawnCount++;

	G_UseTargets( spawnpoint, self );

	GClip_LinkEntity( self );

	// let the gametypes perform their changes
	if( game.asEngine != NULL )
		GT_asCallPlayerRespawn( self, old_team, self->s.team );
	else
		G_Gametype_GENERIC_ClientRespawn( self, old_team, self->s.team );
}
Beispiel #13
0
/*
* CopyToBodyQue
*/
static edict_t *CopyToBodyQue( edict_t *ent, edict_t *attacker, int damage )
{
	edict_t	*body;
	int contents;

	if( GS_RaceGametype() )
		return NULL;

	contents = G_PointContents( ent->s.origin );
	if( contents & CONTENTS_NODROP )
		return NULL;

	G_Client_UnlinkBodies( ent );

	// grab a body que and cycle to the next one
	body = &game.edicts[gs.maxclients + level.body_que + 1];
	level.body_que = ( level.body_que + 1 ) % BODY_QUEUE_SIZE;

	// send an effect on the removed body
	if( body->s.modelindex && body->s.type == ET_CORPSE )
		ThrowSmallPileOfGibs( body, 10 );

	GClip_UnlinkEntity( body );

	memset( body, 0, sizeof( edict_t ) ); //clean up garbage

	//init body edict
	G_InitEdict( body );
	body->classname = "body";
	body->health = ent->health;
	body->mass = ent->mass;
	body->r.owner = ent->r.owner;
	body->s.type = ent->s.type;
	body->s.team = ent->s.team;
	body->s.effects = 0;
	body->r.svflags = SVF_CORPSE;
	body->r.svflags &= ~SVF_NOCLIENT;
	body->activator = ent;
	if( g_deadbody_followkiller->integer )
		body->enemy = attacker;

	//use flat yaw
	body->s.angles[PITCH] = 0;
	body->s.angles[ROLL] = 0;
	body->s.angles[YAW] = ent->s.angles[YAW];
	body->s.modelindex2 = 0; // <-  is bodyOwner when in ET_CORPSE, but not in ET_GENERIC or ET_PLAYER
	body->s.weapon = 0;

	//copy player position and box size
	VectorCopy( ent->s.old_origin, body->s.old_origin );
	VectorCopy( ent->s.origin, body->s.origin );
	VectorCopy( ent->s.origin, body->olds.origin );
	VectorCopy( ent->r.mins, body->r.mins );
	VectorCopy( ent->r.maxs, body->r.maxs );
	VectorCopy( ent->r.absmin, body->r.absmin );
	VectorCopy( ent->r.absmax, body->r.absmax );
	VectorCopy( ent->r.size, body->r.size );
	VectorCopy( ent->velocity, body->velocity );
	body->r.maxs[2] = body->r.mins[2] + 8;

	body->r.solid = SOLID_YES;
	body->takedamage = DAMAGE_YES;
	body->r.clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
	body->movetype = MOVETYPE_TOSS;
	body->die = body_die;
	body->think = body_think; // body self destruction countdown

	if( ent->health < GIB_HEALTH
		|| meansOfDeath == MOD_ELECTROBOLT_S /* electrobolt always gibs */ )
	{
		ThrowSmallPileOfGibs( body, damage );

		// reset gib impulse
		VectorClear( body->velocity );
		ThrowClientHead( body, damage ); // sets ET_GIB

		body->s.frame = 0;
		body->nextThink = level.time + 3000 + random() * 3000;
		body->deadflag = DEAD_DEAD;
	}
	else if( ent->s.type == ET_PLAYER )
	{
		// copy the model
		body->s.type = ET_CORPSE;
		body->s.modelindex = ent->s.modelindex;
		body->s.bodyOwner = ent->s.number; // bodyOwner is the same as modelindex2
		body->s.skinnum = ent->s.skinnum;
		body->s.teleported = true;

		// launch the death animation on the body
		{
			static int i;
			i = ( i+1 )%3;
			G_AddEvent( body, EV_DIE, i, true );
			switch( i )
			{
			default:
			case 0:
				body->s.frame = ( ( BOTH_DEAD1&0x3F )|( BOTH_DEAD1&0x3F )<<6|( 0 &0xF )<<12 );
				break;
			case 1:
				body->s.frame = ( ( BOTH_DEAD2&0x3F )|( BOTH_DEAD2&0x3F )<<6|( 0 &0xF )<<12 );
				break;
			case 2:
				body->s.frame = ( ( BOTH_DEAD3&0x3F )|( BOTH_DEAD3&0x3F )<<6|( 0 &0xF )<<12 );
				break;
			}
		}

		body->think = body_ready;
		body->takedamage = DAMAGE_NO;
		body->r.solid = SOLID_NOT;
		body->nextThink = level.time + 500; // make damageable in 0.5 seconds
	}
	else // wasn't a player, just copy it's model
	{
		VectorClear( body->velocity );
		body->s.modelindex = ent->s.modelindex;
		body->s.frame = ent->s.frame;
		body->nextThink = level.time + 5000 + random()*10000;
	}

	GClip_LinkEntity( body );
	return body;
}
Beispiel #14
0
void GClip_LinkEntity( tvm_relay_t *relay, edict_t *ent )
{
	int leafs[MAX_TOTAL_ENT_LEAFS];
	int clusters[MAX_TOTAL_ENT_LEAFS];
	int num_leafs;
	int i, j;
	int area;
	int topnode;

	if( ent->r.area.prev )
		GClip_UnlinkEntity( relay, ent ); // unlink from old position

	if( ent == ent->relay->edicts )
		return; // don't add the world

	if( !ent->r.inuse )
		return;

	// set the size
	VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size );

	// set the abs box
	if( ent->s.solid == SOLID_BMODEL &&
		( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) )
	{ // expand for rotation
		float radius;

		radius = RadiusFromBounds( ent->r.mins, ent->r.maxs );

		for( i = 0; i < 3; i++ )
		{
			ent->r.absmin[i] = ent->s.origin[i] - radius;
			ent->r.absmax[i] = ent->s.origin[i] + radius;
		}
	}
	else
	{ // normal
		VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin );
		VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax );
	}

	// because movement is clipped an epsilon away from an actual edge,
	// we must fully check even when bounding boxes don't quite touch
	ent->r.absmin[0] -= 1;
	ent->r.absmin[1] -= 1;
	ent->r.absmin[2] -= 1;
	ent->r.absmax[0] += 1;
	ent->r.absmax[1] += 1;
	ent->r.absmax[2] += 1;

	// link to PVS leafs
	ent->r.num_clusters = 0;
	ent->r.areanum = ent->r.areanum2 = -1;

	// get all leafs, including solids
	num_leafs = trap_CM_BoxLeafnums( relay, ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode );

	// set areas
	for( i = 0; i < num_leafs; i++ )
	{
		clusters[i] = trap_CM_LeafCluster( relay, leafs[i] );
		area = trap_CM_LeafArea( relay, leafs[i] );
		if( area > -1 )
		{ // doors may legally straggle two areas,
			// but nothing should ever need more than that
			if( ent->r.areanum > -1 && ent->r.areanum != area )
			{
				if( ent->r.areanum2 > -1 && ent->r.areanum2 != area )
				{
					TVM_Printf( "Object touching 3 areas at %f %f %f\n", ent->r.absmin[0], ent->r.absmin[1],
						ent->r.absmin[2] );
				}
				ent->r.areanum2 = area;
			}
			else
				ent->r.areanum = area;
		}
	}

	if( num_leafs >= MAX_TOTAL_ENT_LEAFS )
	{ // assume we missed some leafs, and mark by headnode
		ent->r.num_clusters = -1;
		ent->r.headnode = topnode;
	}
	else
	{
		ent->r.num_clusters = 0;
		for( i = 0; i < num_leafs; i++ )
		{
			if( clusters[i] == -1 )
				continue; // not a visible leaf
			for( j = 0; j < i; j++ )
				if( clusters[j] == clusters[i] )
					break;
			if( j == i )
			{
				if( ent->r.num_clusters == MAX_ENT_CLUSTERS )
				{ // assume we missed some leafs, and mark by headnode
					ent->r.num_clusters = -1;
					ent->r.headnode = topnode;
					break;
				}

				ent->r.clusternums[ent->r.num_clusters++] = clusters[i];
			}
		}
	}

	// if first time, make sure old_origin is valid
	if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) )
	{
		VectorCopy( ent->s.origin, ent->s.old_origin );
		//ent->olds = ent->s;
	}
	ent->r.linkcount++;
}