624 lines
16 KiB
C
624 lines
16 KiB
C
// Copyright (C) 2001-2002 Raven Software
|
|
//
|
|
#include "g_local.h"
|
|
|
|
int G_MultipleDamageLocations(int hitLocation);
|
|
|
|
#define MISSILE_PRESTEP_TIME 50
|
|
|
|
/*
|
|
================
|
|
G_BounceMissile
|
|
|
|
================
|
|
*/
|
|
void G_BounceMissile( gentity_t *ent, trace_t *trace )
|
|
{
|
|
vec3_t velocity;
|
|
float dot;
|
|
int hitTime;
|
|
|
|
// nothing to do if already stationary
|
|
if ( ent->s.pos.trType == TR_STATIONARY )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// reflect the velocity on the trace plane
|
|
hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
|
|
BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
|
|
dot = DotProduct( velocity, trace->plane.normal );
|
|
VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
|
|
|
|
if ( ent->s.eFlags & EF_BOUNCE_HALF )
|
|
{
|
|
VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
|
|
// check for stop
|
|
if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 )
|
|
{
|
|
G_SetOrigin( ent, trace->endpos );
|
|
return;
|
|
}
|
|
}
|
|
else if ( ent->s.eFlags & EF_BOUNCE_SCALE )
|
|
{
|
|
// IF it hit a client then barely bounce off of them since they are "soft"
|
|
if ( trace->entityNum < MAX_CLIENTS )
|
|
{
|
|
VectorScale( ent->s.pos.trDelta, 0.04f, ent->s.pos.trDelta );
|
|
|
|
// Make sure the grenade doesnt continuously collide with teh player it hit
|
|
ent->target_ent = &g_entities[trace->entityNum];
|
|
}
|
|
else
|
|
{
|
|
VectorScale( ent->s.pos.trDelta, ent->bounceScale, ent->s.pos.trDelta );
|
|
}
|
|
|
|
// check for stop
|
|
if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 )
|
|
{
|
|
G_SetOrigin( ent, trace->endpos );
|
|
|
|
if ( ent->parent && ent->parent->client )
|
|
{
|
|
gentity_t* nearby;
|
|
|
|
// Find someone on the opposite team near wher ethe grenade landed
|
|
nearby = G_FindNearbyClient ( trace->endpos, ent->parent->client->sess.team==TEAM_RED?TEAM_BLUE:TEAM_RED, 800, NULL );
|
|
|
|
if ( nearby )
|
|
{
|
|
// Make sure there is someone around to hear them scream
|
|
nearby = G_FindNearbyClient ( trace->endpos, nearby->client->sess.team, 800, nearby );
|
|
G_VoiceGlobal ( nearby, "grenade", qtrue );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
|
|
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
|
ent->s.pos.trTime = level.time;
|
|
|
|
G_AddEvent( ent, EV_GRENADE_BOUNCE, trace->surfaceFlags& MATERIAL_MASK );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_ExplodeMissile
|
|
|
|
Explode a missile without an impact
|
|
================
|
|
*/
|
|
void G_ExplodeMissile( gentity_t *ent ) {
|
|
vec3_t dir;
|
|
vec3_t origin;
|
|
|
|
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
|
SnapVector( origin );
|
|
G_SetOrigin( ent, origin );
|
|
|
|
// we don't have a valid direction, so just point straight up
|
|
dir[0] = dir[1] = 0;
|
|
dir[2] = 1;
|
|
|
|
ent->s.eType = ET_GENERAL;
|
|
G_AddEvent( ent, EV_MISSILE_MISS, (DirToByte( dir ) << MATERIAL_BITS) | MATERIAL_NONE);
|
|
|
|
ent->freeAfterEvent = qtrue;
|
|
|
|
// All grenade explosions are now broadcast to ensure that fire and smoke is always seen
|
|
ent->r.svFlags |= SVF_BROADCAST;
|
|
|
|
// splash damage
|
|
if ( ent->splashDamage )
|
|
{
|
|
if (ent->dflags & DAMAGE_AREA_DAMAGE)
|
|
{
|
|
// do damage over time rather than instantly
|
|
G_CreateDamageArea ( ent->r.currentOrigin, ent->parent, ent->splashDamage*0.05f,ent->splashRadius, 8000,ent->methodOfDeath );
|
|
|
|
// do some instant damage
|
|
G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->damage, ent->splashRadius, ent,
|
|
1, ent->dflags, ent->splashMethodOfDeath );
|
|
}
|
|
else
|
|
{ // normal radius of effect damage
|
|
G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent,
|
|
1, ent->dflags, ent->splashMethodOfDeath );
|
|
}
|
|
}
|
|
|
|
trap_LinkEntity( ent );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_GrenadeThink
|
|
|
|
Marks the grenade ready to explode
|
|
================
|
|
*/
|
|
void G_GrenadeThink ( gentity_t* ent )
|
|
{
|
|
ent->s.eFlags |= EF_EXPLODE;
|
|
}
|
|
|
|
void G_RunStuckMissile( gentity_t *ent )
|
|
{
|
|
if ( ent->takedamage )
|
|
{
|
|
if ( ent->s.groundEntityNum >= 0 && ent->s.groundEntityNum < ENTITYNUM_WORLD )
|
|
{
|
|
gentity_t *other = &g_entities[ent->s.groundEntityNum];
|
|
|
|
if ( (!VectorCompare( vec3_origin, other->s.pos.trDelta ) && other->s.pos.trType != TR_STATIONARY) ||
|
|
(!VectorCompare( vec3_origin, other->s.apos.trDelta ) && other->s.apos.trType != TR_STATIONARY) )
|
|
{//thing I stuck to is moving or rotating now, kill me
|
|
G_Damage( ent, other, other, NULL, NULL, 99999, 0, MOD_CRUSH, HL_NONE );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// check think function
|
|
G_RunThink( ent );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_BounceProjectile
|
|
================
|
|
*/
|
|
void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
|
|
vec3_t v, newv;
|
|
float dot;
|
|
|
|
VectorSubtract( impact, start, v );
|
|
dot = DotProduct( v, dir );
|
|
VectorMA( v, -2*dot, dir, newv );
|
|
|
|
VectorNormalize(newv);
|
|
VectorMA(impact, 8192, newv, endout);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
G_CreateMissile
|
|
================
|
|
*/
|
|
gentity_t* G_CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t *owner, attackType_t attack )
|
|
{
|
|
gentity_t *missile;
|
|
|
|
missile = G_Spawn();
|
|
|
|
missile->nextthink = level.time + life;
|
|
missile->think = G_FreeEntity;
|
|
missile->s.eType = ET_MISSILE;
|
|
missile->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
|
missile->parent = owner;
|
|
missile->r.ownerNum = owner->s.number;
|
|
|
|
if ( attack == ATTACK_ALTERNATE )
|
|
{
|
|
missile->s.eFlags |= EF_ALT_FIRING;
|
|
}
|
|
|
|
missile->s.pos.trType = TR_LINEAR;
|
|
missile->s.pos.trTime = level.time;// - MISSILE_PRESTEP_TIME; // NOTENOTE This is a Quake 3 addition over JK2
|
|
missile->target_ent = NULL;
|
|
|
|
SnapVector(org);
|
|
VectorCopy( org, missile->s.pos.trBase );
|
|
VectorScale( dir, vel, missile->s.pos.trDelta );
|
|
VectorCopy( org, missile->r.currentOrigin);
|
|
SnapVector(missile->s.pos.trDelta);
|
|
|
|
return missile;
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_CauseAreaDamage
|
|
================
|
|
*/
|
|
void G_CauseAreaDamage( gentity_t *ent )
|
|
{
|
|
G_RadiusDamage ( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, 3, DAMAGE_NO_TEAMKILL, ent->methodOfDeath );
|
|
|
|
ent->s.time2--;
|
|
|
|
if ( ent->s.time2 <= 0 )
|
|
{
|
|
G_FreeEntity ( ent );
|
|
return;
|
|
}
|
|
|
|
ent->nextthink = level.time + 350;
|
|
trap_LinkEntity( ent );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_CreateDamageArea
|
|
================
|
|
*/
|
|
gentity_t* G_CreateDamageArea ( vec3_t origin, gentity_t* attacker, float damage, float radius, int duration, int mod )
|
|
{
|
|
gentity_t *damageArea;
|
|
|
|
damageArea = G_Spawn();
|
|
|
|
damageArea->nextthink = level.time + 350;
|
|
damageArea->think = G_CauseAreaDamage;
|
|
damageArea->s.eType = ET_DAMAGEAREA;
|
|
damageArea->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
|
damageArea->parent = attacker;
|
|
damageArea->r.ownerNum = attacker->s.number;
|
|
|
|
damageArea->s.pos.trType = TR_STATIONARY;
|
|
damageArea->s.pos.trTime = level.time;
|
|
damageArea->s.time2 = duration / 350;
|
|
damageArea->target_ent = NULL;
|
|
|
|
damageArea->classname = "DamageArea";
|
|
|
|
VectorSet( damageArea->r.maxs, 1, 1, 1 );
|
|
VectorScale( damageArea->r.maxs, -1, damageArea->r.mins );
|
|
|
|
damageArea->splashDamage = damage;
|
|
damageArea->splashRadius = radius;
|
|
damageArea->methodOfDeath = mod;
|
|
|
|
damageArea->dflags = DAMAGE_RADIUS;
|
|
damageArea->clipmask = MASK_SHOT;
|
|
|
|
VectorCopy( origin, damageArea->s.pos.trBase );
|
|
VectorCopy( origin, damageArea->r.currentOrigin);
|
|
SnapVector( damageArea->r.currentOrigin );
|
|
|
|
return damageArea;
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_MissileImpact
|
|
================
|
|
*/
|
|
extern gentity_t *CreateWeaponPickup(vec3_t pos,weapon_t weapon);
|
|
extern int G_GetHitLocation(gentity_t *target, vec3_t ppoint, vec3_t dir );
|
|
void G_MissileImpact( gentity_t *ent, trace_t *trace )
|
|
{
|
|
gentity_t *other;
|
|
vec3_t velocity;
|
|
int d;
|
|
other = &g_entities[trace->entityNum];
|
|
|
|
d = 0;
|
|
|
|
// check for bounce
|
|
if ( ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF | EF_BOUNCE_SCALE ) ) )
|
|
{
|
|
G_BounceMissile( ent, trace );
|
|
return;
|
|
}
|
|
|
|
// impact damage
|
|
if (other->takedamage)
|
|
{
|
|
// FIXME: wrong damage direction?
|
|
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
|
|
if ( VectorLength( velocity ) == 0 )
|
|
{
|
|
velocity[2] = 1; // stepped on a grenade
|
|
}
|
|
if ( ent->damage )
|
|
{
|
|
int location;
|
|
|
|
location = HL_NONE;
|
|
if ( other->client )
|
|
{
|
|
VectorNormalize ( velocity );
|
|
location = G_GetHitLocation ( other, ent->r.currentOrigin, velocity );
|
|
if ( ent->splashDamage )
|
|
{
|
|
location = G_MultipleDamageLocations(location);
|
|
}
|
|
}
|
|
|
|
d = G_Damage(other, ent, &g_entities[ent->r.ownerNum], velocity,
|
|
ent->s.origin, ent->damage, ent->dflags,
|
|
ent->methodOfDeath, location );
|
|
|
|
if ( d && other->client )
|
|
{
|
|
gentity_t *tent;
|
|
vec3_t hitdir;
|
|
|
|
// Put some procedural gore on the target.
|
|
tent = G_TempEntity( ent->r.currentOrigin, EV_EXPLOSION_HIT_FLESH );
|
|
|
|
// send entity and direction
|
|
VectorSubtract(other->r.currentOrigin, ent->r.currentOrigin, hitdir);
|
|
VectorNormalize(hitdir);
|
|
tent->s.eventParm = DirToByte( hitdir );
|
|
tent->s.otherEntityNum2 = other->s.number; // Victim entity number
|
|
|
|
// Pack the shot info into the temp end for gore
|
|
tent->s.time = ent->s.weapon + ((((int)other->s.apos.trBase[YAW]&0x7FFF) % 360) << 16);
|
|
if ( ent->s.eFlags & EF_ALT_FIRING )
|
|
{
|
|
tent->s.time += (ATTACK_ALTERNATE<<8);
|
|
}
|
|
|
|
VectorCopy ( other->r.currentOrigin, tent->s.angles );
|
|
SnapVector ( tent->s.angles );
|
|
}
|
|
}
|
|
}
|
|
|
|
// is it cheaper in bandwidth to just remove this ent and create a new
|
|
// one, rather than changing the missile into the explosion?
|
|
|
|
if ( d && other->client )
|
|
{
|
|
G_AddEvent( ent, EV_MISSILE_HIT,
|
|
(DirToByte( trace->plane.normal ) << MATERIAL_BITS) | (trace->surfaceFlags & MATERIAL_MASK));
|
|
ent->s.otherEntityNum = other->s.number;
|
|
if( ent->damage )
|
|
{
|
|
// FIXME: might be able to use the value from inside G_Damage to avoid recalc???
|
|
ent->s.otherEntityNum2 = G_GetHitLocation ( other, g_entities[ent->r.ownerNum].r.currentOrigin, velocity );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G_AddEvent( ent, EV_MISSILE_MISS,
|
|
(DirToByte( trace->plane.normal ) << MATERIAL_BITS) | (trace->surfaceFlags & MATERIAL_MASK));
|
|
|
|
// If missile should stick into impact point (e.g. a thrown knife).
|
|
if(!Q_stricmp(ent->classname,"Knife"))
|
|
{
|
|
// Create a pickup where we impacted.
|
|
vec3_t pickupPos;
|
|
gentity_t *pickupEnt;
|
|
|
|
VectorMA(trace->endpos,1,trace->plane.normal,pickupPos);
|
|
|
|
pickupEnt=CreateWeaponPickup(pickupPos,WP_KNIFE);
|
|
if(pickupEnt)
|
|
{
|
|
vec3_t knifeDir,knifeAngles;
|
|
|
|
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, knifeDir );
|
|
|
|
//FIXME: needs work to set model angles!
|
|
VectorNormalize ( knifeDir );
|
|
vectoangles(knifeDir,knifeAngles);
|
|
knifeAngles[YAW] += 90;
|
|
knifeAngles[ROLL] = knifeAngles[PITCH];
|
|
knifeAngles[PITCH] = 0;
|
|
|
|
pickupEnt->s.angles[0]=knifeAngles[0];
|
|
pickupEnt->s.angles[1]=knifeAngles[1];
|
|
pickupEnt->s.angles[2]=knifeAngles[2];
|
|
|
|
pickupEnt->think = G_FreeEntity;
|
|
pickupEnt->nextthink = level.time + 30000; // Stick around for 30 seconds
|
|
|
|
pickupEnt->count = 1;
|
|
|
|
pickupEnt->s.eFlags |= EF_ANGLE_OVERRIDE;
|
|
VectorCopy(pickupEnt->s.angles,pickupEnt->r.currentAngles);
|
|
VectorCopy(pickupEnt->s.angles,pickupEnt->s.apos.trBase);
|
|
pickupEnt->s.pos.trType=TR_STATIONARY;
|
|
pickupEnt->s.apos.trTime=level.time;
|
|
pickupEnt->clipmask = ent->clipmask;
|
|
pickupEnt->s.groundEntityNum = trace->entityNum;
|
|
trap_LinkEntity(pickupEnt);
|
|
}
|
|
}
|
|
}
|
|
|
|
ent->freeAfterEvent = qtrue;
|
|
|
|
// All grenade explosions are now broadcast to ensure that fire and smoke is always seen
|
|
ent->r.svFlags |= SVF_BROADCAST;
|
|
|
|
// change over to a normal entity right at the point of impact
|
|
ent->s.eType = ET_GENERAL;
|
|
|
|
SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth
|
|
|
|
G_SetOrigin( ent, trace->endpos );
|
|
|
|
// splash damage (doesn't apply to person directly hit)
|
|
if ( ent->splashDamage )
|
|
{
|
|
if (ent->dflags & DAMAGE_AREA_DAMAGE)
|
|
{
|
|
// do damage over time rather than instantly
|
|
G_CreateDamageArea ( trace->endpos, ent->parent, ent->splashDamage*0.10f,ent->splashRadius*2, 8000,ent->methodOfDeath );
|
|
|
|
// do some instant damage
|
|
G_RadiusDamage( trace->endpos, ent->parent, ent->damage, ent->splashRadius, other,
|
|
1, ent->dflags, ent->splashMethodOfDeath );
|
|
}
|
|
else
|
|
{ // normal radius of effect damage
|
|
G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
|
|
other, 1, ent->dflags, ent->splashMethodOfDeath );
|
|
}
|
|
}
|
|
|
|
trap_LinkEntity( ent );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_RunMissile
|
|
================
|
|
*/
|
|
void G_RunMissile( gentity_t *ent )
|
|
{
|
|
vec3_t origin;
|
|
trace_t tr;
|
|
int passent;
|
|
|
|
// get current position
|
|
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
|
|
|
// if this missile bounced off an invulnerability sphere
|
|
if ( ent->target_ent )
|
|
{
|
|
passent = ent->target_ent->s.number;
|
|
}
|
|
else
|
|
{
|
|
// ignore interactions with the missile owner
|
|
passent = ent->r.ownerNum;
|
|
}
|
|
|
|
// Special case where the grenade has gone up into the sky
|
|
if ( ent->s.eFlags & EF_INSKY )
|
|
{
|
|
// Check to see if its out of the world on the X,Y plane
|
|
// or below it (above is a special case)
|
|
if ( origin[0] < level.worldMins[0] ||
|
|
origin[1] < level.worldMins[1] ||
|
|
origin[0] > level.worldMaxs[0] ||
|
|
origin[1] > level.worldMaxs[1] ||
|
|
origin[2] < level.worldMins[2] )
|
|
{
|
|
G_FreeEntity( ent );
|
|
return;
|
|
}
|
|
|
|
// Above it only kills it if the item has no gravity
|
|
if ( origin[2] > level.worldMaxs[2] && ent->s.pos.trType != TR_GRAVITY && ent->s.pos.trType != TR_LIGHTGRAVITY)
|
|
{
|
|
G_FreeEntity( ent );
|
|
return;
|
|
}
|
|
|
|
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
|
|
|
|
// Hit another sky, must be reentering
|
|
if ( tr.fraction == 1.0f )
|
|
{
|
|
ent->s.eFlags &= ~EF_INSKY;
|
|
VectorCopy ( origin, ent->r.currentOrigin );
|
|
}
|
|
|
|
VectorCopy ( origin, ent->r.currentOrigin );
|
|
trap_LinkEntity ( ent );
|
|
}
|
|
else
|
|
{
|
|
// Run the same test again because the condition may have changed as a result
|
|
// of the greande falling below the sky again
|
|
// Loop this trace so we can break windows
|
|
while ( 1 )
|
|
{
|
|
// trace a line from the previous position to the current position
|
|
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
|
|
|
|
// If its glass then redo the trace after breaking the glass
|
|
if ( tr.fraction != 1 && !Q_stricmp ( g_entities[tr.entityNum].classname, "func_glass" ) )
|
|
{
|
|
g_entities[tr.entityNum].use ( &g_entities[tr.entityNum], ent, ent );
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( tr.startsolid || tr.allsolid )
|
|
{
|
|
// make sure the tr.entityNum is set to the entity we're stuck in
|
|
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
|
|
tr.fraction = 0;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( tr.endpos, ent->r.currentOrigin );
|
|
}
|
|
|
|
trap_LinkEntity( ent );
|
|
|
|
if ( tr.fraction != 1 )
|
|
{
|
|
// Hit the sky or moving through something
|
|
if ( tr.surfaceFlags & SURF_NOIMPACT )
|
|
{
|
|
// Dont kill a missle that hits the sky and has gravity
|
|
if ( tr.surfaceFlags & SURF_SKY )
|
|
{
|
|
ent->s.eFlags |= EF_INSKY;
|
|
ent->r.svFlags |= SVF_BROADCAST;
|
|
VectorCopy ( origin, ent->r.currentOrigin );
|
|
trap_LinkEntity( ent );
|
|
}
|
|
else
|
|
{
|
|
G_FreeEntity( ent );
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G_MissileImpact( ent, &tr );
|
|
|
|
// Is it time to explode
|
|
if ( ent->s.eFlags & EF_EXPLODE )
|
|
{
|
|
ent->s.eFlags &= (~EF_EXPLODE);
|
|
G_ExplodeMissile ( ent );
|
|
return;
|
|
}
|
|
|
|
// Exploded
|
|
if ( ent->s.eType != ET_MISSILE )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// Is it time to explode
|
|
else if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags & EF_EXPLODE) )
|
|
{
|
|
ent->s.eFlags &= (~EF_EXPLODE);
|
|
G_ExplodeMissile ( ent );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If this is a knife then reorient its angles
|
|
if ( ent->s.weapon == WP_KNIFE )
|
|
{
|
|
vec3_t vel;
|
|
|
|
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, vel );
|
|
vectoangles( vel, ent->s.angles );
|
|
|
|
ent->s.angles[YAW] += 90;
|
|
// ent->s.angles[ROLL] = ent->s.angles[PITCH];
|
|
ent->s.angles[ROLL] = 0;
|
|
ent->s.angles[PITCH] = 0;
|
|
}
|
|
|
|
// check think function after bouncing
|
|
G_RunThink( ent );
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|