Exemplo n.º 1
0
void NPC_PlayConfusionSound( gentity_t *self )
{
	if ( self->health > 0 )
	{
		if ( self->enemy ||//was mad
				!TIMER_Done( self, "enemyLastVisible" ) ||//saw something suspicious
				self->client->renderInfo.lookTarget	== 0//was looking at player
			)
		{
			self->NPC->blockedSpeechDebounceTime = 0;//make sure we say this
			G_AddVoiceEvent( self, Q_irand( EV_CONFUSE2, EV_CONFUSE3 ), 2000 );
		}
		else if ( self->NPC && self->NPC->investigateDebounceTime+self->NPC->pauseTime > level.time )//was checking something out
		{
			self->NPC->blockedSpeechDebounceTime = 0;//make sure we say this
			G_AddVoiceEvent( self, EV_CONFUSE1, 2000 );
		}
		//G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
	}
	//reset him to be totally unaware again
	TIMER_Set( self, "enemyLastVisible", 0 );
	self->NPC->tempBehavior = BS_DEFAULT;
	
	//self->NPC->behaviorState = BS_PATROL;
	G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?

	self->NPC->investigateCount = 0;
}
Exemplo n.º 2
0
//[CoOp]
//[SPPortComplete]
void G_CheckCharmed( gentity_t *self )
{
	if ( self
		&& self->client
		&& self->client->playerTeam == NPCTEAM_PLAYER 
		&& self->NPC
		&& self->NPC->charmedTime 
		&& (self->NPC->charmedTime < level.time ||self->health <= 0) )
	{//we were charmed, set us back!
		//NOTE: presumptions here...
		team_t	savTeam = self->client->enemyTeam;
		self->client->enemyTeam = self->client->playerTeam;
		self->client->playerTeam = savTeam;
		self->client->leader = NULL;
		self->NPC->charmedTime = 0;
		if ( self->health > 0 )
		{
			if ( self->NPC->tempBehavior == BS_FOLLOW_LEADER )
			{
				self->NPC->tempBehavior = BS_DEFAULT;
			}
			G_ClearEnemy( self );
			//say something to let player know you've snapped out of it
			G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
		}
	}

}
void NPC_Respond( gentity_t *self, int userNum )
{
	int event;

	if ( self->NPC->behaviorState == BS_FORMATION )
	{
		event = Q_irand(EV_MISSION1, EV_MISSION3);
	}
	else
	{
		if ( Q_irand( 0, 1 ) )
		{
			event = Q_irand(EV_RESPOND1, EV_RESPOND3);
		}
		else
		{
			event = Q_irand(EV_BUSY1, EV_BUSY3);
		}

		if( !Q_irand( 0, 1 ) )
		{//set looktarget to them for a second or two
			NPC_TempLookTarget( self, userNum, 1000, 3000 );
		}
	}

	G_AddVoiceEvent( self, event, 3000 );
}
Exemplo n.º 4
0
void NPC_Grenadier_Pain( gentity_t *self, gentity_t *attacker, int damage ) {
	self->NPC->localState = LSTATE_UNDERFIRE;

	TIMER_Set( self, "duck", -1 );
	TIMER_Set( self, "stand", 2000 );

	NPC_Pain( self, attacker, damage );

	if ( !damage && self->health > 0 ) {//FIXME: better way to know I was pushed
		G_AddVoiceEvent( self, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 2000 );
	}
}
Exemplo n.º 5
0
void NPC_Sniper_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, vec3_t point, int damage, int mod ) 
{
	self->NPC->localState = LSTATE_UNDERFIRE;

	TIMER_Set( self, "duck", -1 );
	TIMER_Set( self, "stand", 2000 );

	NPC_Pain( self, inflictor, other, point, damage, mod );

	if ( !damage && self->health > 0 )
	{//FIXME: better way to know I was pushed
		G_AddVoiceEvent( self, Q_irand(EV_PUSHED1, EV_PUSHED3), 2000 );
	}
}
Exemplo n.º 6
0
void NPC_Grenadier_PlayConfusionSound( gentity_t *self ) {//FIXME: make this a custom sound in sound set
	if ( self->health > 0 ) {
		G_AddVoiceEvent( self, Q_irand( EV_CONFUSE1, EV_CONFUSE3 ), 2000 );
	}
	//reset him to be totally unaware again
	TIMER_Set( self, "enemyLastVisible", 0 );
	TIMER_Set( self, "flee", 0 );
	self->NPC->squadState = SQUAD_IDLE;
	self->NPC->tempBehavior = BS_DEFAULT;

	//self->NPC->behaviorState = BS_PATROL;
	G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?

	self->NPC->investigateCount = 0;
}
Exemplo n.º 7
0
void NPC_CheckCharmed( void )
{
	if ( NPCInfo->charmedTime && NPCInfo->charmedTime < level.time && NPC->client )
	{//we were charmed, set us back!
		NPC->client->playerTeam = NPC->genericValue1;
		NPC->client->enemyTeam = NPC->genericValue2;
		NPC->s.teamowner = NPC->genericValue3;

		NPC->client->leader = NULL;
		if ( NPCInfo->tempBehavior == BS_FOLLOW_LEADER )
		{
			NPCInfo->tempBehavior = BS_DEFAULT;
		}
		G_ClearEnemy( NPC );
		NPCInfo->charmedTime = 0;
		//say something to let player know you've snapped out of it
		G_AddVoiceEvent( NPC, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
	}
}
Exemplo n.º 8
0
void NPC_CheckCharmed( void )
{
	if ( NPC->client->playerTeam == TEAM_PLAYER && NPCInfo->charmedTime && NPCInfo->charmedTime < level.time && NPC->client )
	{//we were charmed, set us back!
		//NOTE: presumptions here...
		team_t	savTeam = NPC->client->enemyTeam;
		NPC->client->enemyTeam = NPC->client->playerTeam;
		NPC->client->playerTeam = savTeam;
		NPC->client->leader = NULL;
		if ( NPCInfo->tempBehavior == BS_FOLLOW_LEADER )
		{
			NPCInfo->tempBehavior = BS_DEFAULT;
		}
		G_ClearEnemy( NPC );
		NPCInfo->charmedTime = 0;
		//say something to let player know you've snapped out of it
		G_AddVoiceEvent( NPC, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
	}

}
Exemplo n.º 9
0
void NPC_HandleAIFlags (void)
{
	//FIXME: make these flags checks a function call like NPC_CheckAIFlagsAndTimers
	if ( NPCInfo->aiFlags & NPCAI_LOST )
	{//Print that you need help!
		//FIXME: shouldn't remove this just yet if cg_draw needs it
		NPCInfo->aiFlags &= ~NPCAI_LOST;
		
		if ( NPCInfo->goalEntity && NPCInfo->goalEntity == NPC->enemy )
		{//We can't nav to our enemy
			//Drop enemy and see if we should search for him
			NPC_LostEnemyDecideChase();
		}
				}

	//been told to play a victory sound after a delay
	if ( NPCInfo->greetingDebounceTime && NPCInfo->greetingDebounceTime < level.time )
	{
		G_AddVoiceEvent( NPC, Q_irand(EV_VICTORY1, EV_VICTORY3), Q_irand( 2000, 4000 ) );
		NPCInfo->greetingDebounceTime = 0;
	}

	if ( NPCInfo->ffireCount > 0 )
	{
		if ( NPCInfo->ffireFadeDebounce < level.time )
		{
			NPCInfo->ffireCount--;
			NPCInfo->ffireFadeDebounce = level.time + 3000;
		}
	}
	if ( d_patched.integer )
	{//use patch-style navigation
		if ( NPCInfo->consecutiveBlockedMoves > 20 )
		{//been stuck for a while, try again?
			NPCInfo->consecutiveBlockedMoves = 0;
		}
	}
}
Exemplo n.º 10
0
void NPC_Surrender( void )
{//FIXME: say "don't shoot!" if we weren't already surrendering
	if ( NPC->client->ps.weaponTime || PM_InKnockDown( &NPC->client->ps ) )
	{
		return;
	}
	if ( NPC->s.weapon != WP_NONE && 
		NPC->s.weapon != WP_MELEE &&
		NPC->s.weapon != WP_SABER )
	{
		WP_DropWeapon( NPC, NULL );
	}
	if ( NPCInfo->surrenderTime < level.time - 5000 )
	{//haven't surrendered for at least 6 seconds, tell them what you're doing
		//FIXME: need real dialogue EV_SURRENDER
		NPCInfo->blockedSpeechDebounceTime = 0;//make sure we say this
		G_AddVoiceEvent( NPC, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 3000 );
	}
	NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE );
	NPC->client->ps.torsoAnimTimer = 1000;
	NPCInfo->surrenderTime = level.time + 1000;//stay surrendered for at least 1 second
	//FIXME: while surrendering, make a big sight/sound alert? Or G_AlertTeam?
}
Exemplo n.º 11
0
void NPC_Respond( gentity_t *self, int userNum )
{
	int event = -1;
	/*

	if ( Q_irand( 0, 1 ) )
	{
		event = Q_irand(EV_RESPOND1, EV_RESPOND3);
	}
	else
	{
		event = Q_irand(EV_BUSY1, EV_BUSY3);
	}
	*/

	if ( !Q_irand( 0, 1 ) )
	{//set looktarget to them for a second or two
		NPC_TempLookTarget( self, userNum, 1000, 3000 );
	}

	//some last-minute hacked in responses
	switch ( self->client->NPC_class )
	{
	case CLASS_JAN:
		if ( self->enemy )
		{
			if ( !Q_irand( 0, 2 ) )
			{
				event = Q_irand( EV_CHASE1, EV_CHASE3 );
			}
			else if ( Q_irand( 0, 1 ) )
			{
				event = Q_irand( EV_OUTFLANK1, EV_OUTFLANK2 );
			}
			else
			{
				event = Q_irand( EV_COVER1, EV_COVER5 );
			}
		}
		else if ( !Q_irand( 0, 2 ) )
		{
			event = EV_SUSPICIOUS4;
		}
		else if ( !Q_irand( 0, 1 ) )
		{
			event = EV_SOUND1;
		}
		else
		{
			event = EV_CONFUSE1;
		}
		break;
	case CLASS_LANDO:
		if ( self->enemy )
		{
			if ( !Q_irand( 0, 2 ) )
			{
				event = Q_irand( EV_CHASE1, EV_CHASE3 );
			}
			else if ( Q_irand( 0, 1 ) )
			{
				event = Q_irand( EV_OUTFLANK1, EV_OUTFLANK2 );
			}
			else
			{
				event = Q_irand( EV_COVER1, EV_COVER5 );
			}
		}
		else if ( !Q_irand( 0, 6 ) )
		{
			event = EV_SIGHT2;
		}
		else if ( !Q_irand( 0, 5 ) )
		{
			event = EV_GIVEUP4;
		}
		else if ( Q_irand( 0, 4 ) > 1 )
		{
			event = Q_irand( EV_SOUND1, EV_SOUND3 );
		}
		else
		{
			event = Q_irand( EV_JDETECTED1, EV_JDETECTED2 );
		}
		break;
	case CLASS_LUKE:
		if ( self->enemy )
		{
			event = EV_COVER1;
		}
		else
		{
			event = Q_irand( EV_SOUND1, EV_SOUND3 );
		}
		break;
	case CLASS_JEDI:
	case CLASS_KYLE:
		if ( !self->enemy )
		{
			/*
			if ( !(self->svFlags&SVF_IGNORE_ENEMIES)
				&& (self->NPC->scriptFlags&SCF_LOOK_FOR_ENEMIES)
				&& self->client->enemyTeam == TEAM_ENEMY )
			{
				event = Q_irand( EV_ANGER1, EV_ANGER3 );
			}
			else
			*/
			{
				event = Q_irand( EV_CONFUSE1, EV_CONFUSE3 );
			}
		}
		break;
	case CLASS_PRISONER:
		if ( self->enemy )
		{
			if ( Q_irand( 0, 1 ) )
			{
				event = Q_irand( EV_CHASE1, EV_CHASE3 );
			}
			else
			{
				event = Q_irand( EV_OUTFLANK1, EV_OUTFLANK2 );
			}
		}
		else
		{
			event = Q_irand( EV_SOUND1, EV_SOUND3 );
		}
		break;
	case CLASS_REBEL:
		if ( self->enemy )
		{
			if ( !Q_irand( 0, 2 ) )
			{
				event = Q_irand( EV_CHASE1, EV_CHASE3 );
			}
			else
			{
				event = Q_irand( EV_DETECTED1, EV_DETECTED5 );
			}
		}
		else
		{
			event = Q_irand( EV_SOUND1, EV_SOUND3 );
		}
		break;
	case CLASS_BESPIN_COP:
		if ( !Q_stricmp( "bespincop", self->NPC_type ) )
		{//variant 1
			if ( self->enemy )
			{
				if ( Q_irand( 0, 9 ) > 6 )
				{
					event = Q_irand( EV_CHASE1, EV_CHASE3 );
				}
				else if ( Q_irand( 0, 6 ) > 4 )
				{
					event = Q_irand( EV_OUTFLANK1, EV_OUTFLANK2 );
				}
				else
				{
					event = Q_irand( EV_COVER1, EV_COVER5 );
				}
			}
			else if ( !Q_irand( 0, 3 ) )
			{
				event = Q_irand( EV_SIGHT2, EV_SIGHT3 );
			}
			else if ( !Q_irand( 0, 1 ) )
			{
				event = Q_irand( EV_SOUND1, EV_SOUND3 );
			}
			else if ( !Q_irand( 0, 2 ) )
			{
				event = EV_LOST1;
			}
			else if ( !Q_irand( 0, 1 ) )
			{
				event = EV_ESCAPING2;
			}
			else
			{
				event = EV_GIVEUP4;
			}
		}
		else
		{//variant2
			if ( self->enemy )
			{
				if ( Q_irand( 0, 9 ) > 6 )
				{
					event = Q_irand( EV_CHASE1, EV_CHASE3 );
				}
				else if ( Q_irand( 0, 6 ) > 4 )
				{
					event = Q_irand( EV_OUTFLANK1, EV_OUTFLANK2 );
				}
				else
				{
					event = Q_irand( EV_COVER1, EV_COVER5 );
				}
			}
			else if ( !Q_irand( 0, 3 ) )
			{
				event = Q_irand( EV_SIGHT1, EV_SIGHT2 );
			}
			else if ( !Q_irand( 0, 1 ) )
			{
				event = Q_irand( EV_SOUND1, EV_SOUND3 );
			}
			else if ( !Q_irand( 0, 2 ) )
			{
				event = EV_LOST1;
			}
			else if ( !Q_irand( 0, 1 ) )
			{
				event = EV_GIVEUP3;
			}
			else
			{
				event = EV_CONFUSE1;
			}
		}
		break;
	case CLASS_R2D2:				// droid
		G_Sound(self, G_SoundIndex(va("sound/chars/r2d2/misc/r2d2talk0%d.wav",Q_irand(1, 3))));
		break;
	case CLASS_R5D2:				// droid
		G_Sound(self, G_SoundIndex(va("sound/chars/r5d2/misc/r5talk%d.wav",Q_irand(1, 4))));
		break;
	case CLASS_MOUSE:				// droid
		G_Sound(self, G_SoundIndex(va("sound/chars/mouse/misc/mousego%d.wav",Q_irand(1, 3))));
		break;
	case CLASS_GONK:				// droid
		G_Sound(self, G_SoundIndex(va("sound/chars/gonk/misc/gonktalk%d.wav",Q_irand(1, 2))));
		break;
	case CLASS_JAWA:
		G_SoundOnEnt(self, CHAN_VOICE, va("sound/chars/jawa/misc/chatter%d.wav",Q_irand(1, 6)) );
		if ( self->NPC )
		{
			self->NPC->blockedSpeechDebounceTime = level.time + 2000;
		}
		break;
	default:
		break;
	}

	if ( event != -1 )
	{
		//hack here because we reuse some "combat" and "extra" sounds
		qboolean addFlag = (qboolean)((self->NPC->scriptFlags&SCF_NO_COMBAT_TALK) != 0);
		self->NPC->scriptFlags &= ~SCF_NO_COMBAT_TALK;

		G_AddVoiceEvent( self, event, 3000 );

		if ( addFlag )
		{
			self->NPC->scriptFlags |= SCF_NO_COMBAT_TALK;
		}
	}
}
Exemplo n.º 12
0
void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, const vec3_t point, int damage, int mod, int hitLoc, int voiceEvent = -1 )
{
	//If we've already taken pain, then don't take it again
	if ( level.time < self->painDebounceTime && mod != MOD_ELECTROCUTE && mod != MOD_MELEE )
	{//FIXME: if hit while recoving from losing a saber lock, we should still play a pain anim?
		return;
	}

	int		pain_anim = -1;
	float	pain_chance;

	if ( self->s.weapon == WP_THERMAL && self->client->fireDelay > 0 )
	{//don't interrupt thermal throwing anim
		return;
	}
	else if (self->client->ps.powerups[PW_GALAK_SHIELD])
	{
		return;
	}
	else if ( self->client->NPC_class == CLASS_GALAKMECH )
	{
		if ( hitLoc == HL_GENERIC1 )
		{//hit the antenna!
			pain_chance = 1.0f;
			self->s.powerups |= ( 1 << PW_SHOCKED );
			self->client->ps.powerups[PW_SHOCKED] = level.time + Q_irand( 500, 2500 );
		}
		else if ( self->client->ps.powerups[PW_GALAK_SHIELD] )
		{//shield up
			return;
		}
		else if ( self->health > 200 && damage < 100 )
		{//have a *lot* of health
			pain_chance = 0.05f;
		}
		else
		{//the lower my health and greater the damage, the more likely I am to play a pain anim
			pain_chance = (200.0f-self->health)/100.0f + damage/50.0f;
		}
	}
	else if ( self->client && self->client->playerTeam == TEAM_PLAYER && other && !other->s.number )
	{//ally shot by player always complains
		pain_chance = 1.1f;
	}
	else
	{
		if ( other && (other->s.weapon == WP_SABER || mod == MOD_ELECTROCUTE || mod == MOD_CRUSH/*FIXME:MOD_FORCE_GRIP*/) )
		{
			if ( self->client->ps.weapon == WP_SABER
				&& other->s.number < MAX_CLIENTS )
			{//hmm, shouldn't *always* react to damage from player if I have a saber
				pain_chance = 1.05f - ((self->NPC->rank)/(float)RANK_CAPTAIN);
			}
			else
			{
				pain_chance = 1.0f;//always take pain from saber
			}
		}
		else if ( mod == MOD_GAS )
		{
			pain_chance = 1.0f;
		}
		else if ( mod == MOD_MELEE )
		{//higher in rank (skill) we are, less likely we are to be fazed by a punch
			pain_chance = 1.0f - ((RANK_CAPTAIN-self->NPC->rank)/(float)RANK_CAPTAIN);
		}
		else if ( self->client->NPC_class == CLASS_PROTOCOL )
		{
			pain_chance = 1.0f;
		}
		else
		{
			pain_chance = NPC_GetPainChance( self, damage );
		}
		if ( self->client->NPC_class == CLASS_DESANN )
		{
			pain_chance *= 0.5f;
		}
	}

	//See if we're going to flinch
	if ( Q_flrand(0.0f, 1.0f) < pain_chance )
	{
		//Pick and play our animation
		if ( (self->client->ps.eFlags&EF_FORCE_GRIPPED) )
		{
			G_AddVoiceEvent( self, Q_irand(EV_CHOKE1, EV_CHOKE3), 0 );
		}
		else if ( mod == MOD_GAS )
		{
			//SIGH... because our choke sounds are inappropriately long, I have to debounce them in code!
			if ( TIMER_Done( self, "gasChokeSound" ) )
			{
				TIMER_Set( self, "gasChokeSound", Q_irand( 1000, 2000 ) );
				G_AddVoiceEvent( self, Q_irand(EV_CHOKE1, EV_CHOKE3), 0 );
			}
		}
		else if ( (self->client->ps.eFlags&EF_FORCE_DRAINED) )
		{
			NPC_SetPainEvent( self );
		}
		else
		{//not being force-gripped or force-drained
			if ( G_CheckForStrongAttackMomentum( self )
				|| PM_SpinningAnim( self->client->ps.legsAnim )
				|| PM_SaberInSpecialAttack( self->client->ps.torsoAnim )
				|| PM_InKnockDown( &self->client->ps )
				|| PM_RollingAnim( self->client->ps.legsAnim )
				|| (PM_FlippingAnim( self->client->ps.legsAnim )&&!PM_InCartwheel( self->client->ps.legsAnim )) )
			{//strong attacks, rolls, knockdowns, flips and spins cannot be interrupted by pain
				return;
			}
			else
			{//play an anim
				if ( self->client->NPC_class == CLASS_GALAKMECH )
				{//only has 1 for now
					//FIXME: never plays this, it seems...
					pain_anim = BOTH_PAIN1;
				}
				else if ( mod == MOD_MELEE )
				{
					pain_anim = PM_PickAnim( self, BOTH_PAIN2, BOTH_PAIN3 );
				}
				else if ( self->s.weapon == WP_SABER )
				{//temp HACK: these are the only 2 pain anims that look good when holding a saber
					pain_anim = PM_PickAnim( self, BOTH_PAIN2, BOTH_PAIN3 );
				}
				else if ( mod != MOD_ELECTROCUTE )
				{
					pain_anim = G_PickPainAnim( self, point, damage, hitLoc );
				}

				if ( pain_anim == -1 )
				{
					pain_anim = PM_PickAnim( self, BOTH_PAIN1, BOTH_PAIN18 );
				}
				self->client->ps.saberAnimLevel = SS_FAST;//next attack must be a quick attack
				self->client->ps.saberMove = LS_READY;//don't finish whatever saber move you may have been in
				int parts = SETANIM_BOTH;
				if ( PM_CrouchAnim( self->client->ps.legsAnim ) || PM_InCartwheel( self->client->ps.legsAnim ) )
				{
					parts = SETANIM_LEGS;
				}
				self->NPC->aiFlags &= ~NPCAI_KNEEL;
				NPC_SetAnim( self, parts, pain_anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
			}
			if ( voiceEvent != -1 )
			{
				G_AddVoiceEvent( self, voiceEvent, Q_irand( 2000, 4000 ) );
			}
			else
			{
				NPC_SetPainEvent( self );
			}
		}

		//Setup the timing for it
		if ( mod == MOD_ELECTROCUTE )
		{
			self->painDebounceTime = level.time + 4000;
		}
		self->painDebounceTime = level.time + PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t) pain_anim );
		self->client->fireDelay = 0;
	}
}
Exemplo n.º 13
0
void NPC_HandleAIFlags (void)
{
	//FIXME: make these flags checks a function call like NPC_CheckAIFlagsAndTimers
	if ( NPCInfo->aiFlags & NPCAI_LOST )
	{//Print that you need help!
		//FIXME: shouldn't remove this just yet if cg_draw needs it
		NPCInfo->aiFlags &= ~NPCAI_LOST;
		
		/*
		if ( showWaypoints )
		{
			Q3_DebugPrint(WL_WARNING, "%s can't navigate to target %s (my wp: %d, goal wp: %d)\n", NPC->targetname, NPCInfo->goalEntity->targetname, NPC->waypoint, NPCInfo->goalEntity->waypoint );
		}
		*/

		if ( NPCInfo->goalEntity && NPCInfo->goalEntity == NPC->enemy )
		{//We can't nav to our enemy
			//Drop enemy and see if we should search for him
			NPC_LostEnemyDecideChase();
		}
	}

	if ( NPCInfo->aiFlags & NPCAI_AWAITING_COMM )
	{
		if(NPCInfo->commWaitTime < level.time)
		{
			//FIXME: we shouldn't assume team_leader, we should remember who sent this hail!
			NPC_SetSayState(NPC, NPC->client->team_leader, Q_irand(SAY_BADHAIL1, SAY_BADHAIL4));
			NPCInfo->aiFlags &= ~NPCAI_AWAITING_COMM;
		}
	}

	/*
	NPCInfo->canShove = qfalse;
	//flag never gets set in current nav implementation
	if (NPCInfo->aiFlags & NPCAI_BLOCKED)
	{
		NPCInfo->consecutiveBlockedMoves++;
		NPCInfo->blockedDebounceTime = level.time + 1000;//Remember you were blocked for a whole second
		//If totally blocked, should we see if we can jump the obstacle?
		if(NPCInfo->blockingEntNum == ENTITYNUM_WORLD)//WORLD
		{//Can't go anywhere
			G_ActivateBehavior( NPC, BSET_STUCK);
			//If you're in formation, what do we do here?
		}
		else
		{
			gentity_t *blocker = &g_entities[NPCInfo->blockingEntNum];

			if( NPCInfo->consecutiveBlockedMoves > 10 )
			{//Okay, shove them out of the way!
				if(NPCInfo->shoveCount > 3)
				{//Already tried shoving 4 times, just stand here
					NPCInfo->canShove = qfalse;
				}
				else
				{
					NPCInfo->canShove = qtrue;
				}
			}

			if(blocker->client && blocker->client->playerTeam == NPC->client->playerTeam)
			{//Should we ask it to get out of the way?
				//FIXME:  NPC_SetSayBState(NPC, blocker, Q_irand(SAY_MOVEIT1, SAY_MOVEIT4);// ?
				if(NPCInfo->blockedSpeechDebounceTime < level.time)
				{
					if ( NPC->behaviorSet[BSET_BLOCKED] )
					{
						G_ActivateBehavior( NPC, BSET_BLOCKED);
					}
					else
					{
						G_AddVoiceEvent( NPC, Q_irand(EV_BLOCKED1, EV_BLOCKED3), 0 );
					}
#ifdef _DEBUG
					//gi.Printf( "%s: 'Hey, %s, move it!'\n", NPC->targetname, blocker->targetname );
#endif
					//NPCInfo->blockedSpeechDebounceTime = level.time + 10000;//FIXME: make a define
					//Ok, need to make it get out of the way...
				}
			}
			else if((blocker->client || blocker->takedamage) && blocker->health > 0 && blocker->health < 200 )
			{//Attack it!?  Set enemy and temp behavior?  Hmm...
				//Careful, what if it's explosive?
				G_SetEnemy( NPC, blocker );
				if( NPCInfo->consecutiveBlockedMoves == 30 )
				{//Blocked for three seconds straight
					G_ActivateBehavior( NPC, BSET_BLOCKED);
				}
			}
		}
	}
	else if(NPCInfo->blockedDebounceTime < level.time)
	{//Only clear if haven't been blocked for a whole second
		NPCInfo->consecutiveBlockedMoves = 0;
		NPCInfo->shoveCount = 0;
	}

	if(NPCInfo->shoveDebounce < level.time)
	{//We have shoved for 1 second at least
		NPCInfo->lastShoveDir = 0.0f;
	}

	//NAV_ClearBlockedInfo(NPC);
	*/

	//MRJ Request:
	if ( NPCInfo->aiFlags & NPCAI_GREET_ALLIES && !NPC->enemy )//what if "enemy" is the greetEnt?
	{//If no enemy, look for teammates to greet
		//FIXME: don't say hi to the same guy over and over again.
		if ( NPCInfo->greetingDebounceTime < level.time )
		{//Has been at least 2 seconds since we greeted last
			if ( !NPCInfo->greetEnt )
			{//Find a teammate whom I'm facing and who is facing me and within 128
				NPCInfo->greetEnt = NPC_PickAlly( qtrue, 128, qtrue, qtrue );
			}

			if ( NPCInfo->greetEnt && !Q_irand(0, 5) )
			{//Start greeting someone
				qboolean	greeted = qfalse;

				//TODO:  If have a greetscript, run that instead?

				//FIXME: make them greet back?
				if( !Q_irand( 0, 2 ) )
				{//Play gesture anim (press gesture button?)
					greeted = qtrue;
					NPC_SetAnim( NPC, SETANIM_TORSO, Q_irand( BOTH_GESTURE1, BOTH_GESTURE3 ), SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD );
					//NOTE: play full-body gesture if not moving?
				}

				if( !Q_irand( 0, 2 ) )
				{//Play random voice greeting sound
					greeted = qtrue;
					//FIXME: need NPC sound sets

					G_AddVoiceEvent( NPC, Q_irand(EV_GREET1, EV_GREET3), 2000 );
				}

				if( !Q_irand( 0, 1 ) )
				{//set looktarget to them for a second or two
					greeted = qtrue;
					NPC_TempLookTarget( NPC, NPCInfo->greetEnt->s.number, 1000, 3000 );
				}

				if ( greeted )
				{//Did at least one of the things above
					//Don't greet again for 2 - 4 seconds
					NPCInfo->greetingDebounceTime = level.time + Q_irand( 2000, 4000 );
					NPCInfo->greetEnt = NULL;
				}
			}
		}
	}
}
Exemplo n.º 14
0
void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, vec3_t point, int damage, int mod, int hitLoc, int voiceEvent )
{
    int		pain_anim = -1;
    float	pain_chance;

    //If we've already taken pain, then don't take it again
    if ( level.time < self->painDebounceTime && /*mod != MOD_ELECTROCUTE &&*/ mod != MOD_MELEE ) //rwwFIXMEFIXME: MOD_ELECTROCUTE
    {   //FIXME: if hit while recoving from losing a saber lock, we should still play a pain anim?
        return;
    }

    if ( self->s.weapon == WP_THERMAL && self->client->ps.weaponTime > 0 )
    {   //don't interrupt thermal throwing anim
        return;
    }
    else if ( self->client->NPC_class == CLASS_GALAKMECH )
    {
        if ( hitLoc == HL_GENERIC1 )
        {   //hit the antenna!
            pain_chance = 1.0f;
            //	self->s.powerups |= ( 1 << PW_SHOCKED );
            //	self->client->ps.powerups[PW_SHOCKED] = level.time + Q_irand( 500, 2500 );
            //rwwFIXMEFIXME: support for this
        }
        //	else if ( self->client->ps.powerups[PW_GALAK_SHIELD] )
        //	{//shield up
        //		return;
        //	}
        //rwwFIXMEFIXME: and this
        else if ( self->health > 200 && damage < 100 )
        {   //have a *lot* of health
            pain_chance = 0.05f;
        }
        else
        {   //the lower my health and greater the damage, the more likely I am to play a pain anim
            pain_chance = (200.0f-self->health)/100.0f + damage/50.0f;
        }
    }
    else if ( self->client && self->client->playerTeam == NPCTEAM_PLAYER && other && !other->s.number )
    {   //ally shot by player always complains
        pain_chance = 1.1f;
    }
    else
    {
        if ( other && other->s.weapon == WP_SABER || /*mod == MOD_ELECTROCUTE ||*/ mod == MOD_CRUSH/*FIXME:MOD_FORCE_GRIP*/ )
        {
            pain_chance = 1.0f;//always take pain from saber
        }
        else if ( mod == MOD_MELEE )
        {   //higher in rank (skill) we are, less likely we are to be fazed by a punch
            pain_chance = 1.0f - ((RANK_CAPTAIN-self->NPC->rank)/(float)RANK_CAPTAIN);
        }
        else if ( self->client->NPC_class == CLASS_PROTOCOL )
        {
            pain_chance = 1.0f;
        }
        else
        {
            pain_chance = NPC_GetPainChance( self, damage );
        }
        if ( self->client->NPC_class == CLASS_DESANN )
        {
            pain_chance *= 0.5f;
        }
    }

    //See if we're going to flinch
    if ( random() < pain_chance )
    {
        int animLength;

        //Pick and play our animation
        if ( self->client->ps.fd.forceGripBeingGripped < level.time )
        {   //not being force-gripped or force-drained
            if ( /*G_CheckForStrongAttackMomentum( self ) //rwwFIXMEFIXME: Is this needed?
				||*/ PM_SpinningAnim( self->client->ps.legsAnim )
                || BG_SaberInSpecialAttack( self->client->ps.torsoAnim )
                || PM_InKnockDown( &self->client->ps )
                || PM_RollingAnim( self->client->ps.legsAnim )
                || (BG_FlippingAnim( self->client->ps.legsAnim )&&!PM_InCartwheel( self->client->ps.legsAnim )) )
            {   //strong attacks, rolls, knockdowns, flips and spins cannot be interrupted by pain
            }
            else
            {   //play an anim
                int parts;

                if ( self->client->NPC_class == CLASS_GALAKMECH )
                {   //only has 1 for now
                    //FIXME: never plays this, it seems...
                    pain_anim = BOTH_PAIN1;
                }
                else if ( mod == MOD_MELEE )
                {
                    pain_anim = BG_PickAnim( self->localAnimIndex, BOTH_PAIN2, BOTH_PAIN3 );
                }
                else if ( self->s.weapon == WP_SABER )
                {   //temp HACK: these are the only 2 pain anims that look good when holding a saber
                    pain_anim = BG_PickAnim( self->localAnimIndex, BOTH_PAIN2, BOTH_PAIN3 );
                }
                /*
                else if ( mod != MOD_ELECTROCUTE )
                {
                	pain_anim = G_PickPainAnim( self, point, damage, hitLoc );
                }
                */

                if ( pain_anim == -1 )
                {
                    pain_anim = BG_PickAnim( self->localAnimIndex, BOTH_PAIN1, BOTH_PAIN18 );
                }
                self->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;//next attack must be a quick attack
                self->client->ps.saberMove = LS_READY;//don't finish whatever saber move you may have been in
                parts = SETANIM_BOTH;
                if ( BG_CrouchAnim( self->client->ps.legsAnim ) || PM_InCartwheel( self->client->ps.legsAnim ) )
                {
                    parts = SETANIM_LEGS;
                }

                if (pain_anim != -1)
                {
                    NPC_SetAnim( self, parts, pain_anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
                }
            }
            if ( voiceEvent != -1 )
            {
                G_AddVoiceEvent( self, voiceEvent, Q_irand( 2000, 4000 ) );
            }
            else
            {
                NPC_SetPainEvent( self );
            }
        }
        else
        {
            G_AddVoiceEvent( self, Q_irand(EV_CHOKE1, EV_CHOKE3), 0 );
        }

        //Setup the timing for it
        /*
        if ( mod == MOD_ELECTROCUTE )
        {
        	self->painDebounceTime = level.time + 4000;
        }
        */
        animLength = bgAllAnims[self->localAnimIndex].anims[pain_anim].numFrames * fabs((float)(bgHumanoidAnimations[pain_anim].frameLerp));

        self->painDebounceTime = level.time + animLength;
        self->client->ps.weaponTime = 0;
    }
}
Exemplo n.º 15
0
Arquivo: NPC.c Projeto: Geptun/japp
void NPC_HandleAIFlags (void)
{
	//FIXME: make these flags checks a function call like NPC_CheckAIFlagsAndTimers
	if ( NPCInfo->aiFlags & NPCAI_LOST )
	{//Print that you need help!
		//FIXME: shouldn't remove this just yet if cg_draw needs it
		NPCInfo->aiFlags &= ~NPCAI_LOST;
		
		/*
		if ( showWaypoints )
		{
			Q3_DebugPrint(WL_WARNING, "%s can't navigate to target %s (my wp: %d, goal wp: %d)\n", NPC->targetname, NPCInfo->goalEntity->targetname, NPC->waypoint, NPCInfo->goalEntity->waypoint );
		}
		*/

		if ( NPCInfo->goalEntity && NPCInfo->goalEntity == NPC->enemy )
		{//We can't nav to our enemy
			//Drop enemy and see if we should search for him
			NPC_LostEnemyDecideChase();
		}
	}

	//MRJ Request:
	/*
	if ( NPCInfo->aiFlags & NPCAI_GREET_ALLIES && !NPC->enemy )//what if "enemy" is the greetEnt?
	{//If no enemy, look for teammates to greet
		//FIXME: don't say hi to the same guy over and over again.
		if ( NPCInfo->greetingDebounceTime < level.time )
		{//Has been at least 2 seconds since we greeted last
			if ( !NPCInfo->greetEnt )
			{//Find a teammate whom I'm facing and who is facing me and within 128
				NPCInfo->greetEnt = NPC_PickAlly( qtrue, 128, qtrue, qtrue );
			}

			if ( NPCInfo->greetEnt && !Q_irand(0, 5) )
			{//Start greeting someone
				qboolean	greeted = qfalse;

				//TODO:  If have a greetscript, run that instead?

				//FIXME: make them greet back?
				if( !Q_irand( 0, 2 ) )
				{//Play gesture anim (press gesture button?)
					greeted = qtrue;
					NPC_SetAnim( NPC, SETANIM_TORSO, Q_irand( BOTH_GESTURE1, BOTH_GESTURE3 ), SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD );
					//NOTE: play full-body gesture if not moving?
				}

				if( !Q_irand( 0, 2 ) )
				{//Play random voice greeting sound
					greeted = qtrue;
					//FIXME: need NPC sound sets

					//G_AddVoiceEvent( NPC, Q_irand(EV_GREET1, EV_GREET3), 2000 );
				}

				if( !Q_irand( 0, 1 ) )
				{//set looktarget to them for a second or two
					greeted = qtrue;
					NPC_TempLookTarget( NPC, NPCInfo->greetEnt->s.number, 1000, 3000 );
				}

				if ( greeted )
				{//Did at least one of the things above
					//Don't greet again for 2 - 4 seconds
					NPCInfo->greetingDebounceTime = level.time + Q_irand( 2000, 4000 );
					NPCInfo->greetEnt = NULL;
				}
			}
		}
	}
	*/
	//been told to play a victory sound after a delay
	if ( NPCInfo->greetingDebounceTime && NPCInfo->greetingDebounceTime < level.time )
	{
		G_AddVoiceEvent( NPC, Q_irand(EV_VICTORY1, EV_VICTORY3), Q_irand( 2000, 4000 ) );
		NPCInfo->greetingDebounceTime = 0;
	}

	if ( NPCInfo->ffireCount > 0 )
	{
		if ( NPCInfo->ffireFadeDebounce < level.time )
		{
			NPCInfo->ffireCount--;
			//Com_Printf( "drop: %d < %d\n", NPCInfo->ffireCount, 3+((2-g_spSkill.integer)*2) );
			NPCInfo->ffireFadeDebounce = level.time + 3000;
		}
	}
	if ( d_patched.integer )
	{//use patch-style navigation
		if ( NPCInfo->consecutiveBlockedMoves > 20 )
		{//been stuck for a while, try again?
			NPCInfo->consecutiveBlockedMoves = 0;
		}
	}
}
Exemplo n.º 16
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;
			}
		}
	}
}
Exemplo n.º 17
0
void NPC_GM_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, vec3_t point, int damage, int mod,int hitLoc ) 
{
	if ( self->client->ps.powerups[PW_GALAK_SHIELD] == 0 )
	{//shield is currently down
		//FIXME: allow for radius damage?
		if ( (hitLoc==HL_GENERIC1) && (self->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH) )
		{
			int newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*antenna_base" );
			if ( newBolt != -1 )
			{
				GM_CreateExplosion( self, newBolt );
			}

			gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_shield_off", TURN_OFF );
			gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_antenna", TURN_OFF );
			gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_antenna_base_cap_off", TURN_ON );
			self->client->ps.powerups[PW_GALAK_SHIELD] = 0;//temp, for effect
			self->client->ps.stats[STAT_ARMOR] = 0;//no more armor
			self->NPC->investigateDebounceTime = 0;//stop recharging

			NPC_SetAnim( self, SETANIM_BOTH, BOTH_ALERT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
			TIMER_Set( self, "attackDelay", self->client->ps.torsoAnimTimer );
			G_AddEvent( self, Q_irand( EV_DEATH1, EV_DEATH3 ), self->health );
		}
	}
	else
	{//store the point for shield impact
		if ( point )
		{
			VectorCopy( point, self->pos4 );
			self->client->poisonTime = level.time;
		}
	}

	if ( !self->lockCount && !self->client->ps.torsoAnimTimer )
	{//don't interrupt laser sweep attack or other special attacks/moves
		if ( self->count < 4 && self->health > 100 && hitLoc != HL_GENERIC1 )
		{
			if ( self->delay < level.time )
			{
				int speech;
				switch( self->count )
				{
				default:
				case 0:
					speech = EV_PUSHED1;
					break;
				case 1:
					speech = EV_PUSHED2;
					break;
				case 2:
					speech = EV_PUSHED3;
					break;
				case 3:
					speech = EV_DETECTED1;
					break;
				}
				self->count++;
				self->NPC->blockedSpeechDebounceTime = 0;
				G_AddVoiceEvent( self, speech, Q_irand( 3000, 5000 ) );
				self->delay = level.time + Q_irand( 5000, 7000 );
			}
		}
		else
		{
			NPC_Pain( self, inflictor, attacker, point, damage, mod, hitLoc );
		}
	}
	else if ( hitLoc == HL_GENERIC1 )
	{
		NPC_SetPainEvent( self );
		self->s.powerups |= ( 1 << PW_SHOCKED );
		self->client->ps.powerups[PW_SHOCKED] = level.time + Q_irand( 500, 2500 );
	}

	if ( inflictor && inflictor->lastEnemy == self )
	{//He force-pushed my own lobfires back at me
		if ( mod == MOD_REPEATER_ALT && !Q_irand( 0, 2 ) )
		{
			if ( TIMER_Done( self, "noRapid" ) )
			{
				self->NPC->scriptFlags &= ~SCF_ALT_FIRE;
				self->alt_fire = qfalse;
				TIMER_Set( self, "noLob", Q_irand( 2000, 6000 ) );
			}
			else
			{//hopefully this will make us fire the laser
				TIMER_Set( self, "noLob", Q_irand( 1000, 2000 ) );
			}
		}
		else if ( mod == MOD_REPEATER && !Q_irand( 0, 5 ) )
		{
			if ( TIMER_Done( self, "noLob" ) )
			{
				self->NPC->scriptFlags |= SCF_ALT_FIRE;
				self->alt_fire = qtrue;
				TIMER_Set( self, "noRapid", Q_irand( 2000, 6000 ) );
			}
			else
			{//hopefully this will make us fire the laser
				TIMER_Set( self, "noRapid", Q_irand( 1000, 2000 ) );
			}
		}
	}
}