Beispiel #1
0
void NPC_BSCinematic( void ) 
{

	if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
	{
		WeaponThink( qtrue );
	}

	if ( UpdateGoal() )
	{//have a goalEntity
		//move toward goal, should also face that goal
		NPC_MoveToGoal( qtrue );
	}

	if ( NPCInfo->watchTarget )
	{//have an entity which we want to keep facing
		//NOTE: this will override any angles set by NPC_MoveToGoal
		vec3_t eyes, viewSpot, viewvec, viewangles;

		CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
		CalcEntitySpot( NPCInfo->watchTarget, SPOT_HEAD_LEAN, viewSpot );

		VectorSubtract( viewSpot, eyes, viewvec );
		
		vectoangles( viewvec, viewangles );

		NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw = viewangles[YAW];
		NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch = viewangles[PITCH];
	}

	NPC_UpdateAngles( qtrue, qtrue );
}
Beispiel #2
0
void NPC_BSGrenadier_Default( void ) {
	if ( NPCInfo->scriptFlags & SCF_FIRE_WEAPON ) {
		WeaponThink( qtrue );
	}

	if ( !NPC->enemy ) {//don't have an enemy, look for one
		NPC_BSGrenadier_Patrol();
	}
	else//if ( NPC->enemy )
	{//have an enemy
		NPC_BSGrenadier_Attack();
	}
}
Beispiel #3
0
void NPC_BSShoot(void)
{
//	NPC_BSMove();

	enemyVisibility = VIS_SHOOT;

	if ( client->ps.weaponstate != WEAPON_READY && client->ps.weaponstate != WEAPON_FIRING ) 
	{
		client->ps.weaponstate = WEAPON_READY;
	}

	WeaponThink(qtrue);
}
Beispiel #4
0
void NPC_BSFollowLeader (void)
{
	vec3_t		vec;
	float		leaderDist;
	visibility_t	leaderVis;
	int			curAnim;

	if ( !NPC->client->leader )
	{//ok, stand guard until we find an enemy
		if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
		{
			NPCInfo->tempBehavior = BS_DEFAULT;
		}
		else
		{
			NPCInfo->tempBehavior = BS_STAND_GUARD;
			NPC_BSStandGuard();
		}
		return;
	}

	if ( !NPC->enemy  )
	{//no enemy, find one
		NPC_CheckEnemy( NPCInfo->confusionTime<level.time, qfalse );//don't find new enemy if this is tempbehav
		if ( NPC->enemy )
		{//just found one
			NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
		}
		else
		{
			if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
			{
				int eventID = NPC_CheckAlertEvents( qtrue, qtrue );
				if ( level.alertEvents[eventID].level >= AEL_SUSPICIOUS && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
				{
					NPCInfo->lastAlertID = level.alertEvents[eventID].ID;
					if ( !level.alertEvents[eventID].owner || 
						!level.alertEvents[eventID].owner->client || 
						level.alertEvents[eventID].owner->health <= 0 ||
						level.alertEvents[eventID].owner->client->playerTeam != NPC->client->enemyTeam )
					{//not an enemy
					}
					else
					{
						//FIXME: what if can't actually see enemy, don't know where he is... should we make them just become very alert and start looking for him?  Or just let combat AI handle this... (act as if you lost him)
						G_SetEnemy( NPC, level.alertEvents[eventID].owner );
						NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
						NPCInfo->enemyLastSeenTime = level.time;
						TIMER_Set( NPC, "attackDelay", Q_irand( 500, 1000 ) );
					}
				}

			}
		}
		if ( !NPC->enemy )
		{
			if ( NPC->client->leader 
				&& NPC->client->leader->enemy 
				&& NPC->client->leader->enemy != NPC
				&& ( (NPC->client->leader->enemy->client&&NPC->client->leader->enemy->client->playerTeam==NPC->client->enemyTeam)
					||(NPC->client->leader->enemy->svFlags&SVF_NONNPC_ENEMY&&NPC->client->leader->enemy->noDamageTeam==NPC->client->enemyTeam) )
				&& NPC->client->leader->enemy->health > 0 )
			{
				G_SetEnemy( NPC, NPC->client->leader->enemy );
				NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 );
				NPCInfo->enemyLastSeenTime = level.time;
			}
		}
	}
	else 
	{
		if ( NPC->enemy->health <= 0 || (NPC->enemy->flags&FL_NOTARGET) )
		{
			G_ClearEnemy( NPC );
			if ( NPCInfo->enemyCheckDebounceTime > level.time + 1000 )
			{
				NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 2000 );
			}
		}
		else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time )
		{
			NPC_CheckEnemy( (NPCInfo->confusionTime<level.time||NPCInfo->tempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav
		}
	}
	
	if ( NPC->enemy && NPC->client->ps.weapon )
	{//If have an enemy, face him and fire
		if ( NPC->client->ps.weapon == WP_SABER )//|| NPCInfo->confusionTime>level.time )
		{//lightsaber user or charmed enemy
			if ( NPCInfo->tempBehavior != BS_FOLLOW_LEADER )
			{//not already in a temp bState
				//go after the guy
				NPCInfo->tempBehavior = BS_HUNT_AND_KILL;
				NPC_UpdateAngles(qtrue, qtrue);
				return;
			}
		}

		enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|CHECK_PVS|
		if ( enemyVisibility > VIS_PVS )
		{//face
			vec3_t	enemy_org, muzzle, delta, angleToEnemy;
			float	distanceToEnemy;

			CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org );
			NPC_AimWiggle( enemy_org );

			CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
			
			VectorSubtract( enemy_org, muzzle, delta);
			vectoangles( delta, angleToEnemy );
			distanceToEnemy = VectorNormalize( delta );

			NPCInfo->desiredYaw = angleToEnemy[YAW];
			NPCInfo->desiredPitch = angleToEnemy[PITCH];
			NPC_UpdateFiringAngles( qtrue, qtrue );

			if ( enemyVisibility >= VIS_SHOOT )
			{//shoot
				NPC_AimAdjust( 2 );
				if ( NPC_GetHFOVPercentage( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.hfov ) > 0.6f 
					&& NPC_GetHFOVPercentage( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.vfov ) > 0.5f )
				{//actually withing our front cone
					WeaponThink( qtrue );
				}
			}
			else
			{
				NPC_AimAdjust( 1 );
			}
			
			//NPC_CheckCanAttack(1.0, qfalse);
		}
		else
		{
			NPC_AimAdjust( -1 );
		}
	}
	else
	{//FIXME: combine with vector calc below
		vec3_t	head, leaderHead, delta, angleToLeader;

		CalcEntitySpot( NPC->client->leader, SPOT_HEAD, leaderHead );
		CalcEntitySpot( NPC, SPOT_HEAD, head );
		VectorSubtract (leaderHead, head, delta);
		vectoangles ( delta, angleToLeader );
		VectorNormalize(delta);
		NPC->NPC->desiredYaw = angleToLeader[YAW];
		NPC->NPC->desiredPitch = angleToLeader[PITCH];
		
		NPC_UpdateAngles(qtrue, qtrue);
	}

	//leader visible?
	leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_SHOOT );//			ent->e_UseFunc = useF_NULL;


	//Follow leader, stay within visibility and a certain distance, maintain a distance from.
	curAnim = NPC->client->ps.legsAnim;
	if ( curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 )
	{//Don't move toward leader if we're in a full-body attack anim
		//FIXME, use IdealDistance to determine if we need to close distance
		float	followDist = 96.0f;//FIXME:  If there are enmies, make this larger?
		float	backupdist, walkdist, minrundist;

		if ( NPCInfo->followDist )
		{
			followDist = NPCInfo->followDist;
		}
		backupdist = followDist/2.0f;
		walkdist = followDist*0.83;
		minrundist = followDist*1.33;

		VectorSubtract(NPC->client->leader->currentOrigin, NPC->currentOrigin, vec);
		leaderDist = VectorLength( vec );//FIXME: make this just nav distance?
		//never get within their radius horizontally
		vec[2] = 0;
		float leaderHDist = VectorLength( vec );
		if( leaderHDist > backupdist && (leaderVis != VIS_SHOOT || leaderDist > walkdist) )
		{//We should close in?
			NPCInfo->goalEntity = NPC->client->leader;

			NPC_SlideMoveToGoal();
			if ( leaderVis == VIS_SHOOT && leaderDist < minrundist )
			{
				ucmd.buttons |= BUTTON_WALKING;
			}
		}
		else if ( leaderDist < backupdist )
		{//We should back off?
			NPCInfo->goalEntity = NPC->client->leader;
			NPC_SlideMoveToGoal();

			//reversing direction
			ucmd.forwardmove = -ucmd.forwardmove;
			ucmd.rightmove   = -ucmd.rightmove;
			VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
		}//otherwise, stay where we are
		//check for do not enter and stop if there's one there...
		if ( ucmd.forwardmove || ucmd.rightmove || VectorCompare( vec3_origin, NPC->client->ps.moveDir ) )
		{
			NPC_MoveDirClear( ucmd.forwardmove, ucmd.rightmove, qtrue );
		}
	}
}
Beispiel #5
0
/*
 void NPC_BSAdvanceFight (void)

Advance towards your captureGoal and shoot anyone you can along the way.
*/
void NPC_BSAdvanceFight (void)
{//FIXME: IMPLEMENT
//Head to Goal if I can

	//Make sure we're still headed where we want to capture
	if ( NPCInfo->captureGoal )
	{//FIXME: if no captureGoal, what do we do?
		//VectorCopy( NPCInfo->captureGoal->currentOrigin, NPCInfo->tempGoal->currentOrigin );
		//NPCInfo->goalEntity = NPCInfo->tempGoal;

		NPC_SetMoveGoal( NPC, NPCInfo->captureGoal->currentOrigin, 16, qtrue );

//		NAV_ClearLastRoute(NPC);
		NPCInfo->goalTime = level.time + 100000;
	}

//	NPC_BSRun();

	NPC_CheckEnemy(qtrue, qfalse);

	//FIXME: Need melee code
	if( NPC->enemy )
	{//See if we can shoot him
		vec3_t		delta, forward;
		vec3_t		angleToEnemy;
		vec3_t		hitspot, muzzle, diff, enemy_org, enemy_head;
		float		distanceToEnemy;
		qboolean	attack_ok = qfalse;
		qboolean	dead_on = qfalse;
		float		attack_scale = 1.0;
		float		aim_off;
		float		max_aim_off = 64;

		//Yaw to enemy
		VectorMA(NPC->enemy->absmin, 0.5, NPC->enemy->maxs, enemy_org);
		CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
		
		VectorSubtract (enemy_org, muzzle, delta);
		vectoangles ( delta, angleToEnemy );
		distanceToEnemy = VectorNormalize(delta);

		if(!NPC_EnemyTooFar(NPC->enemy, distanceToEnemy*distanceToEnemy, qtrue))
		{
			attack_ok = qtrue;
		}

		if(attack_ok)
		{
			NPC_UpdateShootAngles(angleToEnemy, qfalse, qtrue);

			NPCInfo->enemyLastVisibility = enemyVisibility;
			enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV);//CHECK_360|//CHECK_PVS|

			if(enemyVisibility == VIS_FOV)
			{//He's in our FOV
				
				attack_ok = qtrue;
				CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_head);

				if(attack_ok)
				{
					trace_t		tr;
					gentity_t	*traceEnt;
					//are we gonna hit him if we shoot at his center?
					gi.trace ( &tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 );
					traceEnt = &g_entities[tr.entityNum];
					if( traceEnt != NPC->enemy &&
						(!traceEnt || !traceEnt->client || !NPC->client->enemyTeam || NPC->client->enemyTeam != traceEnt->client->playerTeam) )
					{//no, so shoot for the head
						attack_scale *= 0.75;
						gi.trace ( &tr, muzzle, NULL, NULL, enemy_head, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 );
						traceEnt = &g_entities[tr.entityNum];
					}

					VectorCopy( tr.endpos, hitspot );

					if( traceEnt == NPC->enemy || (traceEnt->client && NPC->client->enemyTeam && NPC->client->enemyTeam == traceEnt->client->playerTeam) )
					{
						dead_on = qtrue;
					}
					else
					{
						attack_scale *= 0.5;
						if(NPC->client->playerTeam)
						{
							if(traceEnt && traceEnt->client && traceEnt->client->playerTeam)
							{
								if(NPC->client->playerTeam == traceEnt->client->playerTeam)
								{//Don't shoot our own team
									attack_ok = qfalse;
								}
							}
						}
					}
				}

				if( attack_ok )
				{
					//ok, now adjust pitch aim
					VectorSubtract (hitspot, muzzle, delta);
					vectoangles ( delta, angleToEnemy );
					NPC->NPC->desiredPitch = angleToEnemy[PITCH];
					NPC_UpdateShootAngles(angleToEnemy, qtrue, qfalse);

					if( !dead_on )
					{//We're not going to hit him directly, try a suppressing fire
						//see if where we're going to shoot is too far from his origin
						AngleVectors (NPCInfo->shootAngles, forward, NULL, NULL);
						VectorMA ( muzzle, distanceToEnemy, forward, hitspot);
						VectorSubtract(hitspot, enemy_org, diff);
						aim_off = VectorLength(diff);
						if(aim_off > random() * max_aim_off)//FIXME: use aim value to allow poor aim?
						{
							attack_scale *= 0.75;
							//see if where we're going to shoot is too far from his head
							VectorSubtract(hitspot, enemy_head, diff);
							aim_off = VectorLength(diff);
							if(aim_off > random() * max_aim_off)
							{
								attack_ok = qfalse;
							}
						}
						attack_scale *= (max_aim_off - aim_off + 1)/max_aim_off;
					}
				}
			}
		}

		if( attack_ok )
		{
			if( NPC_CheckAttack( attack_scale ))
			{//check aggression to decide if we should shoot
				enemyVisibility = VIS_SHOOT;
				WeaponThink(qtrue);
			}
			else
				attack_ok = qfalse;
		}
//Don't do this- only for when stationary and trying to shoot an enemy
//		else
//			NPC->cantHitEnemyCounter++;
	}
	else
	{//FIXME: 
		NPC_UpdateShootAngles(NPC->client->ps.viewangles, qtrue, qtrue);
	}

	if(!ucmd.forwardmove && !ucmd.rightmove)
	{//We reached our captureGoal
		if(NPC->taskManager)
		{
			Q3_TaskIDComplete( NPC, TID_BSTATE );
		}
	}
}
Beispiel #6
0
void NPC_BSEmplaced( void )
{
	//Don't do anything if we're hurt
	if ( NPC->painDebounceTime > level.time )
	{
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
	{
		WeaponThink( qtrue );
	}

	//If we don't have an enemy, just idle
	if ( NPC_CheckEnemyExt() == qfalse )
	{
		if ( !Q_irand( 0, 30 ) )
		{
			NPCInfo->desiredYaw = NPC->s.angles[1] + Q_irand( -90, 90 );
		}
		if ( !Q_irand( 0, 30 ) )
		{
			NPCInfo->desiredPitch = Q_irand( -20, 20 );
		}
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	qboolean enemyLOS = qfalse;
	qboolean enemyCS = qfalse;
	qboolean faceEnemy = qfalse;
	qboolean shoot = qfalse;
	vec3_t	impactPos;

	if ( NPC_ClearLOS( NPC->enemy ) )
	{
		enemyLOS = qtrue;

		int hit = NPC_ShotEntity( NPC->enemy, impactPos );
		gentity_t *hitEnt = &g_entities[hit];

		if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->takedamage ) )
		{//can hit enemy or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway
			enemyCS = qtrue;
			NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
			VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation );
		}
	}
/*
	else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;
		faceEnemy = qtrue;
		NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
	}
*/

	if ( enemyLOS )
	{//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
		faceEnemy = qtrue;
	}
	if ( enemyCS )
	{
		shoot = qtrue;
	}

	if ( faceEnemy )
	{//face the enemy
		NPC_FaceEnemy( qtrue );
	}
	else
	{//we want to face in the dir we're running
		NPC_UpdateAngles( qtrue, qtrue );
	}

	if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
	{
		shoot = 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)
			shoot = qfalse;
		}
	}
	if ( shoot )
	{//try to shoot if it's time
		if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
		{
			WeaponThink( qtrue );
		}
	}
}
Beispiel #7
0
void	Pilot_Steer_Vehicle()
{
	if (!NPC->enemy || !NPC->enemy->client)
	{
		return;
	}






// SETUP
//=======
	// Setup Actor Data
	//------------------
	CVec3		ActorPos(NPC->currentOrigin);
	CVec3		ActorAngles(NPC->currentAngles);
				ActorAngles[2]	= 0;
	Vehicle_t*	ActorVeh		= NPCInfo->greetEnt->m_pVehicle;
	bool		ActorInTurbo	= (ActorVeh->m_iTurboTime>level.time);
	float		ActorSpeed		= (ActorVeh)?(VectorLength(ActorVeh->m_pParentEntity->client->ps.velocity)):(NPC->client->ps.speed);


	// If my vehicle is spinning out of control, just hold on, we're going to die!!!!!
	//---------------------------------------------------------------------------------
	if (ActorVeh && (ActorVeh->m_ulFlags & VEH_OUTOFCONTROL))
	{
		if (NPC->client->ps.weapon!=WP_NONE)
		{
			NPC_ChangeWeapon(WP_NONE);
		}
		ucmd.buttons	&=~BUTTON_ATTACK;
		ucmd.buttons	&=~BUTTON_ALT_ATTACK;
		return;
	}

	CVec3		ActorDirection;
				AngleVectors(ActorAngles.v, ActorDirection.v, 0, 0);

	CVec3		ActorFuturePos(ActorPos);
				ActorFuturePos.ScaleAdd(ActorDirection, FUTURE_PRED_DIST);

	bool		ActorDoTurbo	= false;
	bool		ActorAccelerate	= false;
	bool		ActorAimAtTarget= true;
	float		ActorYawOffset	= 0.0f;


	// Setup Enemy Data
	//------------------
	CVec3		EnemyPos(NPC->enemy->currentOrigin);
	CVec3		EnemyAngles(NPC->enemy->currentAngles);
				EnemyAngles[2]	= 0;
	Vehicle_t*	EnemyVeh		= (NPC->enemy->s.m_iVehicleNum)?(g_entities[NPC->enemy->s.m_iVehicleNum].m_pVehicle):(0);
	bool		EnemyInTurbo	= (EnemyVeh && EnemyVeh->m_iTurboTime>level.time);
	float		EnemySpeed		= (EnemyVeh)?(EnemyVeh->m_pParentEntity->client->ps.speed):(NPC->enemy->resultspeed);
	bool		EnemySlideBreak	= (EnemyVeh && (EnemyVeh->m_ulFlags&VEH_SLIDEBREAKING || EnemyVeh->m_ulFlags&VEH_STRAFERAM));
	bool		EnemyDead		= (NPC->enemy->health<=0);

	bool		ActorFlank		= (NPCInfo->lastAvoidSteerSideDebouncer>level.time && EnemyVeh && EnemySpeed>10.0f);

	CVec3		EnemyDirection;
	CVec3		EnemyRight;
				AngleVectors(EnemyAngles.v, EnemyDirection.v, EnemyRight.v, 0);

	CVec3		EnemyFuturePos(EnemyPos);
				EnemyFuturePos.ScaleAdd(EnemyDirection, FUTURE_PRED_DIST);

	ESide		EnemySide		= ActorPos.LRTest(EnemyPos, EnemyFuturePos);
	CVec3		EnemyFlankPos(EnemyFuturePos);
				EnemyFlankPos.ScaleAdd(EnemyRight, (EnemySide==Side_Right)?(FUTURE_SIDE_DIST):(-FUTURE_SIDE_DIST));

	// Debug Draw Enemy Data
	//-----------------------
	if (false)
	{
		CG_DrawEdge(EnemyPos.v,			EnemyFuturePos.v, EDGE_IMPACT_SAFE);
		CG_DrawEdge(EnemyFuturePos.v,	EnemyFlankPos.v, EDGE_IMPACT_SAFE);
	}


	// Setup Move And Aim Directions
	//-------------------------------
	CVec3		MoveDirection((ActorFlank)?(EnemyFlankPos):(EnemyFuturePos));
				MoveDirection	-= ActorPos;
	float		MoveDistance	= MoveDirection.SafeNorm();
	float		MoveAccuracy	= MoveDirection.Dot(ActorDirection);

	CVec3		AimDirection(EnemyPos);
				AimDirection	-= ActorPos;
	float		AimDistance		= AimDirection.SafeNorm();
	float		AimAccuracy		= AimDirection.Dot(ActorDirection);



	if (!ActorFlank && TIMER_Done(NPC, "FlankAttackCheck"))
	{
		TIMER_Set(NPC, "FlankAttackCheck", Q_irand(1000, 3000));
		if (MoveDistance<4000 && Q_irand(0, 1)==0)
		{
			NPCInfo->lastAvoidSteerSideDebouncer	= level.time + Q_irand(8000, 14000);
		}
	}



	// Fly By Sounds
	//---------------
	if ((ActorVeh->m_pVehicleInfo->soundFlyBy || ActorVeh->m_pVehicleInfo->soundFlyBy2) &&
		EnemyVeh &&
		MoveDistance<800 &&
		ActorSpeed>500.0f &&
		TIMER_Done(NPC, "FlybySoundDebouncer")
		)
	{
		if (EnemySpeed<100.0f || (ActorDirection.Dot(EnemyDirection)*(MoveDistance/800.0f))<-0.5f)
		{
			TIMER_Set(NPC, "FlybySoundDebouncer", 2000);
			int soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy;
			if (ActorVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1)))
			{
				soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy2;
			}
			G_Sound(ActorVeh->m_pParentEntity, soundFlyBy);		
		}
	}



// FLY PAST BEHAVIOR
//===================
 	if (EnemySlideBreak || !TIMER_Done(NPC, "MinHoldDirectionTime"))
	{
		if (TIMER_Done(NPC, "MinHoldDirectionTime"))
		{ 
			TIMER_Set(NPC, "MinHoldDirectionTime", 500);	// Hold For At Least 500 ms
		}
		ActorAccelerate		= true;							// Go
		ActorAimAtTarget	= false;						// Don't Alter Our Aim Direction
		ucmd.buttons		&=~BUTTON_VEH_SPEED;			// Let Normal Vehicle Controls Go
	}


// FLANKING BEHAVIOR
//===================
	else if (ActorFlank)
	{
  		ActorAccelerate	= true;
		ActorDoTurbo	= (MoveDistance>2500 || EnemyInTurbo);
		ucmd.buttons	|= BUTTON_VEH_SPEED;			// Tells PMove to use the ps.speed we calculate here, not the one from g_vehicles.c


		// For Flanking, We Calculate The Speed By Hand, Rather Than Using Pure Accelerate / No Accelerate Functionality
		//---------------------------------------------------------------------------------------------------------------
		NPC->client->ps.speed = ActorVeh->m_pVehicleInfo->speedMax * ((ActorInTurbo)?(1.35f):(1.15f));


		// If In Slowing Distance, Scale Down The Speed As We Approach Our Move Target
		//-----------------------------------------------------------------------------
		if (MoveDistance<ATTACK_FLANK_SLOWING)
		{
			NPC->client->ps.speed *= (MoveDistance/ATTACK_FLANK_SLOWING);
			NPC->client->ps.speed += EnemySpeed;

			// Match Enemy Speed
			//-------------------
			if (NPC->client->ps.speed<5.0f && EnemySpeed<5.0f)
			{
				NPC->client->ps.speed = EnemySpeed;
			}

			// Extra Slow Down When Out In Front
			//-----------------------------------
 			if  (MoveAccuracy<0.0f)
			{
				NPC->client->ps.speed *= (MoveAccuracy + 1.0f);
			}

	
			MoveDirection	*=        (MoveDistance/ATTACK_FLANK_SLOWING);
			EnemyDirection	*= 1.0f - (MoveDistance/ATTACK_FLANK_SLOWING);
			MoveDirection	+= EnemyDirection;

			if (TIMER_Done(NPC, "RamCheck"))
			{
				TIMER_Set(NPC, "RamCheck", Q_irand(1000, 3000));
				if (MoveDistance<RAM_DIST && Q_irand(0, 2)==0)
				{
					VEH_StartStrafeRam(ActorVeh, (EnemySide==Side_Left));
				}
			}
		}
	}


// NORMAL CHASE BEHAVIOR
//=======================
	else
	{
		if (!EnemyVeh && AimAccuracy>0.99f && MoveDistance<500 && !EnemyDead)
		{
			ActorAccelerate = true;
			ActorDoTurbo	= false;
		}
		else
		{
			ActorAccelerate = ((MoveDistance>500 && EnemySpeed>20.0f) || MoveDistance>1000);
			ActorDoTurbo	= (MoveDistance>3000 && EnemySpeed>20.0f);
		}
		ucmd.buttons	&=~BUTTON_VEH_SPEED;
	}




// APPLY RESULTS
//=======================
	// Decide Turbo
	//--------------
	if (ActorDoTurbo || ActorInTurbo)
	{
		ucmd.buttons |= BUTTON_ALT_ATTACK;
	}
	else
	{
		ucmd.buttons &=~BUTTON_ALT_ATTACK;
	}

	// Decide Acceleration
	//---------------------
	ucmd.forwardmove = (ActorAccelerate)?(127):(0);



	// Decide To Shoot
	//-----------------
	ucmd.buttons	&=~BUTTON_ATTACK;
	ucmd.rightmove	= 0;
 	if (AimDistance<2000 && !EnemyDead)
	{
		// If Doing A Ram Attack
		//-----------------------
		if (ActorYawOffset!=0)
		{
			if (NPC->client->ps.weapon!=WP_NONE)
			{
				NPC_ChangeWeapon(WP_NONE);
			}
			ucmd.buttons	&=~BUTTON_ATTACK;
		}
 		else if (AimAccuracy>ATTACK_FWD)
		{
			if (NPC->client->ps.weapon!=WP_NONE)
			{
				NPC_ChangeWeapon(WP_NONE);
			}
			ucmd.buttons	|= BUTTON_ATTACK;
		}
		else if (AimAccuracy<AIM_SIDE && AimAccuracy>-AIM_SIDE)
		{
			if (NPC->client->ps.weapon!=WP_BLASTER)
			{
				NPC_ChangeWeapon(WP_BLASTER);
			}

			if (AimAccuracy<ATTACK_SIDE && AimAccuracy>-ATTACK_SIDE)
			{
				//if (!TIMER_Done(NPC, "RiderAltAttack"))
				//{
				//	ucmd.buttons |= BUTTON_ALT_ATTACK;
				//}
				//else
				//{
                    ucmd.buttons |= BUTTON_ATTACK;

			/*		if (TIMER_Done(NPC, "RiderAltAttackCheck"))
					{
						TIMER_Set(NPC, "RiderAltAttackCheck", Q_irand(1000, 3000));
						if (Q_irand(0, 2)==0)
						{
							TIMER_Set(NPC, "RiderAltAttack", 300);
						}
					}*/
				//}
				WeaponThink(true);
			}
			ucmd.rightmove = (EnemySide==Side_Left)?( 127):(-127);
		}
		else
		{
			if (NPC->client->ps.weapon!=WP_NONE)
			{
				NPC_ChangeWeapon(WP_NONE);
			}
		}
	}
	else
	{
		if (NPC->client->ps.weapon!=WP_NONE)
		{
			NPC_ChangeWeapon(WP_NONE);
		}
	}


	// Aim At Target
	//---------------
	if (ActorAimAtTarget)
	{
		MoveDirection.VecToAng();
		NPCInfo->desiredPitch	= AngleNormalize360(MoveDirection[PITCH]);
		NPCInfo->desiredYaw		= AngleNormalize360(MoveDirection[YAW] + ActorYawOffset);
	}
	NPC_UpdateAngles(qtrue, qtrue);
}
Beispiel #8
0
void RT_FireDecide( void )
{
	qboolean enemyLOS = qfalse;
	qboolean enemyCS = qfalse;
	qboolean enemyInFOV = qfalse;
	//qboolean move = qtrue;
	qboolean faceEnemy = qfalse;
	qboolean shoot = qfalse;
	qboolean hitAlly = qfalse;
	vec3_t	impactPos;
	float	enemyDist;

	if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE 
		&& NPC->client->ps.forceJumpZStart
		&& !PM_FlippingAnim( NPC->client->ps.legsAnim )
		&& !Q_irand( 0, 10 ) )
	{//take off
		RT_FlyStart( NPC );
	}

	if ( !NPC->enemy )
	{
		return;
	}

	VectorClear( impactPos );
	enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );

	vec3_t	enemyDir, shootDir;
	VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, enemyDir );
	VectorNormalize( enemyDir );
	AngleVectors( NPC->client->ps.viewangles, shootDir, NULL, NULL );
	float dot = DotProduct( enemyDir, shootDir );
	if ( dot > 0.5f ||( enemyDist * (1.0f-dot)) < 10000 )
	{//enemy is in front of me or they're very close and not behind me
		enemyInFOV = qtrue;
	}

	if ( enemyDist < MIN_ROCKET_DIST_SQUARED )//128
	{//enemy within 128
		if ( (NPC->client->ps.weapon == WP_FLECHETTE || 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;
			//FIXME: we can never go back to alt-fire this way since, after this, we don't know if we were initially supposed to use alt-fire or not...
		}
	}

	//can we see our target?
	if ( TIMER_Done( NPC, "nextAttackDelay" ) && TIMER_Done( NPC, "flameTime" ) )
	{
		if ( NPC_ClearLOS( NPC->enemy ) )
		{
			NPCInfo->enemyLastSeenTime = level.time;
			enemyLOS = qtrue;

			if ( NPC->client->ps.weapon == WP_NONE )
			{
				enemyCS = qfalse;//not true, but should stop us from firing
			}
			else
			{//can we shoot our target?
				if ( (NPC->client->ps.weapon == WP_ROCKET_LAUNCHER 
					|| (NPC->client->ps.weapon == WP_CONCUSSION && !(NPCInfo->scriptFlags&SCF_ALT_FIRE))
					|| (NPC->client->ps.weapon == WP_FLECHETTE && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist < MIN_ROCKET_DIST_SQUARED )//128*128
				{
					enemyCS = qfalse;//not true, but should stop us from firing
					hitAlly = qtrue;//us!
					//FIXME: if too close, run away!
				}
				else if ( enemyInFOV )
				{//if enemy is FOV, go ahead and check for shooting
					int hit = NPC_ShotEntity( NPC->enemy, impactPos );
					gentity_t *hitEnt = &g_entities[hit];

					if ( hit == NPC->enemy->s.number 
						|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
						|| ( hitEnt && hitEnt->takedamage && ((hitEnt->svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) )
					{//can hit enemy or enemy ally or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway
						enemyCS = qtrue;
						//NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
						VectorCopy( NPC->enemy->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!!!
							hitAlly = 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
				{
					enemyCS = qfalse;//not true, but should stop us from firing
				}
			}
		}
		else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
		{
			NPCInfo->enemyLastSeenTime = level.time;
			faceEnemy = qtrue;
			//NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
		}

		if ( NPC->client->ps.weapon == WP_NONE )
		{
			faceEnemy = qfalse;
			shoot = qfalse;
		}
		else
		{
			if ( enemyLOS )
			{//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
				faceEnemy = qtrue;
			}
			if ( enemyCS )
			{
				shoot = qtrue;
			}
		}

		if ( !enemyCS )
		{//if have a clear shot, always try
			//See if we should continue to fire on their last position
			//!TIMER_Done( NPC, "stick" ) || 
			if ( !hitAlly //we're not going to hit an ally
				&& enemyInFOV //enemy is in our FOV //FIXME: or we don't have a clear LOS?
				&& NPCInfo->enemyLastSeenTime > 0 )//we've seen the enemy
			{
				if ( level.time - NPCInfo->enemyLastSeenTime < 10000 )//we have seem the enemy in the last 10 seconds
				{
					if ( !Q_irand( 0, 10 ) )
					{
						//Fire on the last known position
						vec3_t	muzzle, dir, angles;
						qboolean tooClose = qfalse;
						qboolean tooFar = qfalse;

						CalcEntitySpot( NPC, SPOT_HEAD, muzzle );
						if ( VectorCompare( impactPos, 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 );
							gi.trace( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
							VectorCopy( tr.endpos, impactPos );
						}

						//see if impact would be too close to me
						float distThreshold = 16384/*128*128*/;//default
						switch ( NPC->s.weapon )
						{
						case WP_ROCKET_LAUNCHER:
						case WP_FLECHETTE:
						case WP_THERMAL:
						case WP_TRIP_MINE:
						case WP_DET_PACK:
							distThreshold = 65536/*256*256*/;
							break;
						case WP_REPEATER:
							if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
							{
								distThreshold = 65536/*256*256*/;
							}
							break;
						case WP_CONCUSSION:
							if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
							{
								distThreshold = 65536/*256*256*/;
							}
							break;
						default:
							break;
						}

						float dist = DistanceSquared( impactPos, muzzle );

						if ( dist < distThreshold )
						{//impact would be too close to me
							tooClose = qtrue;
						}
						else if ( level.time - NPCInfo->enemyLastSeenTime > 5000 ||
							(NPCInfo->group && level.time - NPCInfo->group->lastSeenEnemyTime > 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
							switch ( NPC->s.weapon )
							{
							case WP_ROCKET_LAUNCHER:
							case WP_FLECHETTE:
							case WP_THERMAL:
							case WP_TRIP_MINE:
							case WP_DET_PACK:
								distThreshold = 262144/*512*512*/;
								break;
							case WP_REPEATER:
								if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
								{
									distThreshold = 262144/*512*512*/;
								}
								break;
							case WP_CONCUSSION:
								if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
								{
									distThreshold = 262144/*512*512*/;
								}
								break;
							default:
								break;
							}
							dist = DistanceSquared( impactPos, 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];

							shoot = qtrue;
							faceEnemy = qfalse;
						}
					}
				}
			}
		}

		//FIXME: don't shoot right away!
		if ( NPC->client->fireDelay )
		{
			if ( NPC->s.weapon == WP_ROCKET_LAUNCHER
				|| (NPC->s.weapon == WP_CONCUSSION&&!(NPCInfo->scriptFlags&SCF_ALT_FIRE)) )
			{
				if ( !enemyLOS || !enemyCS )
				{//cancel it
					NPC->client->fireDelay = 0;
				}
				else
				{//delay our next attempt
					TIMER_Set( NPC, "nextAttackDelay", Q_irand( 1000, 3000 ) );//FIXME: base on g_spskill
				}
			}
		}
		else if ( shoot )
		{//try to shoot if it's time
			if ( TIMER_Done( NPC, "nextAttackDelay" ) )
			{
				if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
				{
					WeaponThink( qtrue );
				}
				//NASTY
				int altChance = 6;//FIXME: base on g_spskill
				if ( NPC->s.weapon == WP_ROCKET_LAUNCHER )
				{
					if ( (ucmd.buttons&BUTTON_ATTACK) 
						&& !Q_irand( 0, altChance ) )
					{//every now and then, shoot a homing rocket
						ucmd.buttons &= ~BUTTON_ATTACK;
						ucmd.buttons |= BUTTON_ALT_ATTACK;
						NPC->client->fireDelay = Q_irand( 1000, 3000 );//FIXME: base on g_spskill
					}
				}
				else if ( NPC->s.weapon == WP_CONCUSSION )
				{
					if ( (ucmd.buttons&BUTTON_ATTACK) 
						&& Q_irand( 0, altChance*5 ) )
					{//fire the beam shot
						ucmd.buttons &= ~BUTTON_ATTACK;
						ucmd.buttons |= BUTTON_ALT_ATTACK;
						TIMER_Set( NPC, "nextAttackDelay", Q_irand( 1500, 2500 ) );//FIXME: base on g_spskill
					}
					else
					{//fire the rocket-like shot
						TIMER_Set( NPC, "nextAttackDelay", Q_irand( 3000, 5000 ) );//FIXME: base on g_spskill
					}
				}
			}
		}
	}
}
////////////////////////////////////////////////////////////////////////////////////////
// Call This function to make Boba actually shoot his current weapon
////////////////////////////////////////////////////////////////////////////////////////
void	Boba_Fire()
{
	WeaponThink(qtrue);

	// If Actually Fired, Decide To Apply Alt Fire And Calc Next Attack Delay
	//------------------------------------------------------------------------
	if (ucmd.buttons&BUTTON_ATTACK)
	{
		switch (NPC->s.weapon)
		{
		case WP_ROCKET_LAUNCHER:
			TIMER_Set( NPC, "nextAttackDelay", Q_irand(1000, 2000));

			// Occasionally Shoot A Homing Missile
			//-------------------------------------
			if (!Q_irand(0,3))
			{
				ucmd.buttons &= ~BUTTON_ATTACK;
				ucmd.buttons |=  BUTTON_ALT_ATTACK;
				NPC->client->fireDelay = Q_irand( 1000, 3000 );
			}
			break;

		case WP_DISRUPTOR:
			TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 4000));

			// Occasionally Alt-Fire
			//-----------------------
			if (!Q_irand(0,3))
			{
				ucmd.buttons &= ~BUTTON_ATTACK;
				ucmd.buttons |=  BUTTON_ALT_ATTACK;
				NPC->client->fireDelay = Q_irand( 1000, 3000 );
			}
			break;

		case WP_BLASTER:

			if (TIMER_Done(NPC, "nextBlasterAltFireDecide"))
			{
 			 	if (Q_irand(0, (NPC->count*2)+3)>2)
				{
			 		TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(3000, 8000));
					if (!(NPCInfo->scriptFlags&SCF_ALT_FIRE))
					{
						Boba_Printf("ALT FIRE On");
						NPCInfo->scriptFlags |= SCF_ALT_FIRE;
						NPC_ChangeWeapon(WP_BLASTER);			// Update Delay Timers
					}
				}
				else
				{
					TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(2000, 5000));
					if ( (NPCInfo->scriptFlags&SCF_ALT_FIRE))
					{
						Boba_Printf("ALT FIRE Off");
						NPCInfo->scriptFlags &=~SCF_ALT_FIRE;
						NPC_ChangeWeapon(WP_BLASTER);			// Update Delay Timers
					}
				}
			}

			// Occasionally Alt Fire
			//-----------------------
			if (NPCInfo->scriptFlags&SCF_ALT_FIRE)
			{
				ucmd.buttons &= ~BUTTON_ATTACK;
				ucmd.buttons |=  BUTTON_ALT_ATTACK;
			}
			break;
		}
	}
}
Beispiel #10
0
void NPC_BSSniper_Attack( void )
{
	//Don't do anything if we're hurt
	if ( NPC->painDebounceTime > level.time )
	{
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	//NPC_CheckEnemy( qtrue, qfalse );
	//If we don't have an enemy, just idle
	if ( NPC_CheckEnemyExt() == qfalse )//!NPC->enemy )//
	{
		NPC->enemy = NULL;
		NPC_BSSniper_Patrol();//FIXME: or patrol?
		return;
	}

	if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
	{//going to run
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	if ( !NPC->enemy )
	{//WTF?  somehow we lost our enemy?
		NPC_BSSniper_Patrol();//FIXME: or patrol?
		return;
	}

	enemyLOS = enemyCS = qfalse;
	AImove = qtrue;
	faceEnemy = qfalse;
	shoot = qfalse;
	enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
	if ( enemyDist < 16384 )//128 squared
	{//too close, so switch to primary fire
		if ( NPC->client->ps.weapon == WP_DISRUPTOR )
		{//sniping... should be assumed
			if ( NPCInfo->scriptFlags & SCF_ALT_FIRE )
			{//use primary fire
				trace_t	trace;
				gi.trace ( &trace, NPC->enemy->currentOrigin, NPC->enemy->mins, NPC->enemy->maxs, NPC->currentOrigin, NPC->enemy->s.number, NPC->enemy->clipmask, G2_NOCOLLIDE, 0 );
				if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->s.number ) )
				{//he can get right to me
					NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
					//reset fire-timing variables
					NPC_ChangeWeapon( WP_DISRUPTOR );
					NPC_UpdateAngles( qtrue, qtrue );
					return;
				}
			}
			//FIXME: switch back if he gets far away again?
		}
	}
	else if ( enemyDist > 65536 )//256 squared
	{
		if ( NPC->client->ps.weapon == WP_DISRUPTOR )
		{//sniping... should be assumed
			if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
			{//use primary fire
				NPCInfo->scriptFlags |= SCF_ALT_FIRE;
				//reset fire-timing variables
				NPC_ChangeWeapon( WP_DISRUPTOR );
				NPC_UpdateAngles( qtrue, qtrue );
				return;
			}
		}
	}

	Sniper_UpdateEnemyPos();
	//can we see our target?
	if ( NPC_ClearLOS( NPC->enemy ) )//|| (NPCInfo->stats.aim >= 5 && gi.inPVS( NPC->client->renderInfo.eyePoint, NPC->enemy->currentOrigin )) )
	{
		NPCInfo->enemyLastSeenTime = level.time;
		VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation );
		enemyLOS = qtrue;
		float maxShootDist = NPC_MaxDistSquaredForWeapon();
		if ( enemyDist < maxShootDist )
		{
			vec3_t fwd, right, up, muzzle, end;
			trace_t	tr;
			AngleVectors( NPC->client->ps.viewangles, fwd, right, up );
			CalcMuzzlePoint( NPC, fwd, right, up, muzzle, 0 );
			VectorMA( muzzle, 8192, fwd, end );
			gi.trace ( &tr, muzzle, NULL, NULL, end, NPC->s.number, MASK_SHOT, G2_RETURNONHIT, 0 );

			int hit = tr.entityNum;
			//can we shoot our target?
			if ( Sniper_EvaluateShot( hit ) )
			{
				enemyCS = qtrue;
			}
		}
	}
	/*
	else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;
		faceEnemy = qtrue;
	}
	*/

	if ( enemyLOS )
	{//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
		faceEnemy = qtrue;
	}
	if ( enemyCS )
	{
		shoot = qtrue;
	}
	else if ( level.time - NPCInfo->enemyLastSeenTime > 3000 )
	{//Hmm, have to get around this bastard... FIXME: this NPCInfo->enemyLastSeenTime builds up when ducked seems to make them want to run when they uncrouch
		Sniper_ResolveBlockedShot();
	}

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

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

	if ( AImove )
	{//move toward goal
		if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist > 10000 ) )//100 squared
		{
			AImove = Sniper_Move();
		}
		else
		{
			AImove = qfalse;
		}
	}

	if ( !AImove )
	{
		if ( !TIMER_Done( NPC, "duck" ) )
		{
			if ( TIMER_Done( NPC, "watch" ) )
			{//not while watching
				ucmd.upmove = -127;
			}
		}
		//FIXME: what about leaning?
		//FIXME: also, when stop ducking, start looking, if enemy can see me, chance of ducking back down again
	}
	else
	{//stop ducking!
		TIMER_Set( NPC, "duck", -1 );
	}

	if ( TIMER_Done( NPC, "duck" ) 
		&& TIMER_Done( NPC, "watch" ) 
		&& (TIMER_Get( NPC, "attackDelay" )-level.time) > 1000 
		&& NPC->attackDebounceTime < level.time )
	{
		if ( enemyLOS && (NPCInfo->scriptFlags&SCF_ALT_FIRE) )
		{
			if ( NPC->fly_sound_debounce_time < level.time )
			{
				NPC->fly_sound_debounce_time = level.time + 2000;
			}
		}
	}

	if ( !faceEnemy )
	{//we want to face in the dir we're running
		if ( AImove )
		{//don't run away and shoot
			NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
			NPCInfo->desiredPitch = 0;
			shoot = qfalse;
		}
		NPC_UpdateAngles( qtrue, qtrue );
	}
	else// if ( faceEnemy )
	{//face the enemy
		Sniper_FaceEnemy();
	}

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

	//FIXME: don't shoot right away!
	if ( shoot )
	{//try to shoot if it's time
		if ( TIMER_Done( NPC, "attackDelay" ) )
		{
			WeaponThink( qtrue );
			if ( ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK) )
			{
				G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/null.wav" );
			}

			//took a shot, now hide
			if ( !(NPC->spawnflags&SPF_NO_HIDE) && !Q_irand( 0, 1 ) )
			{
				//FIXME: do this if in combat point and combat point has duck-type cover... also handle lean-type cover
				Sniper_StartHide();
			}
			else
			{
				TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time );
			}
		}
	}
}
Beispiel #11
0
void NPC_BSDefault( void ) 
{
//	vec3_t		enemyDir;
//	float		enemyDist;
//	float		shootDist;
//	qboolean	enemyFOV = qfalse;
//	qboolean	enemyShotFOV = qfalse;
//	qboolean	enemyPVS = qfalse;
//	vec3_t		enemyHead;
//	vec3_t		muzzle;
//	qboolean	enemyLOS = qfalse;
//	qboolean	enemyCS = qfalse;
	qboolean	move = qtrue;
//	qboolean	shoot = qfalse;

	
	if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
	{
		WeaponThink( qtrue );
	}

	if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
	{//being forced to walk
		if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START )
		{
			NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD );
		}
	}
	//look for a new enemy if don't have one and are allowed to look, validate current enemy if have one
	NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse, qtrue );
	if ( !NPC->enemy )
	{//still don't have an enemy
		if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
		{//check for alert events
			//FIXME: Check Alert events, see if we should investigate or just look at it
			int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );

			//There is an event to look at
			if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
			{//heard/saw something
				if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
				{//was a big event
					if ( level.alertEvents[alertEvent].owner && level.alertEvents[alertEvent].owner->client && level.alertEvents[alertEvent].owner->health >= 0 && level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
					{//an enemy
						G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
					}
				}
				else
				{//FIXME: investigate lesser events
				}
			}
			//FIXME: also check our allies' condition?
		}
	}

	if ( NPC->enemy && !(NPCInfo->scriptFlags&SCF_FORCED_MARCH) )
	{
		// just use the stormtrooper attack AI...
		NPC_CheckGetNewWeapon();
		if ( NPC->client->leader 
			&& NPCInfo->goalEntity == NPC->client->leader 
			&& !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
		{
			NPC_ClearGoal();
		}
			NPC_BSST_Attack();
		return;
/*
		//have an enemy
		//FIXME: if one of these fails, meaning we can't shoot, do we really need to do the rest?
		VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir );
		enemyDist = VectorNormalize( enemyDir );
		enemyDist *= enemyDist;
		shootDist = NPC_MaxDistSquaredForWeapon();

		enemyFOV = InFOV( NPC->enemy, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov );
		enemyShotFOV = InFOV( NPC->enemy, NPC, 20, 20 );
		enemyPVS = gi.inPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin );

		if ( enemyPVS )
		{//in the pvs
			trace_t	tr;

			CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemyHead );
			enemyHead[2] -= Q_flrand( 0.0f, NPC->enemy->maxs[2]*0.5f );
			CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
			enemyLOS = NPC_ClearLOS( muzzle, enemyHead );

			gi.trace ( &tr, muzzle, vec3_origin, vec3_origin, enemyHead, NPC->s.number, MASK_SHOT );
			enemyCS = NPC_EvaluateShot( tr.entityNum, qtrue );
		}
		else
		{//skip thr 2 traces since they would have to fail
			enemyLOS = qfalse;
			enemyCS = qfalse;
		}
		
		if ( enemyCS && enemyShotFOV )
		{//can hit enemy if we want
			NPC->cantHitEnemyCounter = 0;
		}
		else
		{//can't hit
			NPC->cantHitEnemyCounter++;
		}

		if ( enemyCS && enemyShotFOV && enemyDist < shootDist )
		{//can shoot
			shoot = qtrue;
			if ( NPCInfo->goalEntity == NPC->enemy )
			{//my goal is my enemy and I have a clear shot, no need to chase right now
				move = qfalse;
			}
		}
		else
		{//don't shoot yet, keep chasing
			shoot = qfalse;
			move = qtrue;
		}

		//shoot decision
		if ( !(NPCInfo->scriptFlags&SCF_DONT_FIRE) )
		{//try to shoot
			if ( NPC->enemy )
			{
				if ( shoot )
				{
					if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
					{
						WeaponThink( qtrue );
					}
				}
			}
		}

		//chase decision
		if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
		{//go after him
			NPCInfo->goalEntity = NPC->enemy;
			//FIXME: don't need to chase when have a clear shot and in range?
			if ( !enemyCS && NPC->cantHitEnemyCounter > 60 )
			{//haven't been able to shoot enemy for about 6 seconds, need to do something
				//FIXME: combat points?  Just chase?
				if ( enemyPVS )
				{//in my PVS, just pick a combat point
					//FIXME: implement
				}
				else
				{//just chase him
				}
			}
			//FIXME: in normal behavior, should we use combat Points?  Do we care?  Is anyone actually going to ever use this AI?
		}
		else if ( NPC->cantHitEnemyCounter > 60 )
		{//pick a new one
			NPC_CheckEnemy( qtrue, qfalse, qtrue );
		}
		
		if ( enemyPVS && enemyLOS )//&& !enemyShotFOV )
		{//have a clear LOS to him//, but not looking at him
			//Find the desired angles
			vec3_t	angles;

			GetAnglesForDirection( muzzle, enemyHead, angles );

			NPCInfo->desiredYaw		= AngleNormalize180( angles[YAW] );
			NPCInfo->desiredPitch	= AngleNormalize180( angles[PITCH] );
		}
		*/
	}

	if ( UpdateGoal() )
	{//have a goal
		if ( !NPC->enemy 
			&& NPC->client->leader 
			&& NPCInfo->goalEntity == NPC->client->leader 
			&& !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
		{
			NPC_BSFollowLeader();
		}
		else
		{
			//set angles
			if ( (NPCInfo->scriptFlags & SCF_FACE_MOVE_DIR) || NPCInfo->goalEntity != NPC->enemy )
			{//face direction of movement, NOTE: default behavior when not chasing enemy
				NPCInfo->combatMove = qfalse;
			}
			else
			{//face goal.. FIXME: what if have a navgoal but want to face enemy while moving?  Will this do that?
				vec3_t	dir, angles;

				NPCInfo->combatMove = qfalse;

				VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir );
				vectoangles( dir, angles );
				NPCInfo->desiredYaw = angles[YAW];
				if ( NPCInfo->goalEntity == NPC->enemy )
				{
					NPCInfo->desiredPitch = angles[PITCH];
				}
			}

			//set movement
			//override default walk/run behavior
			//NOTE: redundant, done in NPC_ApplyScriptFlags
			if ( NPCInfo->scriptFlags & SCF_RUNNING )
			{
				ucmd.buttons &= ~BUTTON_WALKING;
			}
			else if ( NPCInfo->scriptFlags & SCF_WALKING )
			{
				ucmd.buttons |= BUTTON_WALKING;
			}
			else if ( NPCInfo->goalEntity == NPC->enemy )
			{
				ucmd.buttons &= ~BUTTON_WALKING;
			}
			else
			{
				ucmd.buttons |= BUTTON_WALKING;
			}

			if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
			{//being forced to walk
				//if ( g_crosshairEntNum != NPC->s.number )
				if (!NPC_SomeoneLookingAtMe(NPC))
				{//don't walk if player isn't aiming at me
					move = qfalse;
				}
			}

			if ( move )
			{
				//move toward goal
				NPC_MoveToGoal( qtrue );
			}
		}
	}
	else if ( !NPC->enemy && NPC->client->leader )
	{
		NPC_BSFollowLeader();
	}

	//update angles
	NPC_UpdateAngles( qtrue, qtrue );
}
Beispiel #12
0
void NPC_BSPointShoot (qboolean shoot)
{//FIXME: doesn't check for clear shot...
	vec3_t	muzzle, dir, angles, org;
	//spot_t	spot_enemy = SPOT_CHEST;

	if ( !NPC->enemy || !NPC->enemy->inuse || (NPC->enemy->NPC && NPC->enemy->health <= 0) )
	{//FIXME: should still keep shooting for a second or two after they actually die...
		trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
		goto finished;
		return;
	}

	CalcEntitySpot(NPC, SPOT_WEAPON, muzzle);
	CalcEntitySpot(NPC->enemy, SPOT_HEAD, org);//Was spot_org
	//Head is a little high, so let's aim for the chest:
	//if ( NPC->enemy->client )
	//{
	//	org[2] -= 12;//NOTE: is this enough?
	//}

	VectorSubtract(org, muzzle, dir);
	vectoangles(dir, angles);

	switch( NPC->client->ps.weapon )
	{
	case WP_NONE:
//	case WP_TRICORDER:
	case WP_STUN_BATON:
	case WP_SABER:
		//don't do any pitch change if not holding a firing weapon
		break;
	default:
		NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]);
		break;
	}

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

	if ( NPC_UpdateAngles ( qtrue, qtrue ) )
	{//FIXME: if angles clamped, this may never work!
		//NPCInfo->shotTime = NPC->attackDebounceTime = 0;

		if ( shoot )
		{//FIXME: needs to hold this down if using a weapon that requires it, like phaser...
			//ucmd.buttons |= BUTTON_ATTACK;
			WeaponThink( qtrue );
		}
		
		//if ( !shoot || !(NPC->svFlags & SVF_LOCKEDENEMY) )
		if (1)
		{//If locked_enemy is on, dont complete until it is destroyed...
			trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
			goto finished;
		}
	}
	//else if ( shoot && (NPC->svFlags & SVF_LOCKEDENEMY) )
	if (0)
	{//shooting them till their dead, not aiming right at them yet...
		/*
		qboolean movingTarget = qfalse;

		if ( NPC->enemy->client )
		{
			if ( VectorLengthSquared( NPC->enemy->client->ps.velocity ) )
			{
				movingTarget = qtrue;
			}
		}
		else if ( VectorLengthSquared( NPC->enemy->s.pos.trDelta ) )
		{
			movingTarget = qtrue;
		}

		if (movingTarget )
		*/
		{
			float	dist = VectorLength( dir );
			float	yawMiss, yawMissAllow = NPC->enemy->r.maxs[0];
			float	pitchMiss, pitchMissAllow = (NPC->enemy->r.maxs[2] - NPC->enemy->r.mins[2])/2;
			
			if ( yawMissAllow < 8.0f )
			{
				yawMissAllow = 8.0f;
			}

			if ( pitchMissAllow < 8.0f )
			{
				pitchMissAllow = 8.0f;
			}

			yawMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw ))) * dist;
			pitchMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[PITCH], NPCInfo->desiredPitch))) * dist;

			if ( yawMissAllow >= yawMiss && pitchMissAllow > pitchMiss )
			{
				ucmd.buttons |= BUTTON_ATTACK;
			}
		}
	}
	
	return;
		
finished:
	NPCInfo->desiredYaw = client->ps.viewangles[YAW];
	NPCInfo->desiredPitch = client->ps.viewangles[PITCH];

	NPCInfo->aimTime = 0;//ok to turn normally now
}
Beispiel #13
0
void NPC_BSGrenadier_Attack( void )
{
	//Don't do anything if we're hurt
	if ( NPC->painDebounceTime > level.time )
	{
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	//NPC_CheckEnemy( qtrue, qfalse );
	//If we don't have an enemy, just idle
	if ( NPC_CheckEnemyExt() == qfalse )//!NPC->enemy )//
	{
		NPC->enemy = NULL;
		NPC_BSGrenadier_Patrol();//FIXME: or patrol?
		return;
	}

	if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
	{//going to run
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}

	if ( !NPC->enemy )
	{//WTF?  somehow we lost our enemy?
		NPC_BSGrenadier_Patrol();//FIXME: or patrol?
		return;
	}

	enemyLOS = enemyCS = qfalse;
	move = qtrue;
	faceEnemy = qfalse;
	shoot = qfalse;
	enemyDist = DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin );

	//See if we should switch to melee attack
	if ( enemyDist < 16384 && (!NPC->enemy->client||NPC->enemy->client->ps.weapon != WP_SABER||!NPC->enemy->client->ps.saberActive) )//128
	{//enemy is close and not using saber
		if ( NPC->client->ps.weapon == WP_THERMAL )
		{//grenadier
			trace_t	trace;
			gi.trace ( &trace, NPC->currentOrigin, NPC->enemy->mins, NPC->enemy->maxs, NPC->enemy->currentOrigin, NPC->s.number, NPC->enemy->clipmask );
			if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->enemy->s.number ) )
			{//I can get right to him
				//reset fire-timing variables
				NPC_ChangeWeapon( WP_MELEE );
				if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//NPCInfo->behaviorState == BS_STAND_AND_SHOOT )
				{//FIXME: should we be overriding scriptFlags?
					NPCInfo->scriptFlags |= SCF_CHASE_ENEMIES;//NPCInfo->behaviorState = BS_HUNT_AND_KILL;
				}
			}
		}
	}
	else if ( enemyDist > 65536 || (NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && NPC->enemy->client->ps.saberActive) )//256
	{//enemy is far or using saber
		if ( NPC->client->ps.weapon == WP_MELEE && (NPC->client->ps.stats[STAT_WEAPONS]&(1<<WP_THERMAL)) )
		{//fisticuffs, make switch to thermal if have it
			//reset fire-timing variables
			NPC_ChangeWeapon( WP_THERMAL );
		}
	}

	//can we see our target?
	if ( NPC_ClearLOS( NPC->enemy ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;
		enemyLOS = qtrue;

		if ( NPC->client->ps.weapon == WP_MELEE )
		{
			if ( enemyDist <= 4096 && InFOV( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 90, 45 ) )//within 64 & infront
			{
				VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation );
				enemyCS = qtrue;
			}
		}
		else if ( InFOV( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 45, 90 ) )
		{//in front of me 
			//can we shoot our target?
			//FIXME: how accurate/necessary is this check?
			int hit = NPC_ShotEntity( NPC->enemy );
			gentity_t *hitEnt = &g_entities[hit];
			if ( hit == NPC->enemy->s.number 
				|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) )
			{
				VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation );
				float enemyHorzDist = DistanceHorizontalSquared( NPC->enemy->currentOrigin, NPC->currentOrigin );
				if ( enemyHorzDist < 1048576 )
				{//within 1024
					enemyCS = qtrue;
					NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
				}
				else
				{
					NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
				}
			}
		}
	}
	else
	{
		NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
	}
	/*
	else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;
		faceEnemy = qtrue;
	}
	*/

	if ( enemyLOS )
	{//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
		faceEnemy = qtrue;
	}

	if ( enemyCS )
	{
		shoot = qtrue;
		if ( NPC->client->ps.weapon == WP_THERMAL )
		{//don't chase and throw
			move = qfalse;
		}
		else if ( NPC->client->ps.weapon == WP_MELEE && enemyDist < (NPC->maxs[0]+NPC->enemy->maxs[0]+16)*(NPC->maxs[0]+NPC->enemy->maxs[0]+16) )
		{//close enough
			move = qfalse;
		}
	}//this should make him chase enemy when out of range...?

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

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

	if ( move )
	{//move toward goal
		if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist > 10000 ) )//100 squared
		{
			move = Grenadier_Move();
		}
		else
		{
			move = qfalse;
		}
	}

	if ( !move )
	{
		if ( !TIMER_Done( NPC, "duck" ) )
		{
			ucmd.upmove = -127;
		}
		//FIXME: what about leaning?
	}
	else
	{//stop ducking!
		TIMER_Set( NPC, "duck", -1 );
	}

	if ( !faceEnemy )
	{//we want to face in the dir we're running
		if ( move )
		{//don't run away and shoot
			NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
			NPCInfo->desiredPitch = 0;
			shoot = qfalse;
		}
		NPC_UpdateAngles( qtrue, qtrue );
	}
	else// if ( faceEnemy )
	{//face the enemy
		NPC_FaceEnemy();
	}

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

	//FIXME: don't shoot right away!
	if ( shoot )
	{//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 );
				TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time );
			}
			
		}
	}
}
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;
	}

	//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.torsoAnimTimer <= 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.legsAnimTimer += 500;
				NPC->client->ps.torsoAnimTimer += 500;
			}
		}
		else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START )
		{
			if ( NPC->client->ps.torsoAnimTimer <= 500 )
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsAnimTimer += 500;
				NPC->client->ps.torsoAnimTimer += 500;
			}
		}
		else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE )
		{
			if ( NPC->client->ps.torsoAnimTimer <= 500 )
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsAnimTimer += 500;
				NPC->client->ps.torsoAnimTimer += 500;
			}
		}
		else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP )
		{
			if ( NPC->client->ps.torsoAnimTimer <= 500 )
			{
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				NPC->client->ps.legsAnimTimer = -1;
				NPC->client->ps.torsoAnimTimer = -1;
			}
		}
		else if ( NPC->wait )
		{
			if ( TIMER_Done( NPC, "gloatTime" ) )
			{
				GM_StartGloat();
			}
			else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->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;
	}
	//If we don't have an enemy, just idle
	if ( NPC_CheckEnemyExt() == qfalse || !NPC->enemy )
	{
		NPC->enemy = NULL;
		NPC_BSGM_Patrol();
		return;
	}

	enemyLOS = enemyCS = qfalse;
	bMove = qtrue;
	faceEnemy = qfalse;
	shoot = qfalse;
	hitAlly = qfalse;
	VectorClear( impactPos );
	enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );

	if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 ||
		NPC->client->ps.torsoAnim == BOTH_ATTACK5 )
	{
		shoot = qfalse;
		if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime )
		{//time to smack
			//recheck enemyDist and InFront
			if ( enemyDist < MELEE_DIST_SQUARED && InFront( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.3f ) )
			{
				vec3_t	smackDir;
				VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, smackDir );
				smackDir[2] += 30;
				VectorNormalize( smackDir );
				//hurt them
				G_Sound( NPC->enemy, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) );
				G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->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 ( PM_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
		shoot = qfalse;
		if ( NPC->lockCount == 1 )
		{//charging up
			if ( TIMER_Done( NPC, "beamDelay" ) )
			{//time to start the beam
				int laserAnim;
				if ( Q_irand( 0, 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.torsoAnimTimer + Q_irand( 1000, 3000 ) );
				//turn on beam effect
				NPC->lockCount = 2;
				G_PlayEffect( "galak/trace_beam", NPC->s.number );
				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->svFlags |= SVF_BROADCAST;
						NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
					}
				}
			}
		}
		else
		{//in the actual attack now
			if ( !NPC->client->ps.torsoAnimTimer )
			{//attack done!
				NPC->lockCount = 0;
				G_FreeEntity( NPCInfo->coverTarg );
				NPC->s.loopSound = 0;
				NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer );
			}
			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 );
				gi.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_SoundAtSpot( trace.endpos, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
							G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_ENERGY );
						}
					}
					if ( NPCInfo->coverTarg )
					{
						G_SetOrigin( NPCInfo->coverTarg, trace.endpos );
					}
					if ( !Q_irand( 0, 5 ) )
					{
						G_SoundAtSpot( trace.endpos, 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 )||enemyDist < MAX_LOB_DIST_SQUARED) )
			{//shield down, use laser
				NPC_GM_StartLaser();
			}
		}
		else*/
		if ( !NPC->client->ps.powerups[PW_GALAK_SHIELD] 
			&& enemyDist < MELEE_DIST_SQUARED 
			&& InFront( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.3f ) 
			&& G_StandardHumanoid( NPC->enemy->NPC_type ) )//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;
				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
				}
				//FIXME: swing sound
				NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
				TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer + 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->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.3f )
			&& ((!Q_irand( 0, 10*(2-g_spskill->integer))&& enemyDist > MIN_LOB_DIST_SQUARED&& enemyDist < 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
			shoot = qfalse;
			NPC_GM_StartLaser();
		}
		else if ( enemyDist < 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 ( (enemyDist > 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_ClearLOS( NPC->enemy ) )
	{
		NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS
		enemyLOS = qtrue;

		if ( NPC->client->ps.weapon == WP_NONE )
		{
			enemyCS = 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))) && enemyDist < MIN_LOB_DIST_SQUARED )//256
			{
				enemyCS = qfalse;//not true, but should stop us from firing
				hitAlly = qtrue;//us!
				//FIXME: if too close, run away!
			}
			else
			{
				int hit = NPC_ShotEntity( NPC->enemy, impactPos );
				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
					enemyCS = qtrue;
					NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
					VectorCopy( NPC->enemy->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!!!
						hitAlly = 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 ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
	{
		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;

		int hit = NPC_ShotEntity( NPC->enemy, impactPos );
		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
			enemyCS = qtrue;
		}
		else
		{
			faceEnemy = qtrue;
			NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
		}
	}

	if ( enemyLOS )
	{
		faceEnemy = qtrue;
	}
	else
	{
		if ( !NPCInfo->goalEntity )
		{
			NPCInfo->goalEntity = NPC->enemy;
		}
		if ( NPCInfo->goalEntity == NPC->enemy )
		{//for now, always chase the enemy
			bMove = qtrue;
		}
	}
	if ( enemyCS )
	{
		shoot = 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
			bMove = 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) && shoot && 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};

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

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

		//Find the desired angles
		qboolean 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&&enemyLOS&&enemyCS)  )
		{//no clear lob shot and no lob shot that will hit something breakable
			if ( enemyLOS && enemyCS && 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
			{
				shoot = 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 ( faceEnemy )
	{//face the enemy
		NPC_FaceEnemy( qtrue );
	}

	if ( !TIMER_Done( NPC, "standTime" ) )
	{
		bMove = 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 bMove
			bMove = qfalse;
		}
	}

	if ( bMove && !NPC->lockCount )
	{//bMove 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 )
		{
			bMove = GM_Move();
		}
		else
		{
			bMove = qfalse;
		}
	}

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

	//FIXME: check scf_face_move_dir here?

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

	if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
	{
		shoot = 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)
			shoot = qfalse;
		}
	}
	//FIXME: don't shoot right away!
	if ( shoot )
	{//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->absmin, NPC->absmax, NPC->enemy->absmin, NPC->enemy->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 )
			{
				NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
				G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_ELECTROCUTE ); 
			}
			else
			{
				G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->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 )
		{//zap him!
			//animate me
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK6, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
			TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer );
			TIMER_Set( NPC, "standTime", NPC->client->ps.legsAnimTimer );
			//FIXME: debounce this?
			NPCInfo->touchedByPlayer = NULL;
			//FIXME: some shield effect?
			NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
			vec3_t	smackDir;
			VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, smackDir );
			smackDir[2] += 30;
			VectorNormalize( smackDir );
			G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->currentOrigin, (g_spskill->integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_ELECTROCUTE ); 
			//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;
			}
			//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;
			}
		}
	}
}
Beispiel #15
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 )
		{//start regenerating the armor
			gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_OFF );
			NPC->flags &= ~FL_SHIELDED;//no more reflections
			VectorSet( NPC->mins, -20, -20, -24 );
			VectorSet( NPC->maxs, 20, 20, 64 );
			NPC->client->crouchheight = NPC->client->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;
			gi.trace( &tr, NPC->currentOrigin, shieldMins, shieldMaxs, NPC->currentOrigin, NPC->s.number, NPC->clipmask );
			if ( !tr.startsolid )
			{
				VectorCopy( shieldMins, NPC->mins );
				VectorCopy( shieldMaxs, NPC->maxs );
				NPC->client->crouchheight = NPC->client->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;
				gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_ON );
			}
		}
	}
	if ( NPC->client->ps.stats[STAT_ARMOR] > 0 )
	{//armor present
		NPC->client->ps.powerups[PW_GALAK_SHIELD] = Q3_INFINITE;//temp, for effect
		gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_ON );
	}
	else
	{
		gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_OFF );
	}

	if( !NPC->enemy )
	{//don't have an enemy, look for one
		NPC_BSGM_Patrol();
	}
	else //if ( NPC->enemy )
	{//have an enemy
		NPC_BSGM_Attack();
	}
}
Beispiel #16
0
void NPC_BSGM_Default( void )
{
	if( NPCS.NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
	{
		WeaponThink( qtrue );
	}

	if ( NPCS.NPC->client->ps.stats[STAT_ARMOR] <= 0 )
	{//armor gone
	//	if ( !NPCInfo->investigateDebounceTime )
		if (0)
		{//start regenerating the armor
			NPC_SetSurfaceOnOff( NPCS.NPC, "torso_shield", TURN_OFF );
			NPCS.NPC->flags &= ~FL_SHIELDED;//no more reflections
			VectorSet( NPCS.NPC->r.mins, -20, -20, -24 );
			VectorSet( NPCS.NPC->r.maxs, 20, 20, 64 );
			NPCS.NPC->client->ps.crouchheight = NPCS.NPC->client->ps.standheight = 64;
			if ( NPCS.NPC->locationDamage[HL_GENERIC1] < GENERATOR_HEALTH )
			{//still have the generator bolt-on
				if ( NPCS.NPCInfo->investigateCount < 12 )
				{
					NPCS.NPCInfo->investigateCount++;
				}
				NPCS.NPCInfo->investigateDebounceTime = level.time + (NPCS.NPCInfo->investigateCount * 5000);
			}
		}
		else if ( NPCS.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, NPCS.NPC->r.currentOrigin, shieldMins, shieldMaxs, NPCS.NPC->r.currentOrigin, NPCS.NPC->s.number, NPCS.NPC->clipmask, qfalse, 0, 0 );
			if ( !tr.startsolid )
			{
				VectorCopy( shieldMins, NPCS.NPC->r.mins );
				VectorCopy( shieldMaxs, NPCS.NPC->r.maxs );
				NPCS.NPC->client->ps.crouchheight = NPCS.NPC->client->ps.standheight = shieldMaxs[2];
				NPCS.NPC->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH;
				NPCS.NPCInfo->investigateDebounceTime = 0;
				NPCS.NPC->flags |= FL_SHIELDED;//reflect normal shots
			//	NPC->fx_time = level.time;
				NPC_SetSurfaceOnOff( NPCS.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( !NPCS.NPC->enemy )
	{//don't have an enemy, look for one
		NPC_BSGM_Patrol();
	}
	else //if ( NPC->enemy )
	{//have an enemy
		NPC_BSGM_Attack();
	}
}