Example #1
0
//------------------------------
void MineMonster_Attack( void )
{
	if ( !TIMER_Exists( NPC, "attacking" ))
	{
		// usually try and play a jump attack if the player somehow got above them....or just really rarely
		if ( NPC->enemy && ((NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2] > 10 && random() > 0.1f ) 
						|| random() > 0.8f ))
		{
			// Going to do ATTACK4
			TIMER_Set( NPC, "attacking", 1750 + random() * 200 );
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );

			TIMER_Set( NPC, "attack2_dmg", 950 ); // level two damage
		}
		else if ( random() > 0.5f )
		{
			if ( random() > 0.8f )
			{
				// Going to do ATTACK3, (rare)
				TIMER_Set( NPC, "attacking", 850 );
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );

				TIMER_Set( NPC, "attack2_dmg", 400 ); // level two damage
			}
			else
			{
				// Going to do ATTACK1
				TIMER_Set( NPC, "attacking", 850 );
				NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );

				TIMER_Set( NPC, "attack1_dmg", 450 ); // level one damage
			}
		}
		else
		{
			// Going to do ATTACK2
			TIMER_Set( NPC, "attacking", 1250 );
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );

			TIMER_Set( NPC, "attack1_dmg", 700 ); // level one damage
		}
	}
	else
	{
		// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
		if ( TIMER_Done2( NPC, "attack1_dmg", qtrue ))
		{
			MineMonster_TryDamage( NPC->enemy, 5 );
		}
		else if ( TIMER_Done2( NPC, "attack2_dmg", qtrue ))
		{
			MineMonster_TryDamage( NPC->enemy, 10 );
		}
	}

	// Just using this to remove the attacking flag at the right time
	TIMER_Done2( NPC, "attacking", qtrue );
}
Example #2
0
//------------------------------
void Howler_Attack( void )
{
	if ( !TIMER_Exists( NPC, "attacking" ))
	{
		// Going to do ATTACK1
		TIMER_Set( NPC, "attacking", 1700 + random() * 200 );
		NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );

		TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage
	}

	// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
	if ( TIMER_Done2( NPC, "attack_dmg", qtrue ))
	{
		Howler_TryDamage( NPC->enemy, 5 );
	}

	// Just using this to remove the attacking flag at the right time
	TIMER_Done2( NPC, "attacking", qtrue );
}
Example #3
0
//----------------------------------
void MineMonster_Combat( void )
{
	float distance;
	qboolean advance;

	// If we cannot see our target or we have somewhere to go, then do that
	if ( !NPC_ClearLOS4( NPC->enemy ) || UpdateGoal( ))
	{
		NPCInfo->combatMove = qtrue;
		NPCInfo->goalEntity = NPC->enemy;
		NPCInfo->goalRadius = MAX_DISTANCE;	// just get us within combat range

		NPC_MoveToGoal( qtrue );
		return;
	}

	// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
	NPC_FaceEnemy( qtrue );

	distance	= DistanceHorizontalSquared( &NPC->r.currentOrigin, &NPC->enemy->r.currentOrigin );	

	advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse  );

	if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
	{
		if ( TIMER_Done2( NPC, "takingPain", qtrue ))
		{
			NPCInfo->localState = LSTATE_CLEAR;
		}
		else
		{
			MineMonster_Move( qtrue );
		}
	}
	else
	{
		MineMonster_Attack();
	}
}
Example #4
0
/*
-------------------------
NPC_BSRancor_Default
-------------------------
*/
void NPC_BSRancor_Default( void )
{
	AddSightEvent( NPCS.NPC, NPCS.NPC->r.currentOrigin, 1024, AEL_DANGER_GREAT, 50 );

	Rancor_Crush();

	NPCS.NPC->client->ps.eFlags2 &= ~(EF2_USE_ALT_ANIM|EF2_GENERIC_NPC_FLAG);
	if ( NPCS.NPC->count )
	{//holding someone
		NPCS.NPC->client->ps.eFlags2 |= EF2_USE_ALT_ANIM;
		if ( NPCS.NPC->count == 2 )
		{//in my mouth
			NPCS.NPC->client->ps.eFlags2 |= EF2_GENERIC_NPC_FLAG;
		}
	}
	else
	{
		NPCS.NPC->client->ps.eFlags2 &= ~(EF2_USE_ALT_ANIM|EF2_GENERIC_NPC_FLAG);
	}

	if ( TIMER_Done2( NPCS.NPC, "clearGrabbed", qtrue ) )
	{
		Rancor_DropVictim( NPCS.NPC );
	}
	else if ( NPCS.NPC->client->ps.legsAnim == BOTH_PAIN2 
		&& NPCS.NPC->count == 1 
		&& NPCS.NPC->activator )
	{
		if ( !Q_irand( 0, 3 ) )
		{
			Rancor_CheckDropVictim();
		}
	}
	if ( !TIMER_Done( NPCS.NPC, "rageTime" ) )
	{//do nothing but roar first time we see an enemy
		AddSoundEvent( NPCS.NPC, NPCS.NPC->r.currentOrigin, 1024, AEL_DANGER_GREAT, qfalse );//, qfalse );
		NPC_FaceEnemy( qtrue );
		return;
	}
	if ( NPCS.NPC->enemy )
	{
		/*
		if ( NPC->enemy->client //enemy is a client
			&& (NPC->enemy->client->NPC_class == CLASS_UGNAUGHT || NPC->enemy->client->NPC_class == CLASS_JAWA )//enemy is a lowly jawa or ugnaught
			&& NPC->enemy->enemy != NPC//enemy's enemy is not me
			&& (!NPC->enemy->enemy || !NPC->enemy->enemy->client || NPC->enemy->enemy->client->NPC_class!=CLASS_RANCOR) )//enemy's enemy is not a client or is not a rancor (which is as scary as me anyway)
		{//they should be scared of ME and no-one else
			G_SetEnemy( NPC->enemy, NPC );
		}
		*/
		if ( TIMER_Done(NPCS.NPC,"angrynoise") )
		{
			G_Sound( NPCS.NPC, CHAN_AUTO, G_SoundIndex( va("sound/chars/rancor/misc/anger%d.wav", Q_irand(1, 3))) );

			TIMER_Set( NPCS.NPC, "angrynoise", Q_irand( 5000, 10000 ) );
		}
		else
		{
			AddSoundEvent( NPCS.NPC, NPCS.NPC->r.currentOrigin, 512, AEL_DANGER_GREAT, qfalse );//, qfalse );
		}
		if ( NPCS.NPC->count == 2 && NPCS.NPC->client->ps.legsAnim == BOTH_ATTACK3 )
		{//we're still chewing our enemy up
			NPC_UpdateAngles( qtrue, qtrue );
			return;
		}
		//else, if he's in our hand, we eat, else if he's on the ground, we keep attacking his dead body for a while
		if( NPCS.NPC->enemy->client && NPCS.NPC->enemy->client->NPC_class == CLASS_RANCOR )
		{//got mad at another Rancor, look for a valid enemy
			if ( TIMER_Done( NPCS.NPC, "rancorInfight" ) )
			{
				NPC_CheckEnemyExt( qtrue );
			}
		}
		else if ( !NPCS.NPC->count )
		{
			if ( ValidEnemy( NPCS.NPC->enemy ) == qfalse )
			{
				TIMER_Remove( NPCS.NPC, "lookForNewEnemy" );//make them look again right now
				if ( !NPCS.NPC->enemy->inuse || level.time - NPCS.NPC->enemy->s.time > Q_irand( 10000, 15000 ) )
				{//it's been a while since the enemy died, or enemy is completely gone, get bored with him
					NPCS.NPC->enemy = NULL;
					Rancor_Patrol();
					NPC_UpdateAngles( qtrue, qtrue );
					return;
				}
			}
			if ( TIMER_Done( NPCS.NPC, "lookForNewEnemy" ) )
			{
				gentity_t *newEnemy, *sav_enemy = NPCS.NPC->enemy;//FIXME: what about NPC->lastEnemy?
				NPCS.NPC->enemy = NULL;
				newEnemy = NPC_CheckEnemy( NPCS.NPCInfo->confusionTime < level.time, qfalse, qfalse );
				NPCS.NPC->enemy = sav_enemy;
				if ( newEnemy && newEnemy != sav_enemy )
				{//picked up a new enemy!
					NPCS.NPC->lastEnemy = NPCS.NPC->enemy;
					G_SetEnemy( NPCS.NPC, newEnemy );
					//hold this one for at least 5-15 seconds
					TIMER_Set( NPCS.NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
				}
				else
				{//look again in 2-5 secs
					TIMER_Set( NPCS.NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) );
				}
			}
		}
		Rancor_Combat();
	}
	else 
	{
		if ( TIMER_Done(NPCS.NPC,"idlenoise") )
		{
			G_Sound( NPCS.NPC, CHAN_AUTO, G_SoundIndex( va("sound/chars/rancor/snort_%d.wav", Q_irand(1, 2))) );

			TIMER_Set( NPCS.NPC, "idlenoise", Q_irand( 2000, 4000 ) );
			AddSoundEvent( NPCS.NPC, NPCS.NPC->r.currentOrigin, 384, AEL_DANGER, qfalse );//, qfalse );
		}
		if ( NPCS.NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
		{
			Rancor_Patrol();
		}
		else
		{
			Rancor_Idle();
		}
	}

	NPC_UpdateAngles( qtrue, qtrue );
}
Example #5
0
//----------------------------------
void Rancor_Combat( void )
{
	if ( NPCS.NPC->count )
	{//holding my enemy
		if ( TIMER_Done2( NPCS.NPC, "takingPain", qtrue ))
		{
			NPCS.NPCInfo->localState = LSTATE_CLEAR;
		}
		else
		{
			Rancor_Attack( 0, qfalse );
		}
		NPC_UpdateAngles( qtrue, qtrue );
		return;
	}
	// If we cannot see our target or we have somewhere to go, then do that
	if ( !NPC_ClearLOS4( NPCS.NPC->enemy ) )//|| UpdateGoal( ))
	{
		NPCS.NPCInfo->combatMove = qtrue;
		NPCS.NPCInfo->goalEntity = NPCS.NPC->enemy;
		NPCS.NPCInfo->goalRadius = MIN_DISTANCE;//MAX_DISTANCE;	// just get us within combat range

		if ( !NPC_MoveToGoal( qtrue ) )
		{//couldn't go after him?  Look for a new one
			TIMER_Set( NPCS.NPC, "lookForNewEnemy", 0 );
			NPCS.NPCInfo->consecutiveBlockedMoves++;
		}
		else 
		{
			NPCS.NPCInfo->consecutiveBlockedMoves = 0;
		}
		return;
	}

	// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
	NPC_FaceEnemy( qtrue );

	{
		float	distance;
		qboolean	advance;
		qboolean	doCharge;

		distance	= Distance( NPCS.NPC->r.currentOrigin, NPCS.NPC->enemy->r.currentOrigin );	
		advance = (qboolean)( distance > (NPCS.NPC->r.maxs[0]+MIN_DISTANCE) ? qtrue : qfalse  );
		doCharge = qfalse;

		if ( advance )
		{//have to get closer
			vec3_t	yawOnlyAngles;
			VectorSet( yawOnlyAngles, 0, NPCS.NPC->r.currentAngles[YAW], 0 );
			if ( NPCS.NPC->enemy->health > 0
				&& fabs(distance-250) <= 80 
				&& InFOV3( NPCS.NPC->enemy->r.currentOrigin, NPCS.NPC->r.currentOrigin, yawOnlyAngles, 30, 30 ) )
			{
				if ( !Q_irand( 0, 9 ) )
				{//go for the charge
					doCharge = qtrue;
					advance = qfalse;
				}
			}
		}

		if (( advance /*|| NPCInfo->localState == LSTATE_WAITING*/ ) && TIMER_Done( NPCS.NPC, "attacking" )) // waiting monsters can't attack
		{
			if ( TIMER_Done2( NPCS.NPC, "takingPain", qtrue ))
			{
				NPCS.NPCInfo->localState = LSTATE_CLEAR;
			}
			else
			{
				Rancor_Move( qtrue );
			}
		}
		else
		{
			Rancor_Attack( distance, doCharge );
		}
	}
}
Example #6
0
void Rancor_Attack( float distance, qboolean doCharge )
{

	if ( !TIMER_Exists( NPCS.NPC, "attacking" ) )
	{
		if ( NPCS.NPC->count == 2 && NPCS.NPC->activator )
		{
		}
		else if ( NPCS.NPC->count == 1 && NPCS.NPC->activator )
		{//holding enemy
			if ( NPCS.NPC->activator->health > 0 && Q_irand( 0, 1 ) )
			{//quick bite
				NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
				TIMER_Set( NPCS.NPC, "attack_dmg", 450 );
			}
			else
			{//full eat
				NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
				TIMER_Set( NPCS.NPC, "attack_dmg", 900 );
				//Make victim scream in fright
				if ( NPCS.NPC->activator->health > 0 && NPCS.NPC->activator->client )
				{
					G_AddEvent( NPCS.NPC->activator, Q_irand(EV_DEATH1, EV_DEATH3), 0 );
					NPC_SetAnim( NPCS.NPC->activator, SETANIM_TORSO, BOTH_FALLDEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
					if ( NPCS.NPC->activator->NPC )
					{//no more thinking for you
						TossClientItems( NPCS.NPC );
						NPCS.NPC->activator->NPC->nextBStateThink = Q3_INFINITE;
					}
				}
			}
		}
		else if ( NPCS.NPC->enemy->health > 0 && doCharge )
		{//charge
			vec3_t	fwd, yawAng;
			VectorSet( yawAng, 0, NPCS.NPC->client->ps.viewangles[YAW], 0 );
			AngleVectors( yawAng, fwd, NULL, NULL );
			VectorScale( fwd, distance*1.5f, NPCS.NPC->client->ps.velocity );
			NPCS.NPC->client->ps.velocity[2] = 150;
			NPCS.NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;

			NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_MELEE2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
			TIMER_Set( NPCS.NPC, "attack_dmg", 1250 );
		}
		else if ( !Q_irand(0, 1) )
		{//smash
			NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_MELEE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
			TIMER_Set( NPCS.NPC, "attack_dmg", 1000 );
		}
		else
		{//try to grab
			NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
			TIMER_Set( NPCS.NPC, "attack_dmg", 1000 );
		}

		TIMER_Set( NPCS.NPC, "attacking", NPCS.NPC->client->ps.legsTimer + random() * 200 );
	}

	// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks

	if ( TIMER_Done2( NPCS.NPC, "attack_dmg", qtrue ) )
	{
		vec3_t shakePos;
		switch ( NPCS.NPC->client->ps.legsAnim )
		{
		case BOTH_MELEE1:
			Rancor_Smash();
			G_GetBoltPosition( NPCS.NPC, NPCS.NPC->client->renderInfo.handLBolt, shakePos, 0 );
			G_ScreenShake( shakePos, NULL, 4.0f, 1000, qfalse );
			//CGCam_Shake( 1.0f*playerDist/128.0f, 1000 );
			break;
		case BOTH_MELEE2:
			Rancor_Bite();
			TIMER_Set( NPCS.NPC, "attack_dmg2", 450 );
			break;
		case BOTH_ATTACK1:
			if ( NPCS.NPC->count == 1 && NPCS.NPC->activator )
			{
				G_Damage( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC, vec3_origin, NPCS.NPC->activator->r.currentOrigin, Q_irand( 25, 40 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
				if ( NPCS.NPC->activator->health <= 0 )
				{//killed him
					//make it look like we bit his head off
					//NPC->activator->client->dismembered = qfalse;
					G_Dismember( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC->activator->r.currentOrigin, G2_MODELPART_HEAD, 90, 0, NPCS.NPC->activator->client->ps.torsoAnim, qtrue);
					//G_DoDismemberment( NPC->activator, NPC->activator->r.currentOrigin, MOD_SABER, 1000, HL_HEAD, qtrue );
					NPCS.NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE;
					NPCS.NPC->activator->client->ps.forceHandExtendTime = 0;
					NPC_SetAnim( NPCS.NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
				}
				G_Sound( NPCS.NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) );
			}
			break;
		case BOTH_ATTACK2:
			//try to grab
			Rancor_Swing( qtrue );
			break;
		case BOTH_ATTACK3:
			if ( NPCS.NPC->count == 1 && NPCS.NPC->activator )
			{
				//cut in half
				if ( NPCS.NPC->activator->client )
				{
					//NPC->activator->client->dismembered = qfalse;
					G_Dismember( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC->activator->r.currentOrigin, G2_MODELPART_WAIST, 90, 0, NPCS.NPC->activator->client->ps.torsoAnim, qtrue);
					//G_DoDismemberment( NPC->activator, NPC->enemy->r.currentOrigin, MOD_SABER, 1000, HL_WAIST, qtrue );
				}
				//KILL
				G_Damage( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC, vec3_origin, NPCS.NPC->activator->r.currentOrigin, NPCS.NPC->enemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_MELEE );//, HL_NONE );//
				if ( NPCS.NPC->activator->client )
				{
					NPCS.NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE;
					NPCS.NPC->activator->client->ps.forceHandExtendTime = 0;
					NPC_SetAnim( NPCS.NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
				}
				TIMER_Set( NPCS.NPC, "attack_dmg2", 1350 );
				G_Sound( NPCS.NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
				G_AddEvent( NPCS.NPC->activator, EV_JUMP, NPCS.NPC->activator->health );
			}
			break;
		}
	}
	else if ( TIMER_Done2( NPCS.NPC, "attack_dmg2", qtrue ) )
	{
		switch ( NPCS.NPC->client->ps.legsAnim )
		{
		case BOTH_MELEE1:
			break;
		case BOTH_MELEE2:
			Rancor_Bite();
			break;
		case BOTH_ATTACK1:
			break;
		case BOTH_ATTACK2:
			break;
		case BOTH_ATTACK3:
			if ( NPCS.NPC->count == 1 && NPCS.NPC->activator )
			{//swallow victim
				G_Sound( NPCS.NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) );
				//FIXME: sometimes end up with a live one in our mouths?
				//just make sure they're dead
				if ( NPCS.NPC->activator->health > 0 )
				{
					//cut in half
					//NPC->activator->client->dismembered = qfalse;
					G_Dismember( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC->activator->r.currentOrigin, G2_MODELPART_WAIST, 90, 0, NPCS.NPC->activator->client->ps.torsoAnim, qtrue);
					//G_DoDismemberment( NPC->activator, NPC->enemy->r.currentOrigin, MOD_SABER, 1000, HL_WAIST, qtrue );
					//KILL
					G_Damage( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC, vec3_origin, NPCS.NPC->activator->r.currentOrigin, NPCS.NPC->enemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_MELEE );//, HL_NONE );
					NPCS.NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE;
					NPCS.NPC->activator->client->ps.forceHandExtendTime = 0;
					NPC_SetAnim( NPCS.NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
					G_AddEvent( NPCS.NPC->activator, EV_JUMP, NPCS.NPC->activator->health );
				}
				if ( NPCS.NPC->activator->client )
				{//*sigh*, can't get tags right, just remove them?
					NPCS.NPC->activator->client->ps.eFlags |= EF_NODRAW;
				}
				NPCS.NPC->count = 2;
				TIMER_Set( NPCS.NPC, "clearGrabbed", 2600 );
			}
			break;
		}
	}
	else if ( NPCS.NPC->client->ps.legsAnim == BOTH_ATTACK2 )
	{
		if ( NPCS.NPC->client->ps.legsTimer >= 1200 && NPCS.NPC->client->ps.legsTimer <= 1350 )
		{
			if ( Q_irand( 0, 2 ) )
			{
				Rancor_Swing( qfalse );
			}
			else
			{
				Rancor_Swing( qtrue );
			}
		}
		else if ( NPCS.NPC->client->ps.legsTimer >= 1100 && NPCS.NPC->client->ps.legsTimer <= 1550 )
		{
			Rancor_Swing( qtrue );
		}
	}

	// Just using this to remove the attacking flag at the right time
	TIMER_Done2( NPCS.NPC, "attacking", qtrue );
}
Example #7
0
//----------------------------------
//replaced with SP version.
static void Howler_Combat( void )
{
	qboolean	faced = qfalse;
	float		distance;	
	qboolean	advance = qfalse;
	if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE )
	{//not on the ground
		if ( NPC->client->ps.legsAnim == BOTH_JUMP1
			|| NPC->client->ps.legsAnim == BOTH_INAIR1 )
		{//flying through the air with the greatest of ease, etc
			Howler_TryDamage( 10, qfalse, qfalse );
		}
	}
	else
	{//not in air, see if we should attack or advance
		// If we cannot see our target or we have somewhere to go, then do that
		if ( !NPC_ClearLOS4( NPC->enemy ) )//|| UpdateGoal( ))
		{
			NPCInfo->goalEntity = NPC->enemy;
			NPCInfo->goalRadius = MAX_DISTANCE;	// just get us within combat range

			if ( NPCInfo->localState == LSTATE_BERZERK )
			{
				NPC_Howler_Move( 3 );
			}
			else
			{
				NPC_Howler_Move( 10 );
			}
			NPC_UpdateAngles( qfalse, qtrue );
			return;
		}

		distance = DistanceHorizontal( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );	

		if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps ) )
		{//get really close to knocked down enemies
			advance = (qboolean)( distance > MIN_DISTANCE ? qtrue : qfalse  );
		}
		else
		{
			advance = (qboolean)( distance > MAX_DISTANCE ? qtrue : qfalse  );//MIN_DISTANCE
		}

		if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
		{
			if ( TIMER_Done2( NPC, "takingPain", qtrue ))
			{
				NPCInfo->localState = LSTATE_CLEAR;
			}
			else if ( TIMER_Done( NPC, "standing" ) )
			{
				faced = Howler_Move( qtrue );
			}
		}
		else
		{
			Howler_Attack( distance, qfalse );
		}
	}

	if ( !faced )
	{
		if ( //TIMER_Done( NPC, "standing" ) //not just standing there
			//!advance //not moving
			TIMER_Done( NPC, "attacking" ) )// not attacking
		{//not standing around
			// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
			NPC_FaceEnemy( qtrue );
		}
		else
		{
			NPC_UpdateAngles( qfalse, qtrue );
		}
	}
}
Example #8
0
static void Howler_Attack( float enemyDist, qboolean howl )
{
	int dmg = (NPCInfo->localState==LSTATE_BERZERK)?5:2;

	vec3_t boltOrg;
	vec3_t fwd;

	if ( !TIMER_Exists( NPC, "attacking" ))
	{
		int attackAnim = BOTH_GESTURE1;
		// Going to do an attack
		if ( NPC->enemy && NPC->enemy->client && PM_InKnockDown( &NPC->enemy->client->ps )
			&& enemyDist <= MIN_DISTANCE )
		{
			attackAnim = BOTH_ATTACK2;
		}
		else if ( !Q_irand( 0, 4 ) || howl )
		{//howl attack
			//G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" );
		}
		else if ( enemyDist > MIN_DISTANCE && Q_irand( 0, 1 ) )
		{//lunge attack
			//jump foward
			vec3_t	fwd, yawAng;
			
			VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 );
			AngleVectors( yawAng, fwd, NULL, NULL );
			VectorScale( fwd, (enemyDist*3.0f), NPC->client->ps.velocity );
			NPC->client->ps.velocity[2] = 200;
			NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
			
			attackAnim = BOTH_ATTACK1;
		}
		else
		{//tongue attack
			attackAnim = BOTH_ATTACK2;
		}

		NPC_SetAnim( NPC, SETANIM_BOTH, attackAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART );
		if ( NPCInfo->localState == LSTATE_BERZERK )
		{//attack again right away
			TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer );
		}
		else
		{
			TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + Q_irand( 0, 1500 ) );//FIXME: base on skill
			TIMER_Set( NPC, "standing", -level.time );
			TIMER_Set( NPC, "walking", -level.time );
			TIMER_Set( NPC, "running", NPC->client->ps.legsTimer + 5000 );
		}

		TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage
	}

	// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
	switch ( NPC->client->ps.legsAnim )
	{
	case BOTH_ATTACK1:
	case BOTH_MELEE1:
		if ( NPC->client->ps.legsTimer > 650//more than 13 frames left
			&& BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 800 )//at least 16 frames into anim
		{
			Howler_TryDamage( dmg, qfalse, qfalse );
		}
		break;
	case BOTH_ATTACK2:
	case BOTH_MELEE2:
		if ( NPC->client->ps.legsTimer > 350//more than 7 frames left
			&& BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 550 )//at least 11 frames into anim
		{
			Howler_TryDamage( dmg, qtrue, qfalse );
		}
		break;
	case BOTH_GESTURE1:
		{
			if ( NPC->client->ps.legsTimer > 1800//more than 36 frames left
				&& BG_AnimLength( NPC->localAnimIndex, (animNumber_t)NPC->client->ps.legsAnim ) - NPC->client->ps.legsTimer >= 950 )//at least 19 frames into anim
			{
				Howler_Howl();
				if ( !NPC->count )
				{
					//RAFIXME - this probably won't work correctly.
					G_GetBoltPosition(NPC, NPC->NPC->genericBolt1, boltOrg, 0);
					AngleVectors( NPC->client->ps.viewangles, fwd, NULL, NULL );
					G_PlayEffectID( G_EffectIndex( "howler/sonic" ), boltOrg, fwd);
					//G_PlayEffect( G_EffectIndex( "howler/sonic" ), NPC->ghoul2, NPC->NPC->genericBolt1, NPC->s.number, NPC->currentOrigin, 4750, qtrue );
					G_SoundOnEnt( NPC, CHAN_VOICE, "sound/chars/howler/howl.mp3" );
					NPC->count = 1;
				}
			}
		}
		break;
	default:
		//anims seem to get reset after a load, so just stop attacking and it will restart as needed.
		TIMER_Remove( NPC, "attacking" );
		break;
	}

	// Just using this to remove the attacking flag at the right time
	TIMER_Done2( NPC, "attacking", qtrue );
}
//----------------------------------
void Wampa_Combat( void )
{
	// If we cannot see our target or we have somewhere to go, then do that
	if ( !NPC_ClearLOS( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) )
	{
		if ( !Q_irand( 0, 10 ) )
		{
			if ( Wampa_CheckRoar( NPC ) )
			{
				return;
			}
		}
		NPCInfo->combatMove = qtrue;
		NPCInfo->goalEntity = NPC->enemy;
		NPCInfo->goalRadius = MAX_DISTANCE;	// just get us within combat range

		Wampa_Move( 0 );
		return;
	}
	else if ( UpdateGoal() )
	{
		NPCInfo->combatMove = qtrue;
		NPCInfo->goalEntity = NPC->enemy;
		NPCInfo->goalRadius = MAX_DISTANCE;	// just get us within combat range

		Wampa_Move( 1 );
		return;
	}
	else
	{
		float		distance = enemyDist = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );	
		qboolean	advance = (qboolean)( distance > (NPC->r.maxs[0]+MIN_DISTANCE) ? qtrue : qfalse  );
		qboolean	doCharge = qfalse;

		// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
		//FIXME: always seems to face off to the left or right?!!!!
		NPC_FaceEnemy( qtrue );


		if ( advance )
		{//have to get closer
			vec3_t	yawOnlyAngles;
			VectorSet( yawOnlyAngles, 0, NPC->r.currentAngles[YAW], 0 );
			if ( NPC->enemy->health > 0//enemy still alive
				&& fabs(distance-350) <= 80 //enemy anywhere from 270 to 430 away
				&& InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, yawOnlyAngles, 20, 20 ) )//enemy generally in front
			{//10% chance of doing charge anim
				if ( !Q_irand( 0, 9 ) )
				{//go for the charge
					doCharge = qtrue;
					advance = qfalse;
				}
			}
		}

		if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
		{
			if ( TIMER_Done2( NPC, "takingPain", qtrue ))
			{
				NPCInfo->localState = LSTATE_CLEAR;
			}
			else
			{
				Wampa_Move( 1 );
			}
		}
		else
		{
			if ( !Q_irand( 0, 20 ) )
			{//FIXME: only do this if we just damaged them or vice-versa?
				if ( Wampa_CheckRoar( NPC ) )
				{
					return;
				}
			}
			if ( !Q_irand( 0, 1 ) )
			{//FIXME: base on skill
				Wampa_Attack( distance, doCharge );
			}
		}
	}
}
//------------------------------
void Wampa_Attack( float distance, qboolean doCharge )
{
	if ( !TIMER_Exists( NPC, "attacking" ) )
	{
		if ( Q_irand(0, 2) && !doCharge )
		{//double slash
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
			TIMER_Set( NPC, "attack_dmg", 750 );
		}
		else if ( doCharge || (distance > 270 && distance < 430 && !Q_irand(0, 1)) )
		{//leap
			vec3_t	fwd, yawAng;
			VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 );
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
			TIMER_Set( NPC, "attack_dmg", 500 );
			AngleVectors( yawAng, fwd, NULL, NULL );
			VectorScale( fwd, distance*1.5f, NPC->client->ps.velocity );
			NPC->client->ps.velocity[2] = 150;
			NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
		}
		else
		{//backhand
			NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
			TIMER_Set( NPC, "attack_dmg", 250 );
		}

		TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + random() * 200 );
		//allow us to re-evaluate our running speed/anim
		TIMER_Set( NPC, "runfar", -1 );
		TIMER_Set( NPC, "runclose", -1 );
		TIMER_Set( NPC, "walk", -1 );
	}

	// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks

	if ( TIMER_Done2( NPC, "attack_dmg", qtrue ) )
	{
		switch ( NPC->client->ps.legsAnim )
		{
		case BOTH_ATTACK1:
			Wampa_Slash( NPC->client->renderInfo.handRBolt, qfalse );
			//do second hit
			TIMER_Set( NPC, "attack_dmg2", 100 );
			break;
		case BOTH_ATTACK2:
			Wampa_Slash( NPC->client->renderInfo.handRBolt, qfalse );
			TIMER_Set( NPC, "attack_dmg2", 100 );
			break;
		case BOTH_ATTACK3:
			Wampa_Slash( NPC->client->renderInfo.handLBolt, qtrue );
			break;
		}
	}
	else if ( TIMER_Done2( NPC, "attack_dmg2", qtrue ) )
	{
		switch ( NPC->client->ps.legsAnim )
		{
		case BOTH_ATTACK1:
			Wampa_Slash( NPC->client->renderInfo.handLBolt, qfalse );
			break;
		case BOTH_ATTACK2:
			Wampa_Slash( NPC->client->renderInfo.handLBolt, qfalse );
			break;
		}
	}

	// Just using this to remove the attacking flag at the right time
	TIMER_Done2( NPC, "attacking", qtrue );

	if ( NPC->client->ps.legsAnim == BOTH_ATTACK1 && distance > (NPC->r.maxs[0]+MIN_DISTANCE) )
	{//okay to keep moving
		ucmd.buttons |= BUTTON_WALKING;
		Wampa_Move( 1 );
	}
}