Beispiel #1
0
static void GM_CheckFireState( void )
{
	if ( enemyCS4 )
	{//if have a clear shot, always try
		return;
	}

	if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) )
	{//if moving at all, don't do this
		return;
	}

	//See if we should continue to fire on their last position
	if ( !hitAlly4 && NPCInfo->enemyLastSeenTime > 0 )
	{
		if ( level.time - NPCInfo->enemyLastSeenTime < 10000 )
		{
			if ( !Q_irand( 0, 10 ) )
			{
				//Fire on the last known position
				vec3_t	muzzle, dir, angles;
				qboolean tooClose = qfalse;
				qboolean tooFar = qfalse;
				float distThreshold;
				float dist;

				CalcEntitySpot( NPC, SPOT_HEAD, muzzle );
				if ( VectorCompare( impactPos4, vec3_origin ) )
				{//never checked ShotEntity this frame, so must do a trace...
					trace_t tr;
					//vec3_t	mins = {-2,-2,-2}, maxs = {2,2,2};
					vec3_t	forward, end;
					AngleVectors( NPC->client->ps.viewangles, forward, NULL, NULL );
					VectorMA( muzzle, 8192, forward, end );
					trap_Trace( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
					VectorCopy( tr.endpos, impactPos4 );
				}

				//see if impact would be too close to me
				distThreshold = 16384/*128*128*/;//default
				if ( NPC->s.weapon == WP_REPEATER )
				{
					if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
					{
						distThreshold = 65536/*256*256*/;
					}
				}

				dist = DistanceSquared( impactPos4, muzzle );

				if ( dist < distThreshold )
				{//impact would be too close to me
					tooClose = qtrue;
				}
				else if ( level.time - NPCInfo->enemyLastSeenTime > 5000 )
				{//we've haven't seen them in the last 5 seconds
					//see if it's too far from where he is
					distThreshold = 65536/*256*256*/;//default
					if ( NPC->s.weapon == WP_REPEATER )
					{
						if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
						{
							distThreshold = 262144/*512*512*/;
						}
					}
					dist = DistanceSquared( impactPos4, NPCInfo->enemyLastSeenLocation );
					if ( dist > distThreshold )
					{//impact would be too far from enemy
						tooFar = qtrue;
					}
				}

				if ( !tooClose && !tooFar )
				{//okay too shoot at last pos
					VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir );
					VectorNormalize( dir );
					vectoangles( dir, angles );

					NPCInfo->desiredYaw		= angles[YAW];
					NPCInfo->desiredPitch	= angles[PITCH];

					shoot4 = qtrue;
					faceEnemy4 = qfalse;
					return;
				}
			}
		}
	}
}
Beispiel #2
0
//-----------------------------------------------------
void turretG2_base_think( gentity_t *self )
//-----------------------------------------------------
{
	qboolean	turnOff = qtrue;
	float		enemyDist;
	vec3_t		enemyDir, org, org2;

	self->nextthink = level.time + level.frameTime;

	if ( self->health <= 0 )
	{//dead
		if (self->spawnflags & SPF_TURRETG2_CANRESPAWN)
		{//can respawn
			if ( self->genericValue5 && self->genericValue5 < level.time )
			{ //we are dead, see if it's time to respawn
				turretG2_respawn( self );
			}
		}
		return;
	}
	else if ( self->spawnflags & 1 )
	{// not turned on
		turretG2_turnoff( self );
		turretG2_aim( self );

		// No target
		self->flags |= FL_NOTARGET;
		return;
	}
	else
	{
		// I'm all hot and bothered
		self->flags &= ~FL_NOTARGET;
	}

	if ( self->enemy )
	{
		if ( self->enemy->health < 0 
			|| !self->enemy->inuse )
		{
			self->enemy = NULL;
		}
	}

	if ( self->last_move_time < level.time )
	{//MISNOMER: used a enemy recalcing debouncer
		if ( turretG2_find_enemies( self ) )
		{//found one
			turnOff = qfalse;
			if ( self->enemy->client )
			{//hold on to clients for a min of 3 seconds
				self->last_move_time = level.time + 3000;
			}
			else
			{//hold less
				self->last_move_time = level.time + 500;
			}
		}
	}

	if ( self->enemy != NULL )
	{
		if ( self->enemy->client && self->enemy->client->sess.sessionTeam == TEAM_SPECTATOR )
		{//don't keep going after spectators
			self->enemy = NULL;
		}
		else
		{//FIXME: remain single-minded or look for a new enemy every now and then?
			// enemy is alive
			VectorSubtract( self->enemy->r.currentOrigin, self->r.currentOrigin, enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < self->radius * self->radius )
			{
				// was in valid radius
				if ( trap_InPVS( self->r.currentOrigin, self->enemy->r.currentOrigin ) )
				{
					// Every now and again, check to see if we can even trace to the enemy
					trace_t tr;

					if ( self->enemy->client )
					{
						VectorCopy( self->enemy->client->renderInfo.eyePoint, org );
					}
					else
					{
						VectorCopy( self->enemy->r.currentOrigin, org );
					}
					VectorCopy( self->r.currentOrigin, org2 );
					if ( self->spawnflags & 2 )
					{
						org2[2] += 10;
					}
					else
					{
						org2[2] -= 10;
					}
					trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );

					if ( !tr.allsolid && !tr.startsolid && tr.entityNum == self->enemy->s.number )
					{
						turnOff = qfalse;	// Can see our enemy
					}
				}
			}

		}
	}

	if ( turnOff )
	{
		if ( self->bounceCount < level.time ) // bounceCount is used to keep the thing from ping-ponging from on to off
		{
			turretG2_turnoff( self );
		}
	}
	else
	{
		// keep our enemy for a minimum of 2 seconds from now
		self->bounceCount = level.time + 2000 + random() * 150;
	}

	turretG2_aim( self );
	if ( !turnOff )
	{
		turretG2_head_think( self );
	}
}
Beispiel #3
0
/**
* @brief Traces down to find where an item should rest, instead of letting them
*        free fall from their spawn points.
*/
void FinishSpawningItem(gentity_t *ent)
{
	trace_t tr;
	vec3_t  dest;
	vec3_t  maxs;

	if (ent->spawnflags & 1)     // suspended
	{
		VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
		VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
		VectorCopy(ent->r.maxs, maxs);
	}
	else
	{
		// had to modify this so that items would spawn in shelves
		VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0);
		VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
		VectorCopy(ent->r.maxs, maxs);
		maxs[2] /= 2;
	}

	ent->r.contents   = CONTENTS_TRIGGER | CONTENTS_ITEM;
	ent->touch        = Touch_Item_Auto;
	ent->s.eType      = ET_ITEM;
	ent->s.modelindex = ent->item - bg_itemlist;        // store item number in modelindex

	ent->s.otherEntityNum2 = 0;     // takes modelindex2's place in signaling a dropped item
	// we don't use this (yet, anyway) so I'm taking it so you can specify a model for treasure items and clipboards
	//ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
	if (ent->model)
	{
		ent->s.modelindex2 = G_ModelIndex(ent->model);
	}

	// using an item causes it to respawn
	ent->use = Use_Item;

	// moved this up so it happens for suspended items too (and made it a function)
	G_SetAngle(ent, ent->s.angles);

	if (ent->spawnflags & 1)        // suspended
	{
		G_SetOrigin(ent, ent->s.origin);
	}
	else
	{
		VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096);
		trap_Trace(&tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID);

		if (tr.startsolid)
		{
			vec3_t temp;

			VectorCopy(ent->s.origin, temp);
			temp[2] -= ITEM_RADIUS;

			VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096);
			trap_Trace(&tr, temp, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID);
		}

		if (tr.startsolid)
		{
			G_Printf("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
			G_FreeEntity(ent);
			return;
		}

		// allow to ride movers
		ent->s.groundEntityNum = tr.entityNum;

		G_SetOrigin(ent, tr.endpos);
	}

	if (ent->spawnflags & 2)          // spin
	{
		ent->s.eFlags |= EF_SPINNING;
	}

	// team slaves and targeted items aren't present at start
	if ((ent->flags & FL_TEAMSLAVE) || ent->targetname)
	{
		ent->flags |= FL_NODRAW;
		//ent->s.eFlags |= EF_NODRAW;
		ent->r.contents = 0;
		return;
	}

	// health/ammo can potentially be multi-stage (multiple use)
	if (ent->item->giType == IT_HEALTH || ent->item->giType == IT_AMMO)
	{
		int i;

		// having alternate models defined in bg_misc.c for a health or ammo item specify it as "multi-stage"
		// - left-hand operand of comma expression has no effect
		// initial line: for(i=0;i<4,ent->item->world_model[i];i++) {}
		for (i = 0; i < 4 && ent->item->world_model[i] ; i++)
		{
		}

		ent->s.density = i - 1;   // store number of stages in 'density' for client (most will have '1')
	}

	trap_LinkEntity(ent);
}
Beispiel #4
0
/**
* @brief Spawns an item and tosses it forward.
*/
gentity_t *LaunchItem(gitem_t *item, vec3_t origin, vec3_t velocity, int ownerNum)
{
	gentity_t *dropped = G_Spawn();
	trace_t   tr;
	vec3_t    vec, temp;

	dropped->s.eType           = ET_ITEM;
	dropped->s.modelindex      = item - bg_itemlist; // store item number in modelindex
	dropped->s.otherEntityNum2 = 1; // this is taking modelindex2's place for a dropped item

	dropped->classname = item->classname;
	dropped->item      = item;
	VectorSet(dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0);              // so items sit on the ground
	VectorSet(dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, 2 * ITEM_RADIUS);    // so items sit on the ground
	dropped->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM;

	dropped->clipmask = CONTENTS_SOLID | CONTENTS_MISSILECLIP;      // fix for items falling through grates

	dropped->touch = Touch_Item_Auto;

	trap_Trace(&tr, origin, dropped->r.mins, dropped->r.maxs, origin, ownerNum, MASK_SOLID);
	if (tr.startsolid)
	{
		int i;

		VectorSubtract(g_entities[ownerNum].s.origin, origin, temp);
		VectorNormalize(temp);

		for (i = 16; i <= 48; i += 16)
		{
			VectorScale(temp, i, vec);
			VectorAdd(origin, vec, origin);

			trap_Trace(&tr, origin, dropped->r.mins, dropped->r.maxs, origin, ownerNum, MASK_SOLID);
			if (!tr.startsolid)
			{
				break;
			}
		}
	}

	G_SetOrigin(dropped, origin);
	dropped->s.pos.trType = TR_GRAVITY;
	dropped->s.pos.trTime = level.time;
	VectorCopy(velocity, dropped->s.pos.trDelta);

	// set yaw to parent angles
	temp[PITCH] = 0;
	temp[YAW]   = g_entities[ownerNum].s.apos.trBase[YAW];
	temp[ROLL]  = 0;
	G_SetAngle(dropped, temp);

	dropped->s.eFlags |= EF_BOUNCE_HALF;

	if (item->giType == IT_TEAM)     // Special case for CTF flags
	{
		gentity_t *flag = &g_entities[g_entities[ownerNum].client->flagParent];

		dropped->s.otherEntityNum = g_entities[ownerNum].client->flagParent;    // store the entitynum of our original flag spawner
		dropped->s.density        = 1;
		dropped->think            = Team_DroppedFlagThink;
		dropped->nextthink        = level.time + 30000;

		if (level.gameManager)
		{
			G_Script_ScriptEvent(level.gameManager, "trigger", flag->item->giTag == PW_REDFLAG ? "allied_object_dropped" : "axis_object_dropped");
		}
		G_Script_ScriptEvent(flag, "trigger", "dropped");
	}
	else     // auto-remove after 30 seconds
	{
		dropped->think     = G_FreeEntity;
		dropped->nextthink = level.time + 30000;
	}

	dropped->flags = FL_DROPPED_ITEM;

	trap_LinkEntity(dropped);

	return dropped;
}
Beispiel #5
0
void VEH_TurretThink( Vehicle_t *pVeh, gentity_t *parent, int turretNum )
//-----------------------------------------------------
{
	qboolean	doAim = qfalse;
	float		enemyDist, rangeSq;
	vec3_t		enemyDir;
	turretStats_t *turretStats = &pVeh->m_pVehicleInfo->turret[turretNum];
	vehWeaponInfo_t	*vehWeapon = NULL;
	gentity_t	*turretEnemy = NULL;
	int			curMuzzle = 0;//?
	

	if ( !turretStats || !turretStats->iAmmoMax )
	{//not a valid turret
		return;
	}
	
	if ( turretStats->passengerNum 
		&& pVeh->m_iNumPassengers >= turretStats->passengerNum )
	{//the passenger that has control of this turret is on the ship
		VEH_TurretObeyPassengerControl( pVeh, parent, turretNum );
		return;
	}
	else if ( !turretStats->bAI )//try AI
	{//this turret does not think on its own.
		return;
	}
	//okay, so it has AI, but still don't think if there's no pilot!
	if ( !pVeh->m_pPilot )
	{
		return;
	}

	vehWeapon = &g_vehWeaponInfo[turretStats->iWeapon];
	rangeSq = (turretStats->fAIRange*turretStats->fAIRange);
	curMuzzle = pVeh->turretStatus[turretNum].nextMuzzle;

	if ( pVeh->turretStatus[turretNum].enemyEntNum < ENTITYNUM_WORLD )
	{
		turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum];
		if ( turretEnemy->health < 0 
			|| !turretEnemy->inuse
			|| turretEnemy == ((gentity_t*)pVeh->m_pPilot)//enemy became my pilot///?
			|| turretEnemy == parent
			|| turretEnemy->r.ownerNum == parent->s.number // a passenger?
			|| ( turretEnemy->client && turretEnemy->client->sess.sessionTeam == TEAM_SPECTATOR ) )
		{//don't keep going after spectators, pilot, self, dead people, etc.
			turretEnemy = NULL;
			pVeh->turretStatus[turretNum].enemyEntNum = ENTITYNUM_NONE;
		}
	}

	if ( pVeh->turretStatus[turretNum].enemyHoldTime < level.time )
	{
		if ( VEH_TurretFindEnemies( pVeh, parent, turretStats, turretNum, curMuzzle ) )
		{
			turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum];
			doAim = qtrue;
		}
		else if ( parent->enemy && parent->enemy->s.number < ENTITYNUM_WORLD )
		{
			if ( g_gametype.integer < GT_TEAM
				|| !OnSameTeam( parent->enemy, parent ) )
			{//either not in a team game or the enemy isn't on the same team
				turretEnemy = parent->enemy;
				doAim = qtrue;
			}
		}
		if ( turretEnemy )
		{//found one
			if ( turretEnemy->client )
			{//hold on to clients for a min of 3 seconds
				pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 3000;
			}
			else
			{//hold less
				pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 500;
			}
		}
	}
	if ( turretEnemy != NULL )
	{
		if ( turretEnemy->health > 0 )
		{
			// enemy is alive
			WP_CalcVehMuzzle( parent, curMuzzle );
			VectorSubtract( turretEnemy->r.currentOrigin, pVeh->m_vMuzzlePos[curMuzzle], enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < rangeSq )
			{
				// was in valid radius
				if ( trap_InPVS( pVeh->m_vMuzzlePos[curMuzzle], turretEnemy->r.currentOrigin ) )
				{
					// Every now and again, check to see if we can even trace to the enemy
					trace_t tr;
					vec3_t start, end;
					VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], start );

					VectorCopy( turretEnemy->r.currentOrigin, end );
					trap_Trace( &tr, start, NULL, NULL, end, parent->s.number, MASK_SHOT );

					if ( tr.entityNum == turretEnemy->s.number
						|| (!tr.allsolid && !tr.startsolid ) )
					{
						doAim = qtrue;	// Can see our enemy
					}
				}
			}
		}
	}

	if ( doAim )
	{
		vec3_t aimAngles;
		if ( VEH_TurretAim( pVeh, parent, turretEnemy, turretStats, vehWeapon, turretNum, curMuzzle, aimAngles ) )
		{
			VEH_TurretCheckFire( pVeh, parent, /*turretEnemy,*/ turretStats, vehWeapon, turretNum, curMuzzle );
		}
	}
}
Beispiel #6
0
void
Bullet_Fire(Gentity *ent, float spread, int damage)
{
	Trace tr;
	Vec3	end;
#ifdef MISSIONPACK
	Vec3	impactpoint, bouncedir;
#endif
	float	r;
	float	u;
	Gentity	*tent;
	Gentity	*traceEnt;
	int i, passent;

	damage *= s_quadFactor;

	r = random() * M_PI * 2.0f;
	u = sin(r) * crandom() * spread * 16;
	r = cos(r) * crandom() * spread * 16;
	saddv3 (muzzle, 8192*16, forward, end);
	saddv3 (end, r, right, end);
	saddv3 (end, u, up, end);

	passent = ent->s.number;
	for(i = 0; i < 10; i++){

		trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT);
		if(tr.surfaceFlags & SURF_NOIMPACT)
			return;

		traceEnt = &g_entities[ tr.entityNum ];

		/* snap the endpos to integers, but nudged towards the line */
		snapv3Towards(tr.endpos, muzzle);

		/* send bullet impact */
		if(traceEnt->takedamage && traceEnt->client){
			tent = G_TempEntity(tr.endpos, EV_BULLET_HIT_FLESH);
			tent->s.eventParm = traceEnt->s.number;
			if(LogAccuracyHit(traceEnt, ent))
				ent->client->accuracy_hits++;
		}else{
			tent = G_TempEntity(tr.endpos, EV_BULLET_HIT_WALL);
			tent->s.eventParm = DirToByte(tr.plane.normal);
		}
		tent->s.otherEntityNum = ent->s.number;

		if(traceEnt->takedamage){
#if 0 /* we still want bouncing rails, so saving for later */
			if(traceEnt->client &&
			   traceEnt->client->invulnerabilityTime >
			   level.time){
				if(G_InvulnerabilityEffect(traceEnt, forward,
					   tr.endpos, impactpoint, bouncedir)){
					G_BounceProjectile(muzzle, impactpoint,
						bouncedir,
						end);
					copyv3(impactpoint, muzzle);
					/* the player can hit him/herself with the bounced rail */
					passent = ENTITYNUM_NONE;
				}else{
					copyv3(tr.endpos, muzzle);
					passent = traceEnt->s.number;
				}
				continue;
			}else{
#endif
			G_Damage(traceEnt, ent, ent, forward, tr.endpos,
				damage, 0, MOD_MACHINEGUN);
#ifdef MISSIONPACK
		}
#endif
		}
		break;
	}
}
Beispiel #7
0
/*
=============
SpawnCorpse

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
void SpawnCorpse( gentity_t *ent )
{
  gentity_t   *body;
  int         contents;
  vec3_t      origin, dest;
  trace_t     tr;
  float       vDiff;

  VectorCopy( ent->r.currentOrigin, origin );

  trap_UnlinkEntity( ent );

  // if client is in a nodrop area, don't leave the body
  contents = trap_PointContents( origin, -1 );
  if( contents & CONTENTS_NODROP )
    return;

  body = G_Spawn( );

  VectorCopy( ent->s.apos.trBase, body->s.angles );
  body->s.eFlags = EF_DEAD;
  body->s.eType = ET_CORPSE;
  body->s.number = body - g_entities;
  body->timestamp = level.time;
  body->s.event = 0;
  body->r.contents = CONTENTS_CORPSE;
  body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
  body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;

  if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
    body->classname = "humanCorpse";
  else
    body->classname = "alienCorpse";

  body->s.misc = MAX_CLIENTS;

  body->think = BodySink;
  body->nextthink = level.time + 20000;

  body->s.legsAnim = ent->s.legsAnim;

  if( !body->nonSegModel )
  {
    switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
    {
      case BOTH_DEATH1:
      case BOTH_DEAD1:
        body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
        break;
      case BOTH_DEATH2:
      case BOTH_DEAD2:
        body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
        break;
      case BOTH_DEATH3:
      case BOTH_DEAD3:
      default:
        body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
        break;
    }
  }
  else
  {
    switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
    {
      case NSPA_DEATH1:
      case NSPA_DEAD1:
        body->s.legsAnim = NSPA_DEAD1;
        break;
      case NSPA_DEATH2:
      case NSPA_DEAD2:
        body->s.legsAnim = NSPA_DEAD2;
        break;
      case NSPA_DEATH3:
      case NSPA_DEAD3:
      default:
        body->s.legsAnim = NSPA_DEAD3;
        break;
    }
  }

  body->takedamage = qfalse;

  body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
  ent->health = 0;

  //change body dimensions
  BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs );
  vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ];

  //drop down to match the *model* origins of ent and body
  VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff );
  trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask );
  VectorCopy( tr.endpos, origin );

  G_SetOrigin( body, origin );
  VectorCopy( origin, body->s.origin );
  body->s.pos.trType = TR_GRAVITY;
  body->s.pos.trTime = level.time;
  VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );

  VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
  trap_LinkEntity( body );
}
Beispiel #8
0
/*
================
G_RunMissile
================
*/
void G_RunMissile( gentity_t *ent ) {
	vec3_t		origin, groundSpot;
	trace_t		tr;
	int			passent;
	qboolean	isKnockedSaber = qfalse;

	if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF))
	{
		isKnockedSaber = qtrue;
		ent->s.pos.trType = TR_GRAVITY;
	}

	// 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
		if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) 
			&& (ent->s.eFlags&EF_JETPACK_ACTIVE) )
		{//A vehicle missile that should be solid to its owner
			//I don't care about hitting my owner
			passent = ent->s.number;
		}
		else
		{
			passent = ent->r.ownerNum;
		}
	}
	// trace a line from the previous position to the current position
	if (d_projectileGhoul2Collision.integer)
	{
		trap_G2Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );

		if (tr.fraction != 1.0 && tr.entityNum < ENTITYNUM_WORLD)
		{
			gentity_t *g2Hit = &g_entities[tr.entityNum];

			if (g2Hit->inuse && g2Hit->client && g2Hit->ghoul2)
			{ //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with.
				g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags;
				g2Hit->client->g2LastSurfaceTime = level.time;
			}

			if (g2Hit->ghoul2)
			{
				tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here.
			}
		}
	}
	else
	{
		trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
	}

	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 );
	}

	if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1))
	{
		VectorCopy( origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
		goto passthrough;
	}

	trap_LinkEntity( ent );

	if (ent->s.weapon == G2_MODEL_PART && !ent->bounceCount)
	{
		vec3_t lowerOrg;
		trace_t trG;

		VectorCopy(ent->r.currentOrigin, lowerOrg);
		lowerOrg[2] -= 1;
		trap_Trace( &trG, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, lowerOrg, passent, ent->clipmask );

		VectorCopy(trG.endpos, groundSpot);

		if (!trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD)
		{
			ent->s.groundEntityNum = trG.entityNum;
		}
		else
		{
			ent->s.groundEntityNum = ENTITYNUM_NONE;
		}
	}

	if ( tr.fraction != 1) {
		// never explode or bounce on sky
		if ( tr.surfaceFlags & SURF_NOIMPACT ) {
			// If grapple, reset owner
			if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) {
				ent->parent->client->hook = NULL;
			}

			if ((ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber)
			{
				G_RunThink( ent );
				return;
			}
			else if (ent->s.weapon != G2_MODEL_PART)
			{
				G_FreeEntity( ent );
				return;
			}
		}

#if 0 //will get stomped with missile impact event...
		if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS &&
			(tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC))
		{ //player or NPC, try making a mark on him
			/*
			gentity_t *evEnt = G_TempEntity(ent->r.currentOrigin, EV_GHOUL2_MARK);

			evEnt->s.owner = tr.entityNum; //the entity the mark should be placed on
			evEnt->s.weapon = ent->s.weapon; //the weapon used (to determine mark type)
			VectorCopy(ent->r.currentOrigin, evEnt->s.origin); //the point of impact

			//origin2 gets the predicted trajectory-based position.
			BG_EvaluateTrajectory( &ent->s.pos, level.time, evEnt->s.origin2 );

			//If they are the same, there will be problems.
			if (VectorCompare(evEnt->s.origin, evEnt->s.origin2))
			{
				evEnt->s.origin2[2] += 2; //whatever, at least it won't mess up.
			}
			*/
			//ok, let's try adding it to the missile ent instead (tempents bad!)
			G_AddEvent(ent, EV_GHOUL2_MARK, 0);

			//copy current pos to s.origin, and current projected traj to origin2
			VectorCopy(ent->r.currentOrigin, ent->s.origin);
			BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 );

			//the index for whoever we are hitting
			ent->s.otherEntityNum = tr.entityNum;

			if (VectorCompare(ent->s.origin, ent->s.origin2))
			{
				ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up.
			}
		}
#else
		if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS &&
			(tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC))
		{ //player or NPC, try making a mark on him
			//copy current pos to s.origin, and current projected traj to origin2
			VectorCopy(ent->r.currentOrigin, ent->s.origin);
			BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 );

			if (VectorCompare(ent->s.origin, ent->s.origin2))
			{
				ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up.
			}
		}
#endif

		G_MissileImpact( ent, &tr );

		if (tr.entityNum == ent->s.otherEntityNum)
		{ //if the impact event other and the trace ent match then it's ok to do the g2 mark
			ent->s.trickedentindex = 1;
		}

		if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART )
		{
			return;		// exploded
		}
	}

passthrough:
	if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) )
	{//stuck missiles should check some special stuff
		G_RunStuckMissile( ent );
		return;
	}

	if (ent->s.weapon == G2_MODEL_PART)
	{
		if (ent->s.groundEntityNum == ENTITYNUM_WORLD)
		{
			ent->s.pos.trType = TR_LINEAR;
			VectorClear(ent->s.pos.trDelta);
			ent->s.pos.trTime = level.time;

			VectorCopy(groundSpot, ent->s.pos.trBase);
			VectorCopy(groundSpot, ent->r.currentOrigin);

			if (ent->s.apos.trType != TR_STATIONARY)
			{
				ent->s.apos.trType = TR_STATIONARY;
				ent->s.apos.trTime = level.time;

				ent->s.apos.trBase[ROLL] = 0;
				ent->s.apos.trBase[PITCH] = 0;
			}
		}
	}

	// check think function after bouncing
	G_RunThink( ent );
}
Beispiel #9
0
/*
================
G_RunMissile

================
*/
void G_RunMissile( gentity_t *ent )
{
    vec3_t    origin;
    trace_t   tr;
    int       passent;
    qboolean  impact = qfalse;

    // get current position
    BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );

    // ignore interactions with the missile owner
    passent = ent->r.ownerNum;

    // general trace to see if we hit anything at all
    trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs,
                origin, passent, ent->clipmask );

    if( tr.startsolid || tr.allsolid )
    {
        tr.fraction = 0.0f;
        VectorCopy( ent->r.currentOrigin, tr.endpos );
    }

    if( tr.fraction < 1.0f )
    {
        if( !ent->pointAgainstWorld || tr.contents & CONTENTS_BODY )
        {
            // We hit an entity or we don't care
            impact = qtrue;
        }
        else
        {
            trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, origin,
                        passent, ent->clipmask );

            if( tr.fraction < 1.0f )
            {
                // Hit the world with point trace
                impact = qtrue;
            }
            else
            {
                if( tr.contents & CONTENTS_BODY )
                {
                    // Hit an entity
                    impact = qtrue;
                }
                else
                {
                    trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs,
                                origin, passent, CONTENTS_BODY );

                    if( tr.fraction < 1.0f )
                        impact = qtrue;
                }
            }
        }
    }

    VectorCopy( tr.endpos, ent->r.currentOrigin );

    if( impact )
    {
        if( tr.surfaceFlags & SURF_NOIMPACT )
        {
            // Never explode or bounce on sky
            G_FreeEntity( ent );
            return;
        }

        G_MissileImpact( ent, &tr );

        if( ent->s.eType != ET_MISSILE )
            return;   // exploded
    }

    ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into...
    trap_LinkEntity( ent );
    ent->r.contents = 0; //...encoding bbox information

    // check think function after bouncing
    G_RunThink( ent );
}
Beispiel #10
0
qboolean G_PredictPlayerSlideMove( gentity_t *ent, float frametime ) {
	int			bumpcount, numbumps;
	vec3_t		dir;
	float		d;
	int			numplanes;
	vec3_t		planes[MAX_CLIP_PLANES];
	vec3_t		primal_velocity, velocity, origin;
	vec3_t		clipVelocity;
	int			i, j, k;
	trace_t	trace;
	vec3_t		end;
	float		time_left;
	float		into;
	vec3_t		endVelocity;
	vec3_t		endClipVelocity;
//	vec3_t		worldUp = { 0.0f, 0.0f, 1.0f };
	
	numbumps = 4;

	VectorCopy( ent->s.pos.trDelta, primal_velocity );
	VectorCopy( primal_velocity, velocity );
	VectorCopy( ent->s.pos.trBase, origin );

	VectorCopy( velocity, endVelocity );

	time_left = frametime;

	numplanes = 0;

	for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) {

		// calculate position we are trying to move to
		VectorMA( origin, time_left, velocity, end );

		// see if we can make it there
		trap_Trace( &trace, origin, ent->r.mins, ent->r.maxs, end, ent->s.number, ent->clipmask );

		if (trace.allsolid) {
			// entity is completely trapped in another solid
			VectorClear( velocity );
			VectorCopy( origin, ent->s.pos.trBase );
			return qtrue;
		}

		if (trace.fraction > 0) {
			// actually covered some distance
			VectorCopy( trace.endpos, origin );
		}

		if (trace.fraction == 1) {
			break;		// moved the entire distance
		}

		time_left -= time_left * trace.fraction;

		if ( numplanes >= MAX_CLIP_PLANES ) {
			// this shouldn't really happen
			VectorClear( velocity );
			VectorCopy( origin, ent->s.pos.trBase );
			return qtrue;
		}

		//
		// if this is the same plane we hit before, nudge velocity
		// out along it, which fixes some epsilon issues with
		// non-axial planes
		//
		for ( i = 0; i < numplanes; i++ ) {
			if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) {
				VectorAdd( trace.plane.normal, velocity, velocity );
				break;
			}
		}

		if ( i < numplanes ) {
			continue;
		}

		VectorCopy( trace.plane.normal, planes[numplanes] );
		numplanes++;

		//
		// modify velocity so it parallels all of the clip planes
		//

		// find a plane that it enters
		for ( i = 0; i < numplanes; i++ ) {
			into = DotProduct( velocity, planes[i] );
			if ( into >= 0.1 ) {
				continue;		// move doesn't interact with the plane
			}

			// slide along the plane
			G_PredictPlayerClipVelocity( velocity, planes[i], clipVelocity );

			// slide along the plane
			G_PredictPlayerClipVelocity( endVelocity, planes[i], endClipVelocity );

			// see if there is a second plane that the new move enters
			for ( j = 0; j < numplanes; j++ ) {
				if ( j == i ) {
					continue;
				}

				if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) {
					continue;		// move doesn't interact with the plane
				}

				// try clipping the move to the plane
				G_PredictPlayerClipVelocity( clipVelocity, planes[j], clipVelocity );
				G_PredictPlayerClipVelocity( endClipVelocity, planes[j], endClipVelocity );

				// see if it goes back into the first clip plane
				if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) {
					continue;
				}

				// slide the original velocity along the crease
				CrossProduct( planes[i], planes[j], dir );
				VectorNormalize( dir );
				d = DotProduct( dir, velocity );
				VectorScale( dir, d, clipVelocity );

				CrossProduct( planes[i], planes[j], dir );
				VectorNormalize( dir );
				d = DotProduct( dir, endVelocity );
				VectorScale( dir, d, endClipVelocity );

				// see if there is a third plane the the new move enters
				for ( k = 0; k < numplanes; k++ ) {
					if ( k == i || k == j ) {
						continue;
					}

					if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) {
						continue;		// move doesn't interact with the plane
					}

					// stop dead at a tripple plane interaction
					VectorClear( velocity );
					VectorCopy( origin, ent->s.pos.trBase );
					return qtrue;
				}
			}

			// if we have fixed all interactions, try another move
			VectorCopy( clipVelocity, velocity );
			VectorCopy( endClipVelocity, endVelocity );
			break;
		}
	}

	VectorCopy( endVelocity, velocity );
	VectorCopy( origin, ent->s.pos.trBase );

	return (bumpcount != 0);
}
/*
==============
AICast_VisibleFromPos
==============
*/
qboolean AICast_VisibleFromPos( vec3_t srcpos, int srcnum,
								vec3_t destpos, int destnum, qboolean updateVisPos ) {
	int i, contents_mask, passent, hitent;
	trace_t trace;
	vec3_t start, end, middle, eye;
	cast_state_t        *cs = NULL;
	int srcviewheight;
	vec3_t destmins, destmaxs;
	vec3_t right, vec;
	qboolean inPVS;

	if ( g_entities[destnum].flags & FL_NOTARGET ) {
		return qfalse;
	}

	if ( srcnum < aicast_maxclients ) {
		cs = AICast_GetCastState( srcnum );
	}
	//
	if ( cs && cs->bs ) {
		srcviewheight = cs->bs->cur_ps.viewheight;
	} else if ( g_entities[srcnum].client ) {
		srcviewheight = g_entities[srcnum].client->ps.viewheight;
	} else {
		srcviewheight = 0;
	}
	//
	VectorCopy( g_entities[destnum].r.mins, destmins );
	VectorCopy( g_entities[destnum].r.maxs, destmaxs );
	//
	//calculate middle of bounding box
	VectorAdd( destmins, destmaxs, middle );
	VectorScale( middle, 0.5, middle );
	VectorAdd( destpos, middle, middle );
	// calculate eye position
	VectorCopy( srcpos, eye );
	eye[2] += srcviewheight;
	//
	// set the right vector
	VectorSubtract( middle, eye, vec );
	VectorNormalize( vec );
	right[0] = vec[1];
	right[1] = vec[0];
	right[2] = 0;
	//
	inPVS = qfalse;
	//
	for ( i = 0; i < 5; i++ )
	{
		if ( cs && updateVisPos ) {   // if it's a grenade or something, PVS checks don't work very well
			//if the point is not in potential visible sight
			if ( i < 3 ) {    // don't do PVS check for left/right checks
				if ( !trap_InPVS( eye, middle ) ) {
					continue;
				} else {
					inPVS = qtrue;
				}
			} else if ( !inPVS ) {
				break;      // wasn't in potential view in either of the previous tests
			}               // so don't bother doing left/right
		}
		//
		contents_mask = MASK_SHOT & ~CONTENTS_BODY; // we can see anything that a bullet can pass through
		passent = srcnum;
		hitent = destnum;
		VectorCopy( eye, start );
		VectorCopy( middle, end );
		//if the entity is in water, lava or slime
		if ( trap_PointContents( middle, destnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			contents_mask |= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
		} //end if
		  //if eye is in water, lava or slime
		if ( trap_PointContents( eye, srcnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			if ( !( contents_mask & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
				passent = destnum;
				hitent = srcnum;
				VectorCopy( middle, start );
				VectorCopy( eye, end );
			} //end if
			contents_mask ^= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
		} //end if
		  //trace from start to end
		trap_Trace( &trace, start, NULL, NULL, end, ENTITYNUM_NONE /*passent*/, contents_mask );
		//if water was hit
		if ( trace.contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			//if the water surface is translucent
//			if (trace.surface.flags & (SURF_TRANS33|SURF_TRANS66))
			{
				//trace through the water
				contents_mask &= ~( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
				trap_Trace( &trace, trace.endpos, NULL, NULL, end, passent, contents_mask );
			} //end if
		} //end if
		  //if a full trace or the hitent was hit
		if ( trace.fraction >= 1 || trace.entityNum == hitent ) {
			return qtrue;
		}
		//check bottom and top of bounding box as well
		if ( i == 0 ) {
			middle[2] -= ( destmaxs[2] - destmins[2] ) * 0.5;
		} else if ( i == 1 ) {
			middle[2] += destmaxs[2] - destmins[2];
		} else if ( i == 2 )                                                          { // right side
			middle[2] -= ( destmaxs[2] - destmins[2] ) / 2.0;
			VectorMA( eye, destmaxs[0] - 0.5, right, eye );
		} else if ( i == 3 ) {    // left side
			VectorMA( eye, -2.0 * ( destmaxs[0] - 0.5 ), right, eye );
		}
	} //end for

	return qfalse;
}
/*
-------------------------
NPC_BSDroid_Pain
-------------------------
*/
void NPC_Probe_Pain(gentity_t *self, gentity_t *attacker, int damage)
{
	float	pain_chance;
	gentity_t *other = attacker;
	int mod = gPainMOD;
	
	VectorCopy( self->NPC->lastPathAngles, self->s.angles );

	if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) // demp2 always messes them up real good
	{
		vec3_t endPos;
		trace_t	trace;

		VectorSet( endPos, self->r.currentOrigin[0], self->r.currentOrigin[1], self->r.currentOrigin[2] - 128 );
		trap_Trace( &trace, self->r.currentOrigin, NULL, NULL, endPos, self->s.number, MASK_SOLID );

		if ( trace.fraction == 1.0f || mod == MOD_DEMP2 ) // demp2 always does this
		{
			/*
			if (self->client->clientInfo.headModel != 0)
			{
				vec3_t origin;

				VectorCopy(self->r.currentOrigin,origin);
				origin[2] +=50;
//				G_PlayEffect( "small_chunks", origin );
				G_PlayEffect( "chunks/probehead", origin );
				G_PlayEffect( "env/med_explode2", origin );
				self->client->clientInfo.headModel = 0;
				self->client->moveType = MT_RUNJUMP;
				self->client->ps.gravity = g_gravity->value*.1;
			}
			*/
			
			if ( (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT) && other )
			{
				vec3_t dir;

				NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);

				VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, dir );
				VectorNormalize( dir );

				VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
				self->client->ps.velocity[2] -= 127;
			}

			//self->s.powerups |= ( 1 << PW_SHOCKED );
			//self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
			self->client->ps.electrifyTime = level.time + 3000;

			self->NPC->localState = LSTATE_DROP;
		} 
	}
	else
	{
		pain_chance = NPC_GetPainChance( self, damage );

		if ( random() < pain_chance )	// Spin around in pain?
		{
			NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE);
		}	
	}

	NPC_Pain( self, attacker, damage );
}
Beispiel #13
0
void weapon_railgun_fire (gentity_t *ent) {
    vec3_t		end;
    trace_t		trace;
    gentity_t	*tent;
    gentity_t	*traceEnt;
    int			damage;
    int			i;
    int			hits;
    int			unlinked;
    int			passent;
    gentity_t	*unlinkedEntities[MAX_RAIL_HITS];

    damage = 100 * s_quadFactor;

    VectorMA (muzzle, 8192, forward, end);

    // trace only against the solids, so the railgun will go through people
    unlinked = 0;
    hits = 0;
    passent = ent->s.number;
    do {
        trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT );
        if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) {
            break;
        }
        traceEnt = &g_entities[ trace.entityNum ];
        if ( traceEnt->takedamage ) {
            if( LogAccuracyHit( traceEnt, ent ) ) {
                hits++;
            }
            G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
        }
        if ( trace.contents & CONTENTS_SOLID ) {
            break;		// we hit something solid enough to stop the beam
        }
        // unlink this entity, so the next trace will go past it
        trap_UnlinkEntity( traceEnt );
        unlinkedEntities[unlinked] = traceEnt;
        unlinked++;
    } while ( unlinked < MAX_RAIL_HITS );

    // link back in any entities we unlinked
    for ( i = 0 ; i < unlinked ; i++ ) {
        trap_LinkEntity( unlinkedEntities[i] );
    }

    // the final trace endpos will be the terminal point of the rail trail

    // snap the endpos to integers to save net bandwidth, but nudged towards the line
    SnapVectorTowards( trace.endpos, muzzle );

    // send railgun beam effect
    tent = G_TempEntity( trace.endpos, EV_RAILTRAIL );

    // set player number for custom colors on the railtrail
    tent->s.clientNum = ent->s.clientNum;

    VectorCopy( muzzle, tent->s.origin2 );
    // move origin a bit to come closer to the drawn gun muzzle
    VectorMA( tent->s.origin2, 4, right, tent->s.origin2 );
    VectorMA( tent->s.origin2, -1, up, tent->s.origin2 );

    // no explosion at end if SURF_NOIMPACT, but still make the trail
    if ( trace.surfaceFlags & SURF_NOIMPACT ) {
        tent->s.eventParm = 255;	// don't make the explosion at the end
    } else {
        tent->s.eventParm = DirToByte( trace.plane.normal );
    }
    tent->s.clientNum = ent->s.clientNum;

    // give the shooter a reward sound if they have made two railgun hits in a row
    if ( hits == 0 ) {
        // complete miss
        ent->client->accurateCount = 0;
    } else {
        // check for "impressive" reward sound
        ent->client->accurateCount += hits;
        ent->client->accuracy_hits++;
    }

}
Beispiel #14
0
void NPC_BSGM_Attack( void )
{
	//Don't do anything if we're hurt
	if ( NPC->painDebounceTime > level.time )
	{
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

#if 0
	//FIXME: if killed enemy, use victory anim
	if ( NPC->enemy && NPC->enemy->health <= 0 
		&& !NPC->enemy->s.number )
	{//my enemy is dead
		if ( NPC->client->ps.torsoAnim == BOTH_STAND2TO1 )
		{
			if ( NPC->client->ps.torsoTimer <= 500 )
			{
				G_AddVoiceEvent( NPC, Q_irand( EV_VICTORY1, EV_VICTORY3 ), 3000 );
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsTimer += 500;
				NPC->client->ps.torsoTimer += 500;
			}
		}
		else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START )
		{
			if ( NPC->client->ps.torsoTimer <= 500 )
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsTimer += 500;
				NPC->client->ps.torsoTimer += 500;
			}
		}
		else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE )
		{
			if ( NPC->client->ps.torsoTimer <= 500 )
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsTimer += 500;
				NPC->client->ps.torsoTimer += 500;
			}
		}
		else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP )
		{
			if ( NPC->client->ps.torsoTimer <= 500 )
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsTimer = -1;
				NPC->client->ps.torsoTimer = -1;
			}
		}
		else if ( NPC->wait )
		{
			if ( TIMER_Done( NPC, "gloatTime" ) )
			{
				GM_StartGloat();
			}
			else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->r.currentOrigin ) > 4096 && (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//64 squared
			{
				NPCInfo->goalEntity = NPC->enemy;
				GM_Move();
			}
			else
			{//got there
				GM_StartGloat();
			}
		}
		NPC_FaceEnemy( qtrue );
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}
#endif

	//If we don't have an enemy, just idle
	if ( NPC_CheckEnemyExt(qfalse) == qfalse || !NPC->enemy )
	{
		NPC->enemy = NULL;
		NPC_BSGM_Patrol();
		return;
	}

	enemyLOS4 = enemyCS4 = qfalse;
	move4 = qtrue;
	faceEnemy4 = qfalse;
	shoot4 = qfalse;
	hitAlly4 = qfalse;
	VectorClear( impactPos4 );
	enemyDist4 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );

	//if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 ||
	//	NPC->client->ps.torsoAnim == BOTH_ATTACK5 )
	if (0)
	{
		shoot4 = qfalse;
		if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime )
		{//time to smack
			//recheck enemyDist4 and InFront
			if ( enemyDist4 < MELEE_DIST_SQUARED && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) )
			{
				vec3_t	smackDir;
				VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
				smackDir[2] += 30;
				VectorNormalize( smackDir );
				//hurt them
				G_Sound( NPC->enemy, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) );
				G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); 
				if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 )
				{//smackdown
					int knockAnim = BOTH_KNOCKDOWN1;
					if ( BG_CrouchAnim( NPC->enemy->client->ps.legsAnim ) )
					{//knockdown from crouch
						knockAnim = BOTH_KNOCKDOWN4;
					}
					//throw them
					smackDir[2] = 1;
					VectorNormalize( smackDir );
					G_Throw( NPC->enemy, smackDir, 50 );
					NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				}
				else
				{//uppercut
					//throw them
					G_Throw( NPC->enemy, smackDir, 100 );
					//make them backflip
					NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				}
				//done with the damage
				NPCInfo->blockedDebounceTime = 1;
			}
		}
	}
	else if ( NPC->lockCount ) //already shooting laser
	{//sometimes use the laser beam attack, but only after he's taken down our generator
		shoot4 = qfalse;
		if ( NPC->lockCount == 1 )
		{//charging up
			if ( TIMER_Done( NPC, "beamDelay" ) )
			{//time to start the beam
				int laserAnim;
				//if ( Q_irand( 0, 1 ) )
				if (1)
				{
					laserAnim = BOTH_ATTACK2;
				}
				/*
				else
				{
					laserAnim = BOTH_ATTACK7;
				}
				*/
				NPC_SetAnim( NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
				//turn on beam effect
				NPC->lockCount = 2;
				G_PlayEffectID( G_EffectIndex("galak/trace_beam"), NPC->r.currentOrigin, vec3_origin );
				NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
				if ( !NPCInfo->coverTarg )
				{//for moving looping sound at end of trace
					NPCInfo->coverTarg = G_Spawn();
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
						NPCInfo->coverTarg->r.svFlags |= SVF_BROADCAST;
						NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
					}
				}
			}
		}
		else
		{//in the actual attack now
			if ( NPC->client->ps.torsoTimer <= 0 )
			{//attack done!
				NPC->lockCount = 0;
				G_FreeEntity( NPCInfo->coverTarg );
				NPC->s.loopSound = 0;
#if 0
				NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
#endif
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
			}
			else
			{//attack still going
				//do the trace and damage
				trace_t	trace;
				vec3_t	end, mins={-3,-3,-3}, maxs={3,3,3};
				VectorMA( NPC->client->renderInfo.muzzlePoint, 1024, NPC->client->renderInfo.muzzleDir, end );
				trap_Trace( &trace, NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPC->s.number, MASK_SHOT );
				if ( trace.allsolid || trace.startsolid )
				{//oops, in a wall
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
					}
				}
				else
				{//clear
					if ( trace.fraction < 1.0f )
					{//hit something
						gentity_t *traceEnt = &g_entities[trace.entityNum];
						if ( traceEnt && traceEnt->takedamage )
						{//damage it
							G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
							G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_UNKNOWN );
						}
					}
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, trace.endpos );
					}
					if ( !Q_irand( 0, 5 ) )
					{
						G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
					}
				}
			}
		}
	}
	else 
	{//Okay, we're not in a special attack, see if we should switch weapons or start a special attack
		/*
		if ( NPC->s.weapon == WP_REPEATER 
			&& !(NPCInfo->scriptFlags & SCF_ALT_FIRE)//using rapid-fire
			&& NPC->enemy->s.weapon == WP_SABER //enemy using saber
			&& NPC->client && (NPC->client->ps.saberEventFlags&SEF_DEFLECTED)
			&& !Q_irand( 0, 50 ) )
		{//he's deflecting my shots, switch to the laser or the lob fire for a while
			TIMER_Set( NPC, "noRapid", Q_irand( 2000, 6000 ) );
			NPCInfo->scriptFlags |= SCF_ALT_FIRE;
			NPC->alt_fire = qtrue;
			if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && (Q_irand( 0, 1 )||enemyDist4 < MAX_LOB_DIST_SQUARED) )
			{//shield down, use laser
				NPC_GM_StartLaser();
			}
		}
		else*/
		if (// !NPC->client->ps.powerups[PW_GALAK_SHIELD] 
			1 //rwwFIXMEFIXME: just act like the shield is down til the effects and stuff are done
			&& enemyDist4 < MELEE_DIST_SQUARED 
			&& InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) 
			&& NPC->enemy->localAnimIndex <= 1 )//within 80 and in front
		{//our shield is down, and enemy within 80, if very close, use melee attack to slap away
			if ( TIMER_Done( NPC, "attackDelay" ) )
			{
				//animate me
				int swingAnim = BOTH_ATTACK1;
#if 0
				if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH )
				{//generator down, use random melee
					swingAnim = Q_irand( BOTH_ATTACK4, BOTH_ATTACK5 );//smackdown or uppercut
				}
				else
				{//always knock-away
					swingAnim = BOTH_ATTACK5;//uppercut
				}
#endif
				//FIXME: swing sound
				NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
				//delay the hurt until the proper point in the anim
				TIMER_Set( NPC, "smackTime", 600 );
				NPCInfo->blockedDebounceTime = 0;
				//FIXME: say something?
			}
		}
		else if ( !NPC->lockCount && NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH
			&& TIMER_Done( NPC, "attackDelay" )
			&& InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f )
			&& ((!Q_irand( 0, 10*(2-g_spskill.integer))&& enemyDist4 > MIN_LOB_DIST_SQUARED&& enemyDist4 < MAX_LOB_DIST_SQUARED)
				||(!TIMER_Done( NPC, "noLob" )&&!TIMER_Done( NPC, "noRapid" ))) 
			&& NPC->enemy->s.weapon != WP_TURRET )
		{//sometimes use the laser beam attack, but only after he's taken down our generator
			shoot4 = qfalse;
			NPC_GM_StartLaser();
		}
		else if ( enemyDist4 < MIN_LOB_DIST_SQUARED 
			&& (NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPC->enemy->classname ))
			&& TIMER_Done( NPC, "noRapid" ) )//256
		{//enemy within 256
			if ( (NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
			{//shooting an explosive, but enemy too close, switch to primary fire
				NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
				NPC->alt_fire = qfalse;
				//FIXME: use weap raise & lower anims
				NPC_ChangeWeapon( WP_REPEATER );
			}
		}
		else if ( (enemyDist4 > MAX_LOB_DIST_SQUARED || (NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname )))
			&& TIMER_Done( NPC, "noLob" ) )//448
		{//enemy more than 448 away and we are ready to try lob fire again
			if ( (NPC->client->ps.weapon == WP_REPEATER) && !(NPCInfo->scriptFlags & SCF_ALT_FIRE) )
			{//enemy far enough away to use lobby explosives
				NPCInfo->scriptFlags |= SCF_ALT_FIRE;
				NPC->alt_fire = qtrue;
				//FIXME: use weap raise & lower anims
				NPC_ChangeWeapon( WP_REPEATER );
			}
		}
	}

	//can we see our target?
	if ( NPC_ClearLOS4( NPC->enemy ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS
		enemyLOS4 = qtrue;

		if ( NPC->client->ps.weapon == WP_NONE )
		{
			enemyCS4 = qfalse;//not true, but should stop us from firing
			NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon
		}
		else
		{//can we shoot our target?
			if ( ((NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist4 < MIN_LOB_DIST_SQUARED )//256
			{
				enemyCS4 = qfalse;//not true, but should stop us from firing
				hitAlly4 = qtrue;//us!
				//FIXME: if too close, run away!
			}
			else
			{
				int hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
				gentity_t *hitEnt = &g_entities[hit];
				if ( hit == NPC->enemy->s.number 
					|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
					|| ( hitEnt && hitEnt->takedamage ) )
				{//can hit enemy or will hit glass or other breakable, so shoot anyway
					enemyCS4 = qtrue;
					NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
					VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
				}
				else
				{//Hmm, have to get around this bastard
					NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
					if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam )
					{//would hit an ally, don't fire!!!
						hitAlly4 = qtrue;
					}
					else
					{//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire
					}
				}
			}
		}
	}
	else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
	{
		int hit;
		gentity_t *hitEnt;

		if ( TIMER_Done( NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) )
		{
			if ( NPCInfo->enemyCheckDebounceTime < 8 )
			{
				int speech = -1;
				switch( NPCInfo->enemyCheckDebounceTime )
				{
				case 0:
				case 1:
				case 2:
					speech = EV_CHASE1 + NPCInfo->enemyCheckDebounceTime;
					break;
				case 3:
				case 4:
				case 5:
					speech = EV_COVER1 + NPCInfo->enemyCheckDebounceTime-3;
					break;
				case 6:
				case 7:
					speech = EV_ESCAPING1 + NPCInfo->enemyCheckDebounceTime-6;
					break;
				}
				NPCInfo->enemyCheckDebounceTime++;
				if ( speech != -1 )
				{
					G_AddVoiceEvent( NPC, speech, Q_irand( 3000, 5000 ) );
					TIMER_Set( NPC, "talkDebounce", Q_irand( 5000, 7000 ) );
				}
			}
		}

		NPCInfo->enemyLastSeenTime = level.time;

		hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
		hitEnt = &g_entities[hit];
		if ( hit == NPC->enemy->s.number 
			|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
			|| ( hitEnt && hitEnt->takedamage ) )
		{//can hit enemy or will hit glass or other breakable, so shoot anyway
			enemyCS4 = qtrue;
		}
		else
		{
			faceEnemy4 = qtrue;
			NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
		}
	}

	if ( enemyLOS4 )
	{
		faceEnemy4 = qtrue;
	}
	else
	{
		if ( !NPCInfo->goalEntity )
		{
			NPCInfo->goalEntity = NPC->enemy;
		}
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//for now, always chase the enemy
			move4 = qtrue;
		}
	}
	if ( enemyCS4 )
	{
		shoot4 = qtrue;
		//NPCInfo->enemyCheckDebounceTime = level.time;//actually used here as a last actual LOS
	}
	else
	{
		if ( !NPCInfo->goalEntity )
		{
			NPCInfo->goalEntity = NPC->enemy;
		}
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//for now, always chase the enemy
			move4 = qtrue;
		}
	}

	//Check for movement to take care of
	GM_CheckMoveState();

	//See if we should override shooting decision with any special considerations
	GM_CheckFireState();

	if ( NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE) && shoot4 && TIMER_Done( NPC, "attackDelay" ) )
	{
		vec3_t	muzzle;
		vec3_t	angles;
		vec3_t	target;
		vec3_t velocity = {0,0,0};
		vec3_t mins = {-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE}, maxs = {REPEATER_ALT_SIZE,REPEATER_ALT_SIZE,REPEATER_ALT_SIZE};
		qboolean clearshot;

		CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
		
		VectorCopy( NPC->enemy->r.currentOrigin, target );

		target[0] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
		target[1] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
		target[2] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);

		//Find the desired angles
		clearshot = WP_LobFire( NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, 
			velocity, qtrue, NPC->s.number, NPC->enemy->s.number,
			300, 1100, 1500, qtrue );
		if ( VectorCompare( vec3_origin, velocity ) || (!clearshot&&enemyLOS4&&enemyCS4)  )
		{//no clear lob shot and no lob shot that will hit something breakable
			if ( enemyLOS4 && enemyCS4 && TIMER_Done( NPC, "noRapid" ) )
			{//have a clear straight shot, so switch to primary
				NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
				NPC->alt_fire = qfalse;
				NPC_ChangeWeapon( WP_REPEATER );
				//keep this weap for a bit
				TIMER_Set( NPC, "noLob", Q_irand( 500, 1000 ) );
			}
			else
			{
				shoot4 = qfalse;
			}
		}
		else
		{
			vectoangles( velocity, angles );

			NPCInfo->desiredYaw		= AngleNormalize360( angles[YAW] );
			NPCInfo->desiredPitch	= AngleNormalize360( angles[PITCH] );

			VectorCopy( velocity, NPC->client->hiddenDir );
			NPC->client->hiddenDist = VectorNormalize ( NPC->client->hiddenDir );
		}
	}
	else if ( faceEnemy4 )
	{//face the enemy
		NPC_FaceEnemy( qtrue );
	}

	if ( !TIMER_Done( NPC, "standTime" ) )
	{
		move4 = qfalse;
	}
	if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
	{//not supposed to chase my enemies
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//goal is my entity, so don't move
			move4 = qfalse;
		}
	}

	if ( move4 && !NPC->lockCount )
	{//move toward goal
		if ( NPCInfo->goalEntity 
			/*&& NPC->client->ps.legsAnim != BOTH_ALERT1
			&& NPC->client->ps.legsAnim != BOTH_ATTACK2 
			&& NPC->client->ps.legsAnim != BOTH_ATTACK4
			&& NPC->client->ps.legsAnim != BOTH_ATTACK5 
			&& NPC->client->ps.legsAnim != BOTH_ATTACK7*/ )
		{
			move4 = GM_Move();
		}
		else
		{
			move4 = qfalse;
		}
	}

	if ( !TIMER_Done( NPC, "flee" ) )
	{//running away
		faceEnemy4 = qfalse;
	}

	//FIXME: check scf_face_move_dir here?

	if ( !faceEnemy4 )
	{//we want to face in the dir we're running
		if ( !move4 )
		{//if we haven't moved, we should look in the direction we last looked?
			VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles );
		}
		if ( move4 )
		{//don't run away and shoot
			NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
			NPCInfo->desiredPitch = 0;
			shoot4 = qfalse;
		}
	}
	NPC_UpdateAngles( qtrue, qtrue );

	if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
	{
		shoot4 = qfalse;
	}

	if ( NPC->enemy && NPC->enemy->enemy )
	{
		if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER )
		{//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH)
			shoot4 = qfalse;
		}
	}
	//FIXME: don't shoot right away!
	if ( shoot4 )
	{//try to shoot if it's time
		if ( TIMER_Done( NPC, "attackDelay" ) )
		{
			if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
			{
				WeaponThink( qtrue );
			}
		}
	}

	//also:
	if ( NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ) )
	{//crush turrets
		if ( G_BoundsOverlap( NPC->r.absmin, NPC->r.absmax, NPC->enemy->r.absmin, NPC->enemy->r.absmax ) )
		{//have to do this test because placed turrets are not solid to NPCs (so they don't obstruct navigation)
			//if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 )
			if (0)
			{
				NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
				G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); 
			}
			else
			{
				G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); 
			}
		}
	}
	else if ( NPCInfo->touchedByPlayer != NULL && NPCInfo->touchedByPlayer == NPC->enemy )
	{//touched enemy
		//if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 )
		if (0)
		{//zap him!
			vec3_t	smackDir;

			//animate me
#if 0
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK6, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
#endif
			TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
			TIMER_Set( NPC, "standTime", NPC->client->ps.legsTimer );
			//FIXME: debounce this?
			NPCInfo->touchedByPlayer = NULL;
			//FIXME: some shield effect?
			NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;

			VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
			smackDir[2] += 30;
			VectorNormalize( smackDir );
			G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); 
			//throw them
			G_Throw( NPC->enemy, smackDir, 100 );
			//NPC->enemy->s.powerups |= ( 1 << PW_SHOCKED );
			if ( NPC->enemy->client )
			{
			//	NPC->enemy->client->ps.powerups[PW_SHOCKED] = level.time + 1000;
				NPC->enemy->client->ps.electrifyTime = level.time + 1000;
			}
			//stop any attacks
			ucmd.buttons = 0;
		}
	}

	if ( NPCInfo->movementSpeech < 3 && NPCInfo->blockedSpeechDebounceTime <= level.time )
	{
		if ( NPC->enemy && NPC->enemy->health > 0 && NPC->enemy->painDebounceTime > level.time )
		{
			if ( NPC->enemy->health < 50 && NPCInfo->movementSpeech == 2 )
			{
				G_AddVoiceEvent( NPC, EV_ANGER2, Q_irand( 2000, 4000 ) );
				NPCInfo->movementSpeech = 3;
			}
			else if ( NPC->enemy->health < 75 && NPCInfo->movementSpeech == 1 )
			{
				G_AddVoiceEvent( NPC, EV_ANGER1, Q_irand( 2000, 4000 ) );
				NPCInfo->movementSpeech = 2;
			}
			else if ( NPC->enemy->health < 100 && NPCInfo->movementSpeech == 0 )
			{
				G_AddVoiceEvent( NPC, EV_ANGER3, Q_irand( 2000, 4000 ) );
				NPCInfo->movementSpeech = 1;
			}
		}
	}
}
int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
	int			quantity;
	int			i;
	gclient_t	*client;

	if ( !other->client->ps.powerups[ent->item->giTag] ) {
		// round timing to seconds to make multiple powerup timers
		// count in sync
		other->client->ps.powerups[ent->item->giTag] = 
			level.time - ( level.time % 1000 );
	}

	if ( ent->count ) {
		quantity = ent->count;
	} else {
		quantity = ent->item->quantity;
	}

	other->client->ps.powerups[ent->item->giTag] += quantity * 1000;

	// give any nearby players a "denied" anti-reward
	for ( i = 0 ; i < level.maxclients ; i++ ) {
		vec3_t		delta;
		float		len;
		vec3_t		forward;
		trace_t		tr;

		client = &level.clients[i];
		if ( client == other->client ) {
			continue;
		}
		if ( client->pers.connected == CON_DISCONNECTED ) {
			continue;
		}
		if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
			continue;
		}

    // if same team in team game, no sound
    // cannot use OnSameTeam as it expects to g_entities, not clients
  	if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam  ) {
      continue;
    }

		// if too far away, no sound
		VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
		len = VectorNormalize( delta );
		if ( len > 192 ) {
			continue;
		}

		// if not facing, no sound
		AngleVectors( client->ps.viewangles, forward, NULL, NULL );
		if ( DotProduct( delta, forward ) < 0.4 ) {
			continue;
		}

		// if not line of sight, no sound
		trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
		if ( tr.fraction != 1.0 ) {
			continue;
		}

		// anti-reward
		client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD;
	}

	// FIXME: Where's the best place for this?
	if ( ent->item->giTag == PW_BERSERKER ) {
		trap_SendServerCommand( ( ent - g_entities ), va( "srwc %i", WP_PUNCHY ) );
		other->client->pers.cmd.weapon = WP_PUNCHY;
		other->client->ps.weapon = WP_PUNCHY;
	}

	return RESPAWN_POWERUP;
}
Beispiel #16
0
	/**
	 * @brief Move a point towards empty space (away from map geometry).
	 * @param normal Optional direction to move towards.
	 */
	void MoveTowardsRoom( vec3_t origin )
	{
		static const vec3_t vecs[ 162 ] =
		{
			{0.000000, 0.000000, -1.000000}, {0.723607, -0.525725, -0.447220}, {-0.276388, -0.850649, -0.447220},
			{-0.894426, 0.000000, -0.447216}, {-0.276388, 0.850649, -0.447220}, {0.723607, 0.525725, -0.447220},
			{0.276388, -0.850649, 0.447220}, {-0.723607, -0.525725, 0.447220}, {-0.723607, 0.525725, 0.447220},
			{0.276388, 0.850649, 0.447220}, {0.894426, 0.000000, 0.447216}, {0.000000, 0.000000, 1.000000},
			{-0.232822, -0.716563, -0.657519}, {-0.162456, -0.499995, -0.850654}, {-0.077607, -0.238853, -0.967950},
			{0.203181, -0.147618, -0.967950}, {0.425323, -0.309011, -0.850654}, {0.609547, -0.442856, -0.657519},
			{0.531941, -0.681712, -0.502302}, {0.262869, -0.809012, -0.525738}, {-0.029639, -0.864184, -0.502302},
			{0.812729, 0.295238, -0.502301}, {0.850648, 0.000000, -0.525736}, {0.812729, -0.295238, -0.502301},
			{0.203181, 0.147618, -0.967950}, {0.425323, 0.309011, -0.850654}, {0.609547, 0.442856, -0.657519},
			{-0.753442, 0.000000, -0.657515}, {-0.525730, 0.000000, -0.850652}, {-0.251147, 0.000000, -0.967949},
			{-0.483971, -0.716565, -0.502302}, {-0.688189, -0.499997, -0.525736}, {-0.831051, -0.238853, -0.502299},
			{-0.232822, 0.716563, -0.657519}, {-0.162456, 0.499995, -0.850654}, {-0.077607, 0.238853, -0.967950},
			{-0.831051, 0.238853, -0.502299}, {-0.688189, 0.499997, -0.525736}, {-0.483971, 0.716565, -0.502302},
			{-0.029639, 0.864184, -0.502302}, {0.262869, 0.809012, -0.525738}, {0.531941, 0.681712, -0.502302},
			{0.956626, -0.147618, 0.251149}, {0.951058, -0.309013, -0.000000}, {0.860698, -0.442858, -0.251151},
			{0.860698, 0.442858, -0.251151}, {0.951058, 0.309013, 0.000000}, {0.956626, 0.147618, 0.251149},
			{0.155215, -0.955422, 0.251152}, {0.000000, -1.000000, -0.000000}, {-0.155215, -0.955422, -0.251152},
			{0.687159, -0.681715, -0.251152}, {0.587786, -0.809017, 0.000000}, {0.436007, -0.864188, 0.251152},
			{-0.860698, -0.442858, 0.251151}, {-0.951058, -0.309013, -0.000000}, {-0.956626, -0.147618, -0.251149},
			{-0.436007, -0.864188, -0.251152}, {-0.587786, -0.809017, 0.000000}, {-0.687159, -0.681715, 0.251152},
			{-0.687159, 0.681715, 0.251152}, {-0.587786, 0.809017, -0.000000}, {-0.436007, 0.864188, -0.251152},
			{-0.956626, 0.147618, -0.251149}, {-0.951058, 0.309013, 0.000000}, {-0.860698, 0.442858, 0.251151},
			{0.436007, 0.864188, 0.251152}, {0.587786, 0.809017, -0.000000}, {0.687159, 0.681715, -0.251152},
			{-0.155215, 0.955422, -0.251152}, {0.000000, 1.000000, 0.000000}, {0.155215, 0.955422, 0.251152},
			{0.831051, -0.238853, 0.502299}, {0.688189, -0.499997, 0.525736}, {0.483971, -0.716565, 0.502302},
			{0.029639, -0.864184, 0.502302}, {-0.262869, -0.809012, 0.525738}, {-0.531941, -0.681712, 0.502302},
			{-0.812729, -0.295238, 0.502301}, {-0.850648, 0.000000, 0.525736}, {-0.812729, 0.295238, 0.502301},
			{-0.531941, 0.681712, 0.502302}, {-0.262869, 0.809012, 0.525738}, {0.029639, 0.864184, 0.502302},
			{0.483971, 0.716565, 0.502302}, {0.688189, 0.499997, 0.525736}, {0.831051, 0.238853, 0.502299},
			{0.077607, -0.238853, 0.967950}, {0.162456, -0.499995, 0.850654}, {0.232822, -0.716563, 0.657519},
			{0.753442, 0.000000, 0.657515}, {0.525730, 0.000000, 0.850652}, {0.251147, 0.000000, 0.967949},
			{-0.203181, -0.147618, 0.967950}, {-0.425323, -0.309011, 0.850654}, {-0.609547, -0.442856, 0.657519},
			{-0.203181, 0.147618, 0.967950}, {-0.425323, 0.309011, 0.850654}, {-0.609547, 0.442856, 0.657519},
			{0.077607, 0.238853, 0.967950}, {0.162456, 0.499995, 0.850654}, {0.232822, 0.716563, 0.657519},
			{0.052790, -0.688185, -0.723612}, {0.138199, -0.425321, -0.894429}, {0.361805, -0.587779, -0.723611},
			{0.670817, 0.162457, -0.723611}, {0.670818, -0.162458, -0.723610}, {0.447211, -0.000001, -0.894428},
			{-0.638195, -0.262864, -0.723609}, {-0.361801, -0.262864, -0.894428}, {-0.447211, -0.525729, -0.723610},
			{-0.447211, 0.525727, -0.723612}, {-0.361801, 0.262863, -0.894429}, {-0.638195, 0.262863, -0.723609},
			{0.361803, 0.587779, -0.723612}, {0.138197, 0.425321, -0.894429}, {0.052789, 0.688186, -0.723611},
			{1.000000, 0.000000, 0.000000}, {0.947213, -0.162458, -0.276396}, {0.947213, 0.162458, -0.276396},
			{0.309017, -0.951056, 0.000000}, {0.138199, -0.951055, -0.276398}, {0.447216, -0.850648, -0.276398},
			{-0.809018, -0.587783, 0.000000}, {-0.861803, -0.425324, -0.276396}, {-0.670819, -0.688191, -0.276397},
			{-0.809018, 0.587783, -0.000000}, {-0.670819, 0.688191, -0.276397}, {-0.861803, 0.425324, -0.276396},
			{0.309017, 0.951056, -0.000000}, {0.447216, 0.850648, -0.276398}, {0.138199, 0.951055, -0.276398},
			{0.670820, -0.688190, 0.276396}, {0.809019, -0.587783, -0.000002}, {0.861804, -0.425323, 0.276394},
			{-0.447216, -0.850648, 0.276397}, {-0.309017, -0.951056, -0.000001}, {-0.138199, -0.951055, 0.276397},
			{-0.947213, 0.162458, 0.276396}, {-1.000000, -0.000000, 0.000001}, {-0.947213, -0.162458, 0.276397},
			{-0.138199, 0.951055, 0.276397}, {-0.309016, 0.951057, -0.000000}, {-0.447215, 0.850649, 0.276397},
			{0.861804, 0.425322, 0.276396}, {0.809019, 0.587782, 0.000000}, {0.670821, 0.688189, 0.276397},
			{0.361800, -0.262863, 0.894429}, {0.447209, -0.525728, 0.723612}, {0.638194, -0.262864, 0.723610},
			{-0.138197, -0.425319, 0.894430}, {-0.361804, -0.587778, 0.723612}, {-0.052790, -0.688185, 0.723612},
			{-0.447210, 0.000000, 0.894429}, {-0.670817, 0.162457, 0.723611}, {-0.670817, -0.162457, 0.723611},
			{-0.138197, 0.425319, 0.894430}, {-0.052790, 0.688185, 0.723612}, {-0.361804, 0.587778, 0.723612},
			{0.361800, 0.262863, 0.894429}, {0.638194, 0.262864, 0.723610}, {0.447209, 0.525728, 0.723612}
		};
		const int numvecs = sizeof( vecs ) / sizeof( vecs[ 0 ] );
		int i;
		vec3_t accumulator, end;
		trace_t tr;

		VectorClear( accumulator );

		for ( i = 0; i < numvecs; i++ )
		{
			VectorMA( origin, 500, vecs[ i ], end );
			trap_Trace( &tr, origin, nullptr, nullptr, end, 0, MASK_SOLID, 0 );
			VectorAdd( accumulator, tr.endpos, accumulator );
		}

		VectorScale( accumulator, 1.0 / numvecs, accumulator );
		VectorCopy( accumulator, origin );
	}
/*
================
FinishSpawningItem

Traces down to find where an item should rest, instead of letting them
free fall from their spawn points
================
*/
void FinishSpawningItem( gentity_t *ent ) {
	trace_t		tr;
	vec3_t		dest;

	VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
	VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );

	ent->s.eType = ET_ITEM;
	ent->s.modelindex = ent->item - bg_itemlist;		// store item number in modelindex
	ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item

	ent->r.contents = CONTENTS_TRIGGER;
	ent->touch = Touch_Item;
	// useing an item causes it to respawn
	ent->use = Use_Item;

	if ( ent->spawnflags & 1 ) {
		// suspended
		G_SetOrigin( ent, ent->s.origin );
	} else {
		// drop to floor
		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
		if ( tr.startsolid ) {
			G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
			G_FreeEntity( ent );
			return;
		}

		// allow to ride movers
		ent->s.groundEntityNum = tr.entityNum;

		G_SetOrigin( ent, tr.endpos );
	}

	// team slaves and targeted items aren't present at start
	if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
		ent->s.eFlags |= EF_NODRAW;
		ent->r.contents = 0;
		return;
	}

	// powerups don't spawn in for a while
	if ( ent->item->giType == IT_POWERUP ) {
		float	respawn;

		respawn = 45 + crandom() * 15;
		ent->s.eFlags |= EF_NODRAW;
		ent->r.contents = 0;
		ent->nextthink = level.time + respawn * 1000;
		ent->think = RespawnItem;
		return;
	}

	if ( ent->item->giType == IT_HOLDABLE ) {
		if ( ( ent->item->giTag == HI_BAMBAM ) && ( g_gametype.integer != GT_CTF ) ) {
			return;
		}
		else if ( ( ent->item->giTag == HI_BOOMIES ) &&
		          ( ( g_gametype.integer != GT_CTF ) && ( g_gametype.integer != GT_BALLOON ) ) ) {
			return;
		}
	}


	trap_LinkEntity (ent);
}
Beispiel #18
0
	/**
	 * @brief Perform an approximate trace to find a taggable entity.
	 * @param team           Team the caller belongs to.
	 * @param refreshTagged  Refresh all already tagged entities's tags and exclude these entities from further consideration.
	 */
	gentity_t *TagTrace( const vec3_t begin, const vec3_t end, int skip, int mask, team_t team, bool refreshTagged )
	{
		tagtrace_ent_t list[ MAX_GENTITIES ];
		int i, count = 0;
		gentity_t *ent, *reticleEnt = nullptr;
		vec3_t seg, delta;
		float dot;

		VectorSubtract( end, begin, seg );

		// Do a trace for bounding boxes under the reticle first, they are prefered
		{
			trace_t tr;
			trap_Trace( &tr, begin, nullptr, nullptr, end, skip, mask, 0 );
			if ( EntityTaggable( tr.entityNum, team, true ) )
			{
				reticleEnt = g_entities + tr.entityNum;
				if ( !refreshTagged || !CheckRefreshTag( reticleEnt, team ) )
					return reticleEnt;
			}
		}

		for( i = 0; i < level.num_entities; i++ )
		{
			ent = g_entities + i;

			if( ent == reticleEnt )
				continue;

			if( !ent->inuse )
				continue;

			if( !EntityTaggable( i, team, true ) )
				continue;

			VectorSubtract( ent->r.currentOrigin, begin, delta );
			dot = DotProduct( seg, delta ) / VectorLength( seg ) / VectorLength( delta );

			if( dot < 0.9 )
				continue;

			if( !trap_InPVS( ent->r.currentOrigin, begin ) )
				continue;

			// LOS
			{
				trace_t tr;
				trap_Trace( &tr, begin, nullptr, nullptr, ent->r.currentOrigin, skip, mask, 0 );
				if( tr.entityNum != i )
					continue;
			}

			if( refreshTagged && CheckRefreshTag( ent, team ) )
				continue;

			list[ count ].ent = ent;
			list[ count++ ].dot = dot;
		}

		if( !count )
			return nullptr;

		qsort( list, count, sizeof( tagtrace_ent_t ), TagTrace_EntCmp );

		return list[ 0 ].ent;
	}
Beispiel #19
0
void
weapon_railgun_fire(Gentity *ent)
{
	Vec3 end;
#ifdef MISSIONPACK
	Vec3 impactpoint, bouncedir;
#endif
	Trace trace;
	Gentity *tent;
	Gentity *traceEnt;
	int damage;
	int i;
	int hits;
	int unlinked;
	int passent;
	Gentity *unlinkedEntities[MAX_RAIL_HITS];

	damage = 100 * s_quadFactor;

	saddv3 (muzzle, 8192, forward, end);

	/* trace only against the solids, so the railgun will go through people */
	unlinked = 0;
	hits = 0;
	passent = ent->s.number;
	do {
		trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT);
		if(trace.entityNum >= ENTITYNUM_MAX_NORMAL)
			break;
		traceEnt = &g_entities[ trace.entityNum ];
		if(traceEnt->takedamage){
			if(LogAccuracyHit(traceEnt, ent))
				hits++;
			G_Damage (traceEnt, ent, ent, forward, trace.endpos,
				damage, 0,
				MOD_RAILGUN);
		}
		if(trace.contents & CONTENTS_SOLID)
			break;	/* we hit something solid enough to stop the beam */
		/* unlink this entity, so the next trace will go past it */
		trap_UnlinkEntity(traceEnt);
		unlinkedEntities[unlinked] = traceEnt;
		unlinked++;
	} while(unlinked < MAX_RAIL_HITS);

	/* link back in any entities we unlinked */
	for(i = 0; i < unlinked; i++)
		trap_LinkEntity(unlinkedEntities[i]);

	/* the final trace endpos will be the terminal point of the rail trail */

	/* snap the endpos to integers to save net bandwidth, but nudged towards the line */
	snapv3Towards(trace.endpos, muzzle);

	/* send railgun beam effect */
	tent = G_TempEntity(trace.endpos, EV_RAILTRAIL);

	/* set player number for custom colors on the railtrail */
	tent->s.clientNum = ent->s.clientNum;

	copyv3(muzzle, tent->s.origin2);
	/* move origin a bit to come closer to the drawn gun muzzle */
	saddv3(tent->s.origin2, 4, right, tent->s.origin2);
	saddv3(tent->s.origin2, -1, up, tent->s.origin2);

	/* no explosion at end if SURF_NOIMPACT, but still make the trail */
	if(trace.surfaceFlags & SURF_NOIMPACT)
		tent->s.eventParm = 255;	/* don't make the explosion at the end */
	else
		tent->s.eventParm = DirToByte(trace.plane.normal);
	tent->s.clientNum = ent->s.clientNum;

	/* give the shooter a reward sound if they have made two railgun hits in a row */
	if(hits == 0)
		/* complete miss */
		ent->client->accurateCount = 0;
	else{
		/* check for "impressive" reward sound */
		ent->client->accurateCount += hits;
		if(ent->client->accurateCount >= 2){
			ent->client->accurateCount -= 2;
			ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
			/* add the sprite over the player's head */
			ent->client->ps.eFlags &=
				~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT |
				  EF_AWARD_GAUNTLET | EF_AWARD_ASSIST |
				  EF_AWARD_DEFEND |
				  EF_AWARD_CAP);
			ent->client->ps.eFlags	|= EF_AWARD_IMPRESSIVE;
			ent->client->rewardTime = level.time +
						  REWARD_SPRITE_TIME;
		}
		ent->client->accuracy_hits++;
	}

}
Beispiel #20
0
qboolean IsLegShot( gentity_t *targ, vec3_t dir, vec3_t point, int mod ) {
	float height;
	float theight;
	gentity_t *leg;

	if (!(targ->client))
		return qfalse;

	if (targ->health <= 0)
		return qfalse;

	if(!point) {
		return qfalse;
	}

	if(!IsHeadShotWeapon(mod)) {
		return qfalse;
	}

	leg = G_BuildLeg( targ );

	if( leg ) {
		gentity_t	*traceEnt;
		vec3_t		start, end;
		trace_t		tr;

		// trace another shot see if we hit the legs
		VectorCopy( point, start );
		VectorMA( start, 64, dir, end );
		trap_Trace( &tr, start, NULL, NULL, end, targ->s.number, MASK_SHOT );
			
		traceEnt = &g_entities[ tr.entityNum ];

		if( g_debugBullets.integer >= 3 ) {	// show hit player head bb
			gentity_t *tent;
			vec3_t b1, b2;
			VectorCopy( leg->r.currentOrigin, b1 );
			VectorCopy( leg->r.currentOrigin, b2 );
			VectorAdd( b1, leg->r.mins, b1 );
			VectorAdd( b2, leg->r.maxs, b2 );
			tent = G_TempEntity( b1, EV_RAILTRAIL );
			VectorCopy( b2, tent->s.origin2 );
			tent->s.dmgFlags = 1;

			// show headshot trace
			// end the headshot trace at the head box if it hits
			if( tr.fraction != 1 ) {
				VectorMA( start, (tr.fraction * 64), dir, end );
			}
			tent = G_TempEntity( start, EV_RAILTRAIL );
			VectorCopy( end, tent->s.origin2 );
			tent->s.dmgFlags = 0;
		}

		G_FreeEntity( leg );

		if( traceEnt == leg ) {
			return qtrue;
		}
	} else {
		height = point[2] - targ->r.absmin[2];
		theight = targ->r.absmax[2] - targ->r.absmin[2];

		if(height < (theight * 0.4f)) {
			return qtrue;
		}
	}

	return qfalse;
}
Beispiel #21
0
/**
* @brief Drop weapon.
*/
void G_DropWeapon(gentity_t *ent, weapon_t weapon)
{
	vec3_t    angles, velocity, org, offset, mins, maxs;
	gclient_t *client = ent->client;
	gentity_t *ent2;
	gitem_t   *item;
	trace_t   tr;

	if (!IS_VALID_WEAPON(weapon))
	{
		return;
	}

	item = BG_FindItemForWeapon(weapon);
	VectorCopy(client->ps.viewangles, angles);

	// clamp pitch
	if (angles[PITCH] < -30)
	{
		angles[PITCH] = -30;
	}
	else if (angles[PITCH] > 30)
	{
		angles[PITCH] = 30;
	}

	AngleVectors(angles, velocity, NULL, NULL);
	VectorScale(velocity, 64, offset);
	offset[2] += client->ps.viewheight / 2.f;
	VectorScale(velocity, 75, velocity);
	velocity[2] += 50 + random() * 35;

	VectorAdd(client->ps.origin, offset, org);

	VectorSet(mins, -ITEM_RADIUS, -ITEM_RADIUS, 0);
	VectorSet(maxs, ITEM_RADIUS, ITEM_RADIUS, 2 * ITEM_RADIUS);

	trap_Trace(&tr, client->ps.origin, mins, maxs, org, ent->s.number, MASK_SOLID);
	VectorCopy(tr.endpos, org);

	ent2 = LaunchItem(item, org, velocity, client->ps.clientNum);
	COM_BitClear(client->ps.weapons, weapon);

	// FIXME: do a switch
	if (weapon == WP_KAR98)
	{
		COM_BitClear(client->ps.weapons, WP_GPG40);
	}
	else if (weapon == WP_CARBINE)
	{
		COM_BitClear(client->ps.weapons, WP_M7);
	}
	else if (weapon == WP_FG42)
	{
		COM_BitClear(client->ps.weapons, WP_FG42SCOPE);
	}
	else if (weapon == WP_K43)
	{
		COM_BitClear(client->ps.weapons, WP_K43_SCOPE);
	}
	else if (weapon == WP_GARAND)
	{
		COM_BitClear(client->ps.weapons, WP_GARAND_SCOPE);
	}
	else if (weapon == WP_MORTAR)
	{
		COM_BitClear(client->ps.weapons, WP_MORTAR_SET);
	}
	else if (weapon == WP_MORTAR2)
	{
		COM_BitClear(client->ps.weapons, WP_MORTAR2_SET);
	}
	else if (weapon == WP_MOBILE_MG42)
	{
		COM_BitClear(client->ps.weapons, WP_MOBILE_MG42_SET);
	}
	else if (weapon == WP_MOBILE_BROWNING)
	{
		COM_BitClear(client->ps.weapons, WP_MOBILE_BROWNING_SET);
	}

	// Clear out empty weapon, change to next best weapon
	G_AddEvent(ent, EV_WEAPONSWITCHED, 0);

	if (weapon == client->ps.weapon)
	{
		client->ps.weapon = 0;
	}

	if (IS_MORTAR_WEAPON_SET(weapon))
	{
		ent2->count = client->ps.ammo[BG_FindAmmoForWeapon(weapon)] + client->ps.ammoclip[BG_FindClipForWeapon(weapon)];
	}
	else
	{
		ent2->count = client->ps.ammoclip[BG_FindClipForWeapon(weapon)];
	}

	if (weapon == WP_KAR98 || weapon == WP_CARBINE)
	{
		ent2->delay = client->ps.ammo[BG_FindAmmoForWeapon(weaponTable[weapon].weapAlts)];
	}
	else
	{
		ent2->delay = 0;
	}

	//  ent2->item->quantity = client->ps.ammoclip[BG_FindClipForWeapon(weapon)]; // um, modifying an item is not a good idea
	client->ps.ammoclip[BG_FindClipForWeapon(weapon)] = 0;

#ifdef FEATURE_OMNIBOT
	Bot_Event_RemoveWeapon(client->ps.clientNum, Bot_WeaponGameToBot(weapon));
#endif
}
Beispiel #22
0
/*
void CalcEntitySpot ( gentity_t *ent, spot_t spot, vec3_t point ) 

Added: Uses shootAngles if a NPC has them

*/
void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point ) 
{
	vec3_t	forward, up, right;
	vec3_t	start, end;
	trace_t	tr;

	if ( !ent )
	{
		return;
	}
	switch ( spot ) 
	{
	case SPOT_ORIGIN:
		if(VectorCompare(ent->r.currentOrigin, vec3_origin))
		{//brush
			VectorSubtract(ent->r.absmax, ent->r.absmin, point);//size
			VectorMA(ent->r.absmin, 0.5, point, point);
		}
		else
		{
			VectorCopy ( ent->r.currentOrigin, point );
		}
		break;

	case SPOT_CHEST:
	case SPOT_HEAD:
		if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD)*/ )
		{//Actual tag_head eyespot!
			//FIXME: Stasis aliens may have a problem here...
			VectorCopy( ent->client->renderInfo.eyePoint, point );
			if ( ent->client->NPC_class == CLASS_ATST )
			{//adjust up some
				point[2] += 28;//magic number :)
			}
			if ( ent->NPC )
			{//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
				point[0] = ent->r.currentOrigin[0];
				point[1] = ent->r.currentOrigin[1];
			}
			}
		else
		{
			VectorCopy ( ent->r.currentOrigin, point );
			if ( ent->client ) 
			{
				point[2] += ent->client->ps.viewheight;
			}
		}
		if ( spot == SPOT_CHEST && ent->client )
		{
			if ( ent->client->NPC_class != CLASS_ATST )
			{//adjust up some
				point[2] -= ent->r.maxs[2]*0.2f;
			}
		}
		break;

	case SPOT_HEAD_LEAN:
		if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) /*&& (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD*/ )
		{//Actual tag_head eyespot!
			//FIXME: Stasis aliens may have a problem here...
			VectorCopy( ent->client->renderInfo.eyePoint, point );
			if ( ent->client->NPC_class == CLASS_ATST )
			{//adjust up some
				point[2] += 28;//magic number :)
			}
			if ( ent->NPC )
			{//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
				point[0] = ent->r.currentOrigin[0];
				point[1] = ent->r.currentOrigin[1];
			}
			//NOTE: automatically takes leaning into account!
		}
		else
		{
			VectorCopy ( ent->r.currentOrigin, point );
			if ( ent->client ) 
			{
				point[2] += ent->client->ps.viewheight;
			}
		}
		break;

	case SPOT_LEGS:
		VectorCopy ( ent->r.currentOrigin, point );
		point[2] += (ent->r.mins[2] * 0.5);
		break;

	case SPOT_WEAPON:
		if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
		{
			AngleVectors( ent->NPC->shootAngles, forward, right, up );
		}
		else
		{
			AngleVectors( ent->client->ps.viewangles, forward, right, up );
		}
		CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point );
		//NOTE: automatically takes leaning into account!
		break;

	case SPOT_GROUND:
		// if entity is on the ground, just use it's absmin
		if ( ent->s.groundEntityNum != -1 ) 
		{
			VectorCopy( ent->r.currentOrigin, point );
			point[2] = ent->r.absmin[2];
			break;
		}

		// if it is reasonably close to the ground, give the point underneath of it
		VectorCopy( ent->r.currentOrigin, start );
		start[2] = ent->r.absmin[2];
		VectorCopy( start, end );
		end[2] -= 64;
		trap_Trace( &tr, start, ent->r.mins, ent->r.maxs, end, ent->s.number, MASK_PLAYERSOLID );
		if ( tr.fraction < 1.0 ) 
		{
			VectorCopy( tr.endpos, point);
			break;
		}

		// otherwise just use the origin
		VectorCopy( ent->r.currentOrigin, point );
		break;

	default:
		VectorCopy ( ent->r.currentOrigin, point );
		break;
	}
}
Beispiel #23
0
//-----------------------------------------------------
static qboolean VEH_TurretFindEnemies( Vehicle_t *pVeh, 
						 gentity_t *parent, 
						 turretStats_t *turretStats, 
						 int turretNum, int curMuzzle )
//-----------------------------------------------------
{
	qboolean	found = qfalse;
	int			i, count;
	float		bestDist = turretStats->fAIRange * turretStats->fAIRange;
	float		enemyDist;
	vec3_t		enemyDir, org, org2;
	qboolean	foundClient = qfalse;
	gentity_t	*entity_list[MAX_GENTITIES], *target, *bestTarget = NULL;

	WP_CalcVehMuzzle( parent, curMuzzle );
	VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], org2 );

	count = G_RadiusList( org2, turretStats->fAIRange, parent, qtrue, entity_list );

	for ( i = 0; i < count; i++ )
	{
		trace_t	tr;
		target = entity_list[i];

		if ( target == parent 
			|| !target->takedamage 
			|| target->health <= 0 
			|| ( target->flags & FL_NOTARGET ))
		{
			continue;
		}
		if ( !target->client )
		{// only attack clients
			if ( !(target->flags&FL_BBRUSH)//not a breakable brush
				|| !target->takedamage//is a bbrush, but invincible
				|| (target->NPC_targetname&&parent->targetname&&Q_stricmp(target->NPC_targetname,parent->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me
			{
				if ( target->s.weapon == WP_TURRET
					&& target->classname
					&& Q_strncmp( "misc_turret", target->classname, 11 ) == 0 )
				{//these guys we want to shoot at
				}
				else
				{
					continue;
				}
			}
			//else: we will shoot at bbrushes!
		}
		else if ( target->client->sess.sessionTeam == TEAM_SPECTATOR )
		{
			continue;
		}
		if ( target == ((gentity_t*)pVeh->m_pPilot)
			|| target->r.ownerNum == parent->s.number )
		{//don't get angry at my pilot or passengers?
			continue;
		}
		if ( parent->client
			&& parent->client->sess.sessionTeam )
		{
			if ( target->client )
			{
				if ( target->client->sess.sessionTeam == parent->client->sess.sessionTeam )
				{ 
					// A bot/client/NPC we don't want to shoot
					continue;
				}
			}
			else if ( target->teamnodmg == parent->client->sess.sessionTeam )
			{//some other entity that's allied with us
				continue;
			}
		}
		if ( !trap_InPVS( org2, target->r.currentOrigin ))
		{
			continue;
		}

		VectorCopy( target->r.currentOrigin, org );

		trap_Trace( &tr, org2, NULL, NULL, org, parent->s.number, MASK_SHOT );

		if ( tr.entityNum == target->s.number
			|| (!tr.allsolid && !tr.startsolid && tr.fraction == 1.0 ) )
		{
			// Only acquire if have a clear shot, Is it in range and closer than our best?
			VectorSubtract( target->r.currentOrigin, org2, enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current
			{
				bestTarget = target;
				bestDist = enemyDist;
				found = qtrue;
				if ( target->client )
				{//prefer clients over non-clients
					foundClient = qtrue;
				}
			}
		}
	}

	if ( found )
	{
		pVeh->turretStatus[turretNum].enemyEntNum = bestTarget->s.number;
	}

	return found;
}
Beispiel #24
0
qboolean CanDamage (gentity_t *targ, vec3_t origin) {
	vec3_t	dest;
	trace_t	tr;
	vec3_t	midpoint;
	vec3_t offsetmins = { -16.f, -16.f, -16.f };
	vec3_t offsetmaxs = { 16.f, 16.f, 16.f };

	// use the midpoint of the bounds instead of the origin, because
	// bmodels may have their origin is 0,0,0
	// Gordon: well, um, just check then...
	if(targ->r.currentOrigin[0] || targ->r.currentOrigin[1] || targ->r.currentOrigin[2]) {
		VectorCopy( targ->r.currentOrigin, midpoint );

		if( targ->s.eType == ET_MOVER ) {
			midpoint[2] += 32;
		}
	} else {
		VectorAdd (targ->r.absmin, targ->r.absmax, midpoint);
		VectorScale (midpoint, 0.5, midpoint);
	}

//	G_RailTrail( origin, dest );

	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, midpoint, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if (tr.fraction == 1.0)
		return qtrue;

	if(&g_entities[tr.entityNum] == targ)
		return qtrue;

	if( targ->client ) {
		VectorCopy( targ->client->ps.mins, offsetmins );
		VectorCopy( targ->client->ps.maxs, offsetmaxs );
	}

	// this should probably check in the plane of projection, 
	// rather than in world coordinate
	VectorCopy (midpoint, dest);
	dest[0] += offsetmaxs[0];
	dest[1] += offsetmaxs[1];
	dest[2] += offsetmaxs[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	VectorCopy (midpoint, dest);
	dest[0] += offsetmaxs[0];
	dest[1] += offsetmins[1];
	dest[2] += offsetmaxs[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	VectorCopy (midpoint, dest);
	dest[0] += offsetmins[0];
	dest[1] += offsetmaxs[1];
	dest[2] += offsetmaxs[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	VectorCopy (midpoint, dest);
	dest[0] += offsetmins[0];
	dest[1] += offsetmins[1];
	dest[2] += offsetmaxs[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	// =========================

	VectorCopy (midpoint, dest);
	dest[0] += offsetmaxs[0];
	dest[1] += offsetmaxs[1];
	dest[2] += offsetmins[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	VectorCopy (midpoint, dest);
	dest[0] += offsetmaxs[0];
	dest[1] += offsetmins[1];
	dest[2] += offsetmins[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	VectorCopy (midpoint, dest);
	dest[0] += offsetmins[0];
	dest[1] += offsetmaxs[1];
	dest[2] += offsetmins[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	VectorCopy (midpoint, dest);
	dest[0] += offsetmins[0];
	dest[1] += offsetmins[2];
	dest[2] += offsetmins[2];
	trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_CAN_DAMAGE );
	if( tr.fraction == 1 || &g_entities[tr.entityNum] == targ ) {
		return qtrue;
	}

	return qfalse;
}
Beispiel #25
0
//-----------------------------------------------------
static qboolean turretG2_find_enemies( gentity_t *self )
//-----------------------------------------------------
{
	qboolean	found = qfalse;
	int			i, count;
	float		bestDist = self->radius * self->radius;
	float		enemyDist;
	vec3_t		enemyDir, org, org2;
	qboolean	foundClient = qfalse;
	gentity_t	*entity_list[MAX_GENTITIES], *target, *bestTarget = NULL;

	if ( self->aimDebounceTime > level.time ) // time since we've been shut off
	{
		// We were active and alert, i.e. had an enemy in the last 3 secs
		if ( self->painDebounceTime < level.time )
		{
			if ( !(self->spawnflags&SPF_TURRETG2_TURBO) )
			{
				G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" ));
			}
			self->painDebounceTime = level.time + 1000;
		}
	}

	VectorCopy( self->r.currentOrigin, org2 );
	if ( self->spawnflags & 2 )
	{
		org2[2] += 20;
	}
	else
	{
		org2[2] -= 20;
	}

	count = G_RadiusList( org2, self->radius, self, qtrue, entity_list );

	for ( i = 0; i < count; i++ )
	{
		trace_t	tr;
		target = entity_list[i];

		if ( !target->client )
		{
			// only attack clients
			if ( !(target->flags&FL_BBRUSH)//not a breakable brush
				|| !target->takedamage//is a bbrush, but invincible
				|| (target->NPC_targetname&&self->targetname&&Q_stricmp(target->NPC_targetname,self->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me
			{
				continue;
			}
			//else: we will shoot at bbrushes!
		}
		if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET ))
		{
			continue;
		}
		if ( target->client && target->client->sess.sessionTeam == TEAM_SPECTATOR )
		{
			continue;
		}
		if ( self->alliedTeam )
		{
			if ( target->client )
			{
				if ( target->client->sess.sessionTeam == self->alliedTeam )
				{ 
					// A bot/client/NPC we don't want to shoot
					continue;
				}
			}
			else if ( target->teamnodmg == self->alliedTeam )
			{ 
				// An ent we don't want to shoot
				continue;
			}
		}
		if ( !trap_InPVS( org2, target->r.currentOrigin ))
		{
			continue;
		}

		if ( target->client )
		{
			VectorCopy( target->client->renderInfo.eyePoint, org );
		}
		else
		{
			VectorCopy( target->r.currentOrigin, org );
		}

		if ( self->spawnflags & 2 )
		{
			org[2] -= 15;
		}
		else
		{
			org[2] += 5;
		}

		trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );

		if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number ))
		{
			// Only acquire if have a clear shot, Is it in range and closer than our best?
			VectorSubtract( target->r.currentOrigin, self->r.currentOrigin, enemyDir );
			enemyDist = VectorLengthSquared( enemyDir );

			if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current
			{
				if ( self->attackDebounceTime < level.time )
				{
					// We haven't fired or acquired an enemy in the last 2 seconds-start-up sound
					if ( !(self->spawnflags&SPF_TURRETG2_TURBO) )
					{
						G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" ));
					}

					// Wind up turrets for a bit
					self->attackDebounceTime = level.time + 1400;
				}

				bestTarget = target;
				bestDist = enemyDist;
				found = qtrue;
				if ( target->client )
				{//prefer clients over non-clients
					foundClient = qtrue;
				}
			}
		}
	}

	if ( found )
	{
		G_SetEnemy( self, bestTarget );
		if ( VALIDSTRING( self->target2 ))
		{
			G_UseTargets2( self, self, self->target2 );
		}
	}

	return found;
}
Beispiel #26
0
/*
============
etpro_RadiusDamage
mutation of G_RadiusDamage which lets us selectively damage only clients or only non clients
============
*/
qboolean etpro_RadiusDamage( vec3_t origin, gentity_t *inflictor, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod, RadiusScope scope ) {
	float		points, dist;
	gentity_t	*ent;
	int			entityList[MAX_GENTITIES];
	int			numListedEntities;
	vec3_t		mins, maxs;
	vec3_t		v;
	vec3_t		dir;
	int			i, e;
	qboolean	hitClient = qfalse;
	float		boxradius;
	vec3_t		dest; 
	trace_t		tr;
	vec3_t		midpoint;
	int			flags = DAMAGE_RADIUS;

	if( mod == MOD_SATCHEL || mod == MOD_LANDMINE ) {
		flags |= DAMAGE_HALF_KNOCKBACK;
	}

	if( radius < 1 ) {
		radius = 1;
	}

	boxradius = 1.41421356 * radius; // radius * sqrt(2) for bounding box enlargement -- 
	// bounding box was checking against radius / sqrt(2) if collision is along box plane
	for( i = 0 ; i < 3 ; i++ ) {
		mins[i] = origin[i] - boxradius;
		maxs[i] = origin[i] + boxradius;
	}

	numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );

	for( e = 0 ; e < numListedEntities ; e++ ) {
		ent = &g_entities[entityList[ e ]];

		if( ent == ignore ) {
			continue;
		}
		if( !ent->takedamage && ( !ent->dmgparent || !ent->dmgparent->takedamage )) {
			continue;
		}

        switch (scope) {
            default:
            case RADIUS_SCOPE_ANY:
                break;

            case RADIUS_SCOPE_CLIENTS:
		        if (!ent->client && ent->s.eType != ET_CORPSE )
                    continue;
                break;

            case RADIUS_SCOPE_NOCLIENTS:
		        if (ent->client)
                    continue;
                break;
		}

		if(	ent->waterlevel == 3 && mod == MOD_POISON_GAS) {
			continue;
		}

		G_AdjustedDamageVec( ent, origin, v );

		dist = VectorLength( v );
		if ( dist >= radius ) {
			continue;
		}

		points = damage * ( 1.0 - dist / radius );

		if( CanDamage( ent, origin ) ) {
			if( ent->dmgparent ) {
				ent = ent->dmgparent;
			}

			if( AccuracyHit( ent, attacker ) ) {
				hitClient = qtrue;
			}
			VectorSubtract (ent->r.currentOrigin, origin, dir);
			// push the center of mass higher than the origin so players
			// get knocked into the air more
			dir[2] += 24;

			G_Damage( ent, inflictor, attacker, dir, origin, (int)points, flags, mod );
		} else {
			VectorAdd( ent->r.absmin, ent->r.absmax, midpoint );
			VectorScale( midpoint, 0.5, midpoint );
			VectorCopy( midpoint, dest );
		
			trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
			if( tr.fraction < 1.0 ) {
				VectorSubtract( dest, origin, dest );
				dist = VectorLength( dest );
				if( dist < radius * 0.2f ) { // closer than 1/4 dist
					if( ent->dmgparent ) {
						ent = ent->dmgparent;
					}

					if( AccuracyHit( ent, attacker ) ) {
						hitClient = qtrue;
					}
					VectorSubtract (ent->r.currentOrigin, origin, dir);
					dir[2] += 24;
					G_Damage( ent, inflictor, attacker, dir, origin, (int)(points*0.1f), flags, mod );
				}
			}
		}
	}

	return hitClient;
}
Beispiel #27
0
gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) {
	trace_t		tr;
	vec3_t		dest;
	gentity_t	*ent;

	ent = G_Spawn();

	VectorCopy( origin, ent->s.origin );
	VectorCopy( origin, ent->s.pos.trBase );
	VectorCopy( origin, ent->r.currentOrigin );

	VectorSet( ent->r.mins, -15, -15, 0 );
	VectorSet( ent->r.maxs, 15, 15, 87 );

	ent->s.eType = ET_GENERAL;
	ent->flags = FL_NO_KNOCKBACK;

	if( g_gametype.integer == GT_OBELISK ) {
		ent->r.contents = CONTENTS_SOLID;
		ent->takedamage = qtrue;
		ent->health = g_obeliskHealth.integer;
		ent->die = ObeliskDie;
		ent->pain = ObeliskPain;
		ent->think = ObeliskRegen;
		ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
	}
	if( g_gametype.integer == GT_HARVESTER ) {
		ent->r.contents = CONTENTS_TRIGGER;
		ent->touch = ObeliskTouch;
	}

	if ( spawnflags & 1 ) {
		// suspended
		G_SetOrigin( ent, ent->s.origin );
	} else {
		// mappers like to put them exactly on the floor, but being coplanar
		// will sometimes show up as starting in solid, so lif it up one pixel
		ent->s.origin[2] += 1;

		// drop to floor
		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
		if ( tr.startsolid ) {
			ent->s.origin[2] -= 1;
			G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );

			ent->s.groundEntityNum = ENTITYNUM_NONE;
			G_SetOrigin( ent, ent->s.origin );
		}
		else {
			// allow to ride movers
			ent->s.groundEntityNum = tr.entityNum;
			G_SetOrigin( ent, tr.endpos );
		}
	}

	ent->spawnflags = team;

	trap_LinkEntity( ent );

	return ent;
}
Beispiel #28
0
/*
==============
ClientThink

This will be called once for each client frame, which will
usually be a couple times for each server frame on fast clients.

If "g_synchronousClients 1" is set, this will be called exactly
once for each server frame, which makes for smooth demo recording.
==============
*/
void ClientThink_real( gentity_t *ent )
{
  gclient_t *client;
  pmove_t   pm;
  int       oldEventSequence;
  int       msec;
  usercmd_t *ucmd;

  client = ent->client;

  // don't think if the client is not yet connected (and thus not yet spawned in)
  if( client->pers.connected != CON_CONNECTED )
    return;

  // mark the time, so the connection sprite can be removed
  ucmd = &ent->client->pers.cmd;

  // sanity check the command time to prevent speedup cheating
  if( ucmd->serverTime > level.time + 200 )
  {
    ucmd->serverTime = level.time + 200;
//    G_Printf("serverTime <<<<<\n" );
  }

  if( ucmd->serverTime < level.time - 1000 )
  {
    ucmd->serverTime = level.time - 1000;
//    G_Printf("serverTime >>>>>\n" );
  }

  msec = ucmd->serverTime - client->ps.commandTime;
  // following others may result in bad times, but we still want
  // to check for follow toggles
  if( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW )
    return;

  if( msec > 200 )
    msec = 200;

  if( pmove_msec.integer < 8 )
    trap_Cvar_Set( "pmove_msec", "8" );
  else if( pmove_msec.integer > 33 )
    trap_Cvar_Set( "pmove_msec", "33" );

  if( pmove_fixed.integer || client->pers.pmoveFixed )
  {
    ucmd->serverTime = ( ( ucmd->serverTime + pmove_msec.integer - 1 ) / pmove_msec.integer ) * pmove_msec.integer;
    //if (ucmd->serverTime - client->ps.commandTime <= 0)
    //  return;
  }

  //
  // check for exiting intermission
  //
  if( level.intermissiontime )
  {
    ClientIntermissionThink( client );
    return;
  }

  // spectators don't do much
  if( client->sess.sessionTeam == TEAM_SPECTATOR )
  {
    if( client->sess.spectatorState == SPECTATOR_SCOREBOARD )
      return;

    SpectatorThink( ent, ucmd );
    return;
  }

  G_UpdatePTRConnection( client );

  // check for inactivity timer, but never drop the local client of a non-dedicated server
  if( !ClientInactivityTimer( client ) )
    return;

  if( client->noclip )
    client->ps.pm_type = PM_NOCLIP;
  else if( client->ps.stats[ STAT_HEALTH ] <= 0 )
    client->ps.pm_type = PM_DEAD;
  else if( client->ps.stats[ STAT_STATE ] & SS_INFESTING ||
           client->ps.stats[ STAT_STATE ] & SS_HOVELING )
    client->ps.pm_type = PM_FREEZE;
  else if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED ||
           client->ps.stats[ STAT_STATE ] & SS_GRABBED )
    client->ps.pm_type = PM_GRABBED;
  else if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
    client->ps.pm_type = PM_JETPACK;
  else
    client->ps.pm_type = PM_NORMAL;

  if( client->ps.stats[ STAT_STATE ] & SS_GRABBED &&
      client->grabExpiryTime < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_GRABBED;

  if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED &&
      client->lastLockTime + 5000 < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED;

  if( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED &&
      client->lastLockTime + ABUILDER_BLOB_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED;

  client->ps.stats[ STAT_BOOSTTIME ] = level.time - client->lastBoostedTime;

  if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED &&
      client->lastBoostedTime + BOOST_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED;

  if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
      client->lastPoisonCloudedTime + LEVEL1_PCLOUD_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED;

  if( client->ps.stats[ STAT_STATE ] & SS_POISONED &&
      client->lastPoisonTime + ALIEN_POISON_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;

  client->ps.gravity = g_gravity.value;

  if( BG_InventoryContainsUpgrade( UP_MEDKIT, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_MEDKIT, client->ps.stats ) )
  {
    //if currently using a medkit or have no need for a medkit now
    if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ||
        ( client->ps.stats[ STAT_HEALTH ] == client->ps.stats[ STAT_MAX_HEALTH ] &&
          !( client->ps.stats[ STAT_STATE ] & SS_POISONED ) ) )
    {
      BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats );
    }
    else if( client->ps.stats[ STAT_HEALTH ] > 0 )
    {
      //remove anti toxin
      BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats );
      BG_RemoveUpgradeFromInventory( UP_MEDKIT, client->ps.stats );

      client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
      client->poisonImmunityTime = level.time + MEDKIT_POISON_IMMUNITY_TIME;

      client->ps.stats[ STAT_STATE ] |= SS_MEDKIT_ACTIVE;
      client->lastMedKitTime = level.time;
      client->medKitHealthToRestore =
        client->ps.stats[ STAT_MAX_HEALTH ] - client->ps.stats[ STAT_HEALTH ];
      client->medKitIncrementTime = level.time +
        ( MEDKIT_STARTUP_TIME / MEDKIT_STARTUP_SPEED );

      G_AddEvent( ent, EV_MEDKIT_USED, 0 );
    }
  }

  if( BG_InventoryContainsUpgrade( UP_GRENADE, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_GRENADE, client->ps.stats ) )
  {
    int lastWeapon = ent->s.weapon;

    //remove grenade
    BG_DeactivateUpgrade( UP_GRENADE, client->ps.stats );
    BG_RemoveUpgradeFromInventory( UP_GRENADE, client->ps.stats );

    //M-M-M-M-MONSTER HACK
    ent->s.weapon = WP_GRENADE;
    FireWeapon( ent );
    ent->s.weapon = lastWeapon;
  }

  // set speed
  client->ps.speed = g_speed.value * BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] );

  if( client->lastCreepSlowTime + CREEP_TIMEOUT < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_CREEPSLOWED;

  //randomly disable the jet pack if damaged
  if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
  {
    if( ent->lastDamageTime + JETPACK_DISABLE_TIME > level.time )
    {
      if( random( ) > JETPACK_DISABLE_CHANCE )
        client->ps.pm_type = PM_NORMAL;
    }

    //switch jetpack off if no reactor
    if( !level.reactorPresent )
      BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );
  }

  // set up for pmove
  oldEventSequence = client->ps.eventSequence;

  memset( &pm, 0, sizeof( pm ) );

  if( !( ucmd->buttons & BUTTON_TALK ) ) //&& client->ps.weaponTime <= 0 ) //TA: erk more server load
  {
    switch( client->ps.weapon )
    {
      case WP_ALEVEL0:
        if( client->ps.weaponTime <= 0 )
          pm.autoWeaponHit[ client->ps.weapon ] = CheckVenomAttack( ent );
        break;

      case WP_ALEVEL1:
      case WP_ALEVEL1_UPG:
        CheckGrabAttack( ent );
        break;

      case WP_ALEVEL3:
      case WP_ALEVEL3_UPG:
        if( client->ps.weaponTime <= 0 )
          pm.autoWeaponHit[ client->ps.weapon ] = CheckPounceAttack( ent );
        break;

      default:
        break;
    }
  }

  if( ent->flags & FL_FORCE_GESTURE )
  {
    ent->flags &= ~FL_FORCE_GESTURE;
    ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
  }

  pm.ps = &client->ps;
  pm.cmd = *ucmd;

  if( pm.ps->pm_type == PM_DEAD )
    pm.tracemask = MASK_PLAYERSOLID; // & ~CONTENTS_BODY;

  if( pm.ps->stats[ STAT_STATE ] & SS_INFESTING ||
      pm.ps->stats[ STAT_STATE ] & SS_HOVELING )
    pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
  else
    pm.tracemask = MASK_PLAYERSOLID;

  pm.trace = trap_Trace;
  pm.pointcontents = trap_PointContents;
  pm.debugLevel = g_debugMove.integer;
  pm.noFootsteps = (qboolean)0;

  pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
  pm.pmove_msec = pmove_msec.integer;

  VectorCopy( client->ps.origin, client->oldOrigin );

  // moved from after Pmove -- potentially the cause of
  // future triggering bugs
  if( !ent->client->noclip )
    G_TouchTriggers( ent );

  Pmove( &pm );

  // save results of pmove
  if( ent->client->ps.eventSequence != oldEventSequence )
    ent->eventTime = level.time;

  if( g_smoothClients.integer )
    BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
  else
    BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );

  SendPendingPredictableEvents( &ent->client->ps );

  if( !( ent->client->ps.eFlags & EF_FIRING ) )
    client->fireHeld = qfalse;    // for grapple
  if( !( ent->client->ps.eFlags & EF_FIRING2 ) )
    client->fire2Held = qfalse;

  // use the snapped origin for linking so it matches client predicted versions
  VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );

  VectorCopy( pm.mins, ent->r.mins );
  VectorCopy( pm.maxs, ent->r.maxs );

  ent->waterlevel = pm.waterlevel;
  ent->watertype = pm.watertype;

  // execute client events
  ClientEvents( ent, oldEventSequence );

  // link entity now, after any personal teleporters have been used
  trap_LinkEntity( ent );

  // NOTE: now copy the exact origin over otherwise clients can be snapped into solid
  VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
  VectorCopy( ent->client->ps.origin, ent->s.origin );

  // touch other objects
  ClientImpacts( ent, &pm );

  // save results of triggers and client events
  if( ent->client->ps.eventSequence != oldEventSequence )
    ent->eventTime = level.time;

  // swap and latch button actions
  client->oldbuttons = client->buttons;
  client->buttons = ucmd->buttons;
  client->latched_buttons |= client->buttons & ~client->oldbuttons;

  if( ( client->buttons & BUTTON_GETFLAG ) && !( client->oldbuttons & BUTTON_GETFLAG ) &&
       client->ps.stats[ STAT_HEALTH ] > 0 )
  {
    trace_t   trace;
    vec3_t    view, point;
    gentity_t *traceEnt;

    if( client->ps.stats[ STAT_STATE ] & SS_HOVELING )
    {
      gentity_t *hovel = client->hovel;

      //only let the player out if there is room
      if( !AHovel_Blocked( hovel, ent, qtrue ) )
      {
        //prevent lerping
        client->ps.eFlags ^= EF_TELEPORT_BIT;
        client->ps.eFlags &= ~EF_NODRAW;

        //client leaves hovel
        client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING;

        //hovel is empty
        G_setBuildableAnim( hovel, BANIM_ATTACK2, qfalse );
        hovel->active = qfalse;
      }
      else
      {
        //exit is blocked
        G_TriggerMenu( ent->client->ps.clientNum, MN_A_HOVEL_BLOCKED );
      }
    }
    else
    {
#define USE_OBJECT_RANGE 64

      int       entityList[ MAX_GENTITIES ];
      vec3_t    range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE };
      vec3_t    mins, maxs;
      int       i, num;

      //TA: look for object infront of player
      AngleVectors( client->ps.viewangles, view, NULL, NULL );
      VectorMA( client->ps.origin, USE_OBJECT_RANGE, view, point );
      trap_Trace( &trace, client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT );

      traceEnt = &g_entities[ trace.entityNum ];

      if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use )
        traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
      else
      {
        //no entity in front of player - do a small area search

        VectorAdd( client->ps.origin, range, maxs );
        VectorSubtract( client->ps.origin, range, mins );

        num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
        for( i = 0; i < num; i++ )
        {
          traceEnt = &g_entities[ entityList[ i ] ];

          if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use )
          {
            traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
            break;
          }
        }

        if( i == num && client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
        {
          if( BG_UpgradeClassAvailable( &client->ps ) )
          {
            //no nearby objects and alien - show class menu
            G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST );
          }
          else
          {
            //flash frags
            G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 );
          }
        }
      }
    }
  }

  // check for respawning
  if( client->ps.stats[ STAT_HEALTH ] <= 0 )
  {
    // wait for the attack button to be pressed
    if( level.time > client->respawnTime )
    {
      // forcerespawn is to prevent users from waiting out powerups
      if( g_forcerespawn.integer > 0 &&
        ( level.time - client->respawnTime ) > 0 )
      {
        respawn( ent );
        return;
      }

      // pressing attack or use is the normal respawn method
      if( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) )
      {
        respawn( ent );
      }
    }
    return;
  }

  if( level.framenum > client->retriggerArmouryMenu && client->retriggerArmouryMenu )
  {
    G_TriggerMenu( client->ps.clientNum, MN_H_ARMOURY );

    client->retriggerArmouryMenu = 0;
  }

  // Give clients some credit periodically
  if( ent->client->lastKillTime + FREEKILL_PERIOD < level.time )
  {
    if( g_suddenDeathTime.integer &&
        ( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 ) )
    {
      //gotta love logic like this eh?
    }
    else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
      G_AddCreditToClient( ent->client, FREEKILL_ALIEN, qtrue );
    else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
      G_AddCreditToClient( ent->client, FREEKILL_HUMAN, qtrue );

    ent->client->lastKillTime = level.time;
  }

  // perform once-a-second actions
  ClientTimerActions( ent, msec );
  
  if( ent->suicideTime > 0 && ent->suicideTime < level.time )
  {
    ent->flags &= ~FL_GODMODE;
    ent->client->ps.stats[ STAT_HEALTH ] = ent->health = 0;
    player_die( ent, ent, ent, 100000, MOD_SUICIDE );

    ent->suicideTime = 0;
  }
}
Beispiel #29
0
/**
* @brief Run item.
*/
void G_RunItem(gentity_t *ent)
{
	vec3_t  origin;
	trace_t tr;
	int     contents;
	int     mask;

	// if groundentity has been set to -1, it may have been pushed off an edge
	if (ent->s.groundEntityNum == -1)
	{
		if (ent->s.pos.trType != TR_GRAVITY)
		{
			ent->s.pos.trType = TR_GRAVITY;
			ent->s.pos.trTime = level.time;
		}
	}

	if (ent->s.pos.trType == TR_STATIONARY || ent->s.pos.trType == TR_GRAVITY_PAUSED) // check think function
	{
		G_RunThink(ent);
		return;
	}

	if (ent->s.pos.trType == TR_LINEAR && (!ent->clipmask && !ent->r.contents))
	{
		// check think function
		G_RunThink(ent);
		return;
	}

	// get current position
	BG_EvaluateTrajectory(&ent->s.pos, level.time, origin, qfalse, ent->s.effect2Time);

	// trace a line from the previous position to the current position
	if (ent->clipmask)
	{
		mask = ent->clipmask;
	}
	else
	{
		mask = MASK_SOLID;
	}
	trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
	           ent->r.ownerNum, mask);

	if (ent->isProp && ent->takedamage)
	{
		G_RunItemProp(ent, origin);
	}

	VectorCopy(tr.endpos, ent->r.currentOrigin);

	if (tr.startsolid)
	{
		tr.fraction = 0;
	}

	trap_LinkEntity(ent);   // FIXME: avoid this for stationary?

	// check think function
	G_RunThink(ent);

	if (tr.fraction == 1)
	{
		return;
	}

	// if it is in a nodrop volume, remove it
	contents = trap_PointContents(ent->r.currentOrigin, -1);
	if (contents & CONTENTS_NODROP)
	{
		if (ent->item && ent->item->giType == IT_TEAM)
		{
			Team_ReturnFlag(ent);
		}
		else
		{
			G_FreeEntity(ent);
		}
		return;
	}

	G_BounceItem(ent, &tr);
}
Beispiel #30
0
void NPC_BSGM_Default( void )
{
	if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
	{
		WeaponThink( qtrue );
	}
	
	if ( NPC->client->ps.stats[STAT_ARMOR] <= 0 )
	{//armor gone
	//	if ( !NPCInfo->investigateDebounceTime )
		if (0)
		{//start regenerating the armor
			NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_OFF );
			NPC->flags &= ~FL_SHIELDED;//no more reflections
			VectorSet( NPC->r.mins, -20, -20, -24 );
			VectorSet( NPC->r.maxs, 20, 20, 64 );
			NPC->client->ps.crouchheight = NPC->client->ps.standheight = 64;
			if ( NPC->locationDamage[HL_GENERIC1] < GENERATOR_HEALTH )
			{//still have the generator bolt-on
				if ( NPCInfo->investigateCount < 12 )
				{
					NPCInfo->investigateCount++;
				}
				NPCInfo->investigateDebounceTime = level.time + (NPCInfo->investigateCount * 5000);
			}
		}
		else if ( NPCInfo->investigateDebounceTime < level.time )
		{//armor regenerated, turn shield back on
			//do a trace and make sure we can turn this back on?
			trace_t	tr;
			trap_Trace( &tr, NPC->r.currentOrigin, shieldMins, shieldMaxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask );
			if ( !tr.startsolid )
			{
				VectorCopy( shieldMins, NPC->r.mins );
				VectorCopy( shieldMaxs, NPC->r.maxs );
				NPC->client->ps.crouchheight = NPC->client->ps.standheight = shieldMaxs[2];
				NPC->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH;
				NPCInfo->investigateDebounceTime = 0;
				NPC->flags |= FL_SHIELDED;//reflect normal shots
			//	NPC->fx_time = level.time;
				NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_ON );
			}
		}
	}
	/*
	if ( NPC->client->ps.stats[STAT_ARMOR] > 0 )
	{//armor present
		NPC->client->ps.powerups[PW_GALAK_SHIELD] = Q3_INFINITE;//temp, for effect
		NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_ON );
	}
	else
	{
		NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_OFF );
	}
	*/
	//rwwFIXMEFIXME: Allow this stuff, and again, going to have to let the client know about it.
	//Maybe a surface-off bitflag of some sort in the entity state?

	if( !NPC->enemy )
	{//don't have an enemy, look for one
		NPC_BSGM_Patrol();
	}
	else //if ( NPC->enemy )
	{//have an enemy
		NPC_BSGM_Attack();
	}
}