/* ------------------------- Mark1_Hunt - look for enemy. -------------------------` */ void Mark1_Hunt(void) { if ( NPCInfo->goalEntity == NULL ) { NPCInfo->goalEntity = NPC->enemy; } NPC_FaceEnemy( qtrue ); NPCInfo->combatMove = qtrue; NPC_MoveToGoal( qtrue ); }
/* ------------------------- Mark2_Hunt ------------------------- */ void Mark2_Hunt(void) { if ( NPCInfo->goalEntity == NULL ) { NPCInfo->goalEntity = NPC->enemy; } // Turn toward him before moving towards him. NPC_FaceEnemy( qtrue ); NPCInfo->combatMove = qtrue; NPC_MoveToGoal( qtrue ); }
void ImperialProbe_AttackDecision( void ) { float distance; qboolean visible; qboolean advance; // Always keep a good height off the ground ImperialProbe_MaintainHeight(); //randomly talk if ( TIMER_Done(NPC,"patrolNoise") ) { if (TIMER_Done(NPC,"angerNoise")) { G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) ); TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) ); } } // If we don't have an enemy, just idle if ( NPC_CheckEnemyExt(qfalse) == qfalse ) { ImperialProbe_Idle(); return; } NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL); // Rate our distance to the target, and our visibilty distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); visible = NPC_ClearLOS4( NPC->enemy ); advance = (qboolean)(distance > MIN_DISTANCE_SQR); // If we cannot see our target, move to see it if ( visible == qfalse ) { if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES ) { ImperialProbe_Hunt( visible, advance ); 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 ); // Decide what type of attack to do ImperialProbe_Ranged( visible, advance ); }
/* ------------------------- Sentry_AttackDecision ------------------------- */ void Sentry_AttackDecision( void ) { float distance; qboolean visible; qboolean advance; // Always keep a good height off the ground Sentry_MaintainHeight(); NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_2_lp" ); //randomly talk if ( TIMER_Done( NPC, "patrolNoise" ) ) { if ( TIMER_Done( NPC, "angerNoise" ) ) { G_SoundOnEnt( NPC, CHAN_AUTO, va( "sound/chars/sentry/misc/talk%d", Q_irand( 1, 3 ) ) ); TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) ); } } // He's dead. if ( NPC->enemy->health < 1 ) { NPC->enemy = NULL; Sentry_Idle(); return; } // If we don't have an enemy, just idle if ( NPC_CheckEnemyExt( qfalse ) == qfalse ) { Sentry_Idle(); return; } // Rate our distance to the target and visibilty distance = (int)DistanceHorizontalSquared( &NPC->r.currentOrigin, &NPC->enemy->r.currentOrigin ); visible = NPC_ClearLOS4( NPC->enemy ); advance = (qboolean)(distance > MIN_DISTANCE_SQR); // If we cannot see our target, move to see it if ( visible == qfalse ) { if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES ) { Sentry_Hunt( visible, advance ); return; } } NPC_FaceEnemy( qtrue ); Sentry_RangedAttack( visible, advance ); }
/* ------------------------- Interrogator_Attack ------------------------- */ void Interrogator_Attack( void ) { float distance; qboolean visible; qboolean advance; // Always keep a good height off the ground Interrogator_MaintainHeight(); //randomly talk if ( TIMER_Done(NPC,"patrolNoise") ) { if (TIMER_Done(NPC,"angerNoise")) { G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/talk.wav", Q_irand(1, 3)) ); TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) ); } } // If we don't have an enemy, just idle if ( NPC_CheckEnemyExt(qfalse) == qfalse ) { Interrogator_Idle(); return; } // Rate our distance to the target, and our visibilty distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); visible = NPC_ClearLOS4( NPC->enemy ); advance = (qboolean)(distance > MIN_DISTANCE*MIN_DISTANCE ); if ( !visible ) { advance = qtrue; } if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES ) { Interrogator_Hunt( visible, advance ); } NPC_FaceEnemy( qtrue ); if (!advance) { Interrogator_Melee( visible, advance ); } }
qboolean NPC_CheckCanAttackExt( void ) { //We don't want them to shoot if( NPCInfo->scriptFlags & SCF_DONT_FIRE ) return qfalse; //Turn to face if ( NPC_FaceEnemy( qtrue ) == qfalse ) return qfalse; //Must have a clear line of sight to the target if ( NPC_ClearShot( NPC->enemy ) == qfalse ) return qfalse; return qtrue; }
void Interrogator_Hunt( qboolean visible, qboolean advance ) { float distance, speed; vec3_t forward; Interrogator_PartsMove(); NPC_FaceEnemy(qfalse); //If we're not supposed to stand still, pursue the player if ( NPCInfo->standTime < level.time ) { // Only strafe when we can see the player if ( visible ) { Interrogator_Strafe(); if ( NPCInfo->standTime > level.time ) {//successfully strafed return; } } } //If we don't want to advance, stop here if ( advance == qfalse ) return; //Only try and navigate if the player is visible if ( visible == qfalse ) { // Move towards our goal NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 12; //Get our direction from the navigator if we can't see our target if ( NPC_GetMoveDirection( forward, &distance ) == qfalse ) return; } else { VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward ); distance = VectorNormalize( forward ); } speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill.integer; VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity ); }
//------------------------------------ void Seeker_Hunt( qboolean visible, qboolean advance ) { float distance, speed; vector3 forward; NPC_FaceEnemy( qtrue ); // If we're not supposed to stand still, pursue the player if ( NPCInfo->standTime < level.time ) { // Only strafe when we can see the player if ( visible ) { Seeker_Strafe(); return; } } // If we don't want to advance, stop here if ( advance == qfalse ) { return; } // Only try and navigate if the player is visible if ( visible == qfalse ) { // Move towards our goal NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 24; // Get our direction from the navigator if we can't see our target if ( NPC_GetMoveDirection( &forward, &distance ) == qfalse ) { return; } } else { VectorSubtract( &NPC->enemy->r.currentOrigin, &NPC->r.currentOrigin, &forward ); distance = VectorNormalize( &forward ); } speed = SEEKER_FORWARD_BASE_SPEED + SEEKER_FORWARD_MULTIPLIER * g_spSkill.integer; VectorMA( &NPC->client->ps.velocity, speed, &forward, &NPC->client->ps.velocity ); }
/* ------------------------- Reaver_Jump ------------------------- */ void Reaver_Jump( void ) { if ( NPC->enemy ) NPC_FaceEnemy(); // This seems a bit risky, but here I'm trying to actually delay the jump process so it's timed up better with its anims if ( NPCInfo->jumpTime - 2500 > level.time && level.time + 2700 > NPCInfo->jumpTime ) { vec3_t dir; AngleVectors( NPC->currentAngles, dir, NULL, NULL ); VectorScale( dir, REAVER_JUMP_VELOCITY, dir ); dir[2] += REAVER_JUMP_HEIGHT; VectorCopy( dir, NPC->client->ps.velocity ); } }
//------------------------------------ void Seeker_Hunt( qboolean visible, qboolean advance ) { float speed; vec3_t forward; NPC_FaceEnemy( qtrue ); // If we're not supposed to stand still, pursue the player if ( NPCInfo->standTime < level.time ) { // Only strafe when we can see the player if ( visible ) { Seeker_Strafe(); return; } } // If we don't want to advance, stop here if ( advance == qfalse ) { return; } // Only try and navigate if the player is visible if ( visible == qfalse ) { // Move towards our goal NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 24; NPC_MoveToGoal(qtrue); return; } else { VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward ); /*distance = */VectorNormalize( forward ); } speed = SEEKER_FORWARD_BASE_SPEED + SEEKER_FORWARD_MULTIPLIER * g_spskill->integer; VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity ); }
void Boba_DoSniper( gentity_t *self) { if (TIMER_Done(NPC, "PickNewSniperPoint")) { TIMER_Set(NPC, "PickNewSniperPoint", Q_irand(15000, 25000)); int SniperPoint = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_SNIPE|CP_CLEAR|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1); if (SniperPoint!=-1) { NPC_SetCombatPoint(SniperPoint); NPC_SetMoveGoal( NPC, level.combatPoints[SniperPoint].origin, 20, qtrue, SniperPoint ); } } if (Distance(NPC->currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin)<50.0f) { Boba_FireDecide(); } bool IsOnAPath = !!NPC_MoveToGoal(qtrue); // Resolve Blocked Problems //-------------------------- if (NPCInfo->aiFlags&NPCAI_BLOCKED && NPC->client->moveType!=MT_FLYSWIM && ((level.time - NPCInfo->blockedDebounceTime)>3000) ) { Boba_Printf("BLOCKED: Attempting Jump"); if (IsOnAPath) { if (!NPC_TryJump(NPCInfo->blockedTargetPosition)) { Boba_Printf(" Failed"); } } } NPC_FaceEnemy(qtrue); NPC_UpdateAngles( qtrue, qtrue ); }
//---------------------------------- 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(); } }
void RT_Flying_Think( void ) { if ( Q3_TaskIDPending( NPC, TID_MOVE_NAV ) && UpdateGoal() ) {//being scripted to go to a certain spot, don't maintain height if ( NPC_MoveToGoal( qtrue ) ) {//we could macro-nav to our goal if ( NPC->enemy && NPC->enemy->health && NPC->enemy->inuse ) { NPC_FaceEnemy( qtrue ); RT_FireDecide(); } } else {//frick, no where to nav to, keep us in the air! RT_Flying_MaintainHeight(); } return; } if ( NPC->random == 0.0f ) { // used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method. NPC->random = random() * 6.3f; // roughly 2pi } if ( NPC->enemy && NPC->enemy->health && NPC->enemy->inuse ) { RT_Flying_Attack(); RT_FireDecide(); return; } else { RT_Flying_MaintainHeight(); RT_RunStormtrooperAI(); return; } }
//---------------------------------- 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 ); } } }
/* ------------------------- Mark1_AttackDecision ------------------------- */ void Mark1_AttackDecision( void ) { int blasterTest,rocketTest; float distance; distance_e distRate; qboolean visible; qboolean advance; //randomly talk if ( TIMER_Done(NPC,"patrolNoise") ) { if (TIMER_Done(NPC,"angerNoise")) { TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) ); } } // Enemy is dead or he has no enemy. if ((NPC->enemy->health<1) || ( NPC_CheckEnemyExt(qfalse) == qfalse )) { NPC->enemy = NULL; return; } // Rate our distance to the target and visibility distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE; visible = NPC_ClearLOS4( NPC->enemy ); advance = (qboolean)(distance > MIN_DISTANCE_SQR); // If we cannot see our target, move to see it if ((!visible) || (!NPC_FaceEnemy(qtrue))) { Mark1_Hunt(); return; } // See if the side weapons are there blasterTest = trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "l_arm" ); rocketTest = trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "r_arm" ); // It has both side weapons if (!blasterTest && !rocketTest) { ; // So do nothing. } else if (blasterTest!=-1 &&blasterTest) { distRate = DIST_LONG; } else if (rocketTest!=-1 &&rocketTest) { distRate = DIST_MELEE; } else // It should never get here, but just in case { NPC->health = 0; NPC->client->ps.stats[STAT_HEALTH] = 0; if (NPC->die) { NPC->die(NPC, NPC, NPC, 100, MOD_UNKNOWN); } } // We can see enemy so shoot him if timers let you. NPC_FaceEnemy( qtrue ); if (distRate == DIST_MELEE) { Mark1_BlasterAttack(advance); } else if (distRate == DIST_LONG) { Mark1_RocketAttack(advance); } }
void RT_Flying_Hunt( qboolean visible, qboolean advance ) { float distance, speed; vec3_t forward; if ( NPC->forcePushTime >= level.time ) //|| (NPC->client->ps.eFlags&EF_FORCE_GRIPPED) ) {//if being pushed, we don't have control over our movement NPC->delay = 0; return; } NPC_FaceEnemy( qtrue ); // If we're not supposed to stand still, pursue the player if ( NPCInfo->standTime < level.time ) { // Only strafe when we can see the player if ( visible ) { NPC->delay = 0; RT_Flying_Strafe(); return; } } // If we don't want to advance, stop here if ( advance ) { // Only try and navigate if the player is visible if ( visible == qfalse ) { // Move towards our goal NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 24; NPC->delay = 0; NPC_MoveToGoal(qtrue); return; } } //else move straight at/away from him VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward ); forward[2] *= 0.1f; distance = VectorNormalize( forward ); speed = RT_FLYING_FORWARD_BASE_SPEED + RT_FLYING_FORWARD_MULTIPLIER * g_spskill->integer; if ( advance && distance < Q_flrand( 256, 3096 ) ) { NPC->delay = 0; VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity ); } else if ( distance < Q_flrand( 0, 128 ) ) { if ( NPC->health <= 50 ) {//always back off NPC->delay = 0; } else if ( !TIMER_Done( NPC, "backoffTime" ) ) {//still backing off from end of last delay NPC->delay = 0; } else if ( !NPC->delay ) {//start a new delay NPC->delay = Q_irand( 0, 10+(20*(2-g_spskill->integer)) ); } else {//continue the current delay NPC->delay--; } if ( !NPC->delay ) {//delay done, now back off for a few seconds! TIMER_Set( NPC, "backoffTime", Q_irand( 2000, 5000 ) ); VectorMA( NPC->client->ps.velocity, speed*-2, forward, NPC->client->ps.velocity ); } } else { NPC->delay = 0; } }
void NPC_BSSaberDroid_Attack( void ) {//attack behavior //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) == qfalse )//!NPC->enemy )// { NPC->enemy = NULL; NPC_BSSaberDroid_Patrol();//FIXME: or patrol? return; } if ( !NPC->enemy ) {//WTF? somehow we lost our enemy? NPC_BSSaberDroid_Patrol();//FIXME: or patrol? return; } enemyLOS = enemyCS = qfalse; move = qtrue; faceEnemy = qfalse; shoot = qfalse; enemyDist = DistanceSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ); //can we see our target? if ( NPC_ClearLOS4( NPC->enemy ) ) { NPCInfo->enemyLastSeenTime = level.time; enemyLOS = qtrue; if ( enemyDist <= 4096 && InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 90, 45 ) )//within 64 & infront { VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation ); 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 ( !TIMER_Done( NPC, "taunting" ) ) { move = qfalse; } else if ( enemyCS ) { shoot = qtrue; if ( enemyDist < (NPC->r.maxs[0]+NPC->enemy->r.maxs[0]+32)*(NPC->r.maxs[0]+NPC->enemy->r.maxs[0]+32) ) {//close enough move = qfalse; } }//this should make him chase enemy when out of range...? if ( NPC->client->ps.legsTimer && NPC->client->ps.legsAnim != BOTH_A3__L__R )//this one is a running attack {//in the middle of a held, stationary anim, can't move move = qfalse; } if ( move ) {//move toward goal move = SaberDroid_Move(); if ( move ) {//if we had to chase him, be sure to attack as soon as possible TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); } } 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(qtrue); } if ( NPCInfo->scriptFlags&SCF_DONT_FIRE ) { shoot = qfalse; } //FIXME: need predicted blocking? //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 {//attack! NPC_SaberDroid_PickAttack(); //set attac delay for next attack. if ( NPCInfo->rank > RANK_CREWMAN ) { TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime+Q_irand(0, 1000) ); } else { TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime+Q_irand( 0, 1000 )+(Q_irand( 0, (3-g_spskill.integer)*2 )*500) ); } } } } }
//---------------------------------- 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 ); } } } }
//---------------------------------- //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 ); } } }
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 ); } } }
void NPC_BSHirogenAlpha_Attack( void ) { //FIXME: Move to spawn location NPC->flags |= FL_NO_KNOCKBACK; if ( TIMER_Done( NPC, "speech" ) == qfalse ) { NPC_FaceEnemy( qtrue ); return; } //Do any taunts if need-be if ( Hirogen_Speak() ) return; //Check for the enemy (formality) if ( NPC_CheckEnemyExt() == qfalse ) { Hirogen_ActivateShield( qtrue ); NPC_BSHirogenAlpha_Idle(); return; } //See if we're currently attacking the player if ( NPCInfo->standTime > level.time ) { Hirogen_PauseState(); return; } //Turn our shielding on Hirogen_ActivateShield( qtrue ); //See if the target is visible if ( NPC_ClearLOS( NPC->enemy ) == qfalse ) { Hirogen_Hunt(); //This is done to keep the enemy from stopping too soon if ( DistanceSquared( NPC->currentOrigin, NPCInfo->tempGoal->currentOrigin ) > 96 ) { NPC_UpdateAngles( qtrue, qtrue ); return; } } //Must be at our goal if ( DistanceSquared( NPC->currentOrigin, NPCInfo->tempGoal->currentOrigin ) > 96 ) { Hirogen_Hunt(); return; } //Setup our timing information NPCInfo->pauseTime = level.time + HIROGEN_PREFIRE_DELAY; NPCInfo->weaponTime = NPCInfo->pauseTime + HIROGEN_FIRE_DURATION; NPCInfo->standTime = NPCInfo->weaponTime + HIROGEN_RELOAD_PAUSE; G_SoundOnEnt( NPC, CHAN_WEAPON, alphaSpeech[5] ); NPC_UpdateAngles( qtrue, qtrue ); }
/* ------------------------- NPC_BSWampa_Default ------------------------- */ void NPC_BSWampa_Default( void ) { NPC->client->ps.eFlags2 &= ~EF2_USE_ALT_ANIM; //NORMAL ANIMS // stand1 = normal stand // walk1 = normal, non-angry walk // walk2 = injured // run1 = far away run // run2 = close run //VICTIM ANIMS // grabswipe = melee1 - sweep out and grab // stand2 attack = attack4 - while holding victim, swipe at him // walk3_drag = walk5 - walk with drag // stand2 = hold victim // stand2to1 = drop victim if ( !TIMER_Done( NPC, "rageTime" ) ) {//do nothing but roar first time we see an enemy NPC_FaceEnemy( qtrue ); return; } if ( NPC->enemy ) { if ( !TIMER_Done(NPC,"attacking") ) {//in middle of attack //face enemy NPC_FaceEnemy( qtrue ); //continue attack logic enemyDist = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); Wampa_Attack( enemyDist, qfalse ); return; } else { if ( TIMER_Done(NPC,"angrynoise") ) { G_Sound( NPC, CHAN_VOICE, G_SoundIndex( va("sound/chars/wampa/misc/anger%d.wav", Q_irand(1, 2)) ) ); TIMER_Set( NPC, "angrynoise", Q_irand( 5000, 10000 ) ); } //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( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_WAMPA ) {//got mad at another Wampa, look for a valid enemy if ( TIMER_Done( NPC, "wampaInfight" ) ) { NPC_CheckEnemyExt( qtrue ); } } else { if ( ValidEnemy( NPC->enemy ) == qfalse ) { TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now if ( !NPC->enemy->inuse || level.time - 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 NPC->enemy = NULL; Wampa_Patrol(); NPC_UpdateAngles( qtrue, qtrue ); //just lost my enemy if ( (NPC->spawnflags&2) ) {//search around me if I don't have an enemy NPC_BSSearchStart( NPC->waypoint, BS_SEARCH ); NPCInfo->tempBehavior = BS_DEFAULT; } else if ( (NPC->spawnflags&1) ) {//wander if I don't have an enemy NPC_BSSearchStart( NPC->waypoint, BS_WANDER ); NPCInfo->tempBehavior = BS_DEFAULT; } return; } } if ( TIMER_Done( NPC, "lookForNewEnemy" ) ) { gentity_t *newEnemy, *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy? NPC->enemy = NULL; newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse ); NPC->enemy = sav_enemy; if ( newEnemy && newEnemy != sav_enemy ) {//picked up a new enemy! NPC->lastEnemy = NPC->enemy; G_SetEnemy( NPC, newEnemy ); //hold this one for at least 5-15 seconds TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) ); } else {//look again in 2-5 secs TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) ); } } } Wampa_Combat(); return; } } else { if ( TIMER_Done(NPC,"idlenoise") ) { G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/wampa/misc/anger3.wav" ) ); TIMER_Set( NPC, "idlenoise", Q_irand( 2000, 4000 ) ); } if ( (NPC->spawnflags&2) ) {//search around me if I don't have an enemy if ( NPCInfo->homeWp == WAYPOINT_NONE ) {//no homewap, initialize the search behavior NPC_BSSearchStart( WAYPOINT_NONE, BS_SEARCH ); NPCInfo->tempBehavior = BS_DEFAULT; } ucmd.buttons |= BUTTON_WALKING; NPC_BSSearch();//this automatically looks for enemies } else if ( (NPC->spawnflags&1) ) {//wander if I don't have an enemy if ( NPCInfo->homeWp == WAYPOINT_NONE ) {//no homewap, initialize the wander behavior NPC_BSSearchStart( WAYPOINT_NONE, BS_WANDER ); NPCInfo->tempBehavior = BS_DEFAULT; } ucmd.buttons |= BUTTON_WALKING; NPC_BSWander(); if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES ) { if ( NPC_CheckEnemyExt( qtrue ) == qfalse ) { Wampa_Idle(); } else { Wampa_CheckRoar( NPC ); TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) ); } } } else { if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES ) { Wampa_Patrol(); } else { Wampa_Idle(); } } } NPC_UpdateAngles( qtrue, qtrue ); }
/* ------------------------- Sentry_MaintainHeight ------------------------- */ void Sentry_MaintainHeight( void ) { float dif; NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_1_lp" ); // Update our angles regardless NPC_UpdateAngles( qtrue, qtrue ); // If we have an enemy, we should try to hover at about enemy eye level if ( NPC->enemy ) { // Find the height difference dif = (NPC->enemy->r.currentOrigin.z + NPC->enemy->r.maxs.z) - NPC->r.currentOrigin.z; // cap to prevent dramatic height shifts if ( fabsf( dif ) > 8 ) { if ( fabsf( dif ) > SENTRY_HOVER_HEIGHT ) { dif = (dif < 0 ? -24 : 24); } NPC->client->ps.velocity.z = (NPC->client->ps.velocity.z + dif) / 2; } } else { gentity_t *goal = NULL; if ( NPCInfo->goalEntity ) // Is there a goal? { goal = NPCInfo->goalEntity; } else { goal = NPCInfo->lastGoalEntity; } if ( goal ) { dif = goal->r.currentOrigin.z - NPC->r.currentOrigin.z; if ( fabsf( dif ) > SENTRY_HOVER_HEIGHT ) { ucmd.upmove = (ucmd.upmove < 0 ? -4 : 4); } else { if ( NPC->client->ps.velocity.z > 0.0f ) { NPC->client->ps.velocity.z *= SENTRY_VELOCITY_DECAY; if ( fabsf( NPC->client->ps.velocity.z ) < 2 ) { NPC->client->ps.velocity.z = 0; } } } } // Apply friction to Z else if ( NPC->client->ps.velocity.z > 0.0f ) { NPC->client->ps.velocity.z *= SENTRY_VELOCITY_DECAY; if ( fabsf( NPC->client->ps.velocity.z ) < 1 ) { NPC->client->ps.velocity.z = 0; } } } // Apply friction if ( NPC->client->ps.velocity.x > 0.0f ) { NPC->client->ps.velocity.x *= SENTRY_VELOCITY_DECAY; if ( fabsf( NPC->client->ps.velocity.x ) < 1 ) { NPC->client->ps.velocity.x = 0; } } if ( NPC->client->ps.velocity.y > 0.0f ) { NPC->client->ps.velocity.y *= SENTRY_VELOCITY_DECAY; if ( fabsf( NPC->client->ps.velocity.y ) < 1 ) { NPC->client->ps.velocity.y = 0; } } NPC_FaceEnemy( qtrue ); }
//replaced with SP version. void NPC_BSHowler_Default( void ) { if ( NPC->client->ps.legsAnim != BOTH_GESTURE1 ) { NPC->count = 0; } //FIXME: if in jump, do damage in front and maybe knock them down? if ( !TIMER_Done( NPC, "attacking" ) ) { if ( NPC->enemy ) { //NPC_FaceEnemy( qfalse ); Howler_Attack( Distance( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ), qfalse ); } else { //NPC_UpdateAngles( qfalse, qtrue ); Howler_Attack( 0.0f, qfalse ); } NPC_UpdateAngles( qfalse, qtrue ); return; } if ( NPC->enemy ) { if ( NPCInfo->stats.aggression > 0 ) { if ( TIMER_Done( NPC, "aggressionDecay" ) ) { NPCInfo->stats.aggression--; TIMER_Set( NPC, "aggressionDecay", 500 ); } } //RAFIXME - No fleeing, need to fix this at some point. /* if ( !TIMER_Done( NPC, "flee" ) && NPC_BSFlee() ) //this can clear ENEMY {//successfully trying to run away return; } */ if ( NPC->enemy == NULL) { NPC_UpdateAngles( qfalse, qtrue ); return; } if ( NPCInfo->localState == LSTATE_FLEE ) {//we were fleeing, now done (either timer ran out or we cannot flee anymore if ( NPC_ClearLOS4( NPC->enemy ) ) {//if enemy is still around, go berzerk NPCInfo->localState = LSTATE_BERZERK; } else {//otherwise, lick our wounds? NPCInfo->localState = LSTATE_CLEAR; TIMER_Set( NPC, "standing", Q_irand( 3000, 10000 ) ); } } else if ( NPCInfo->localState == LSTATE_BERZERK ) {//go nuts! } else if ( NPCInfo->stats.aggression >= Q_irand( 75, 125 ) ) {//that's it, go nuts! NPCInfo->localState = LSTATE_BERZERK; } else if ( !TIMER_Done( NPC, "retreating" ) ) {//trying to back off NPC_FaceEnemy( qtrue ); if ( NPC->client->ps.speed > NPCInfo->stats.walkSpeed ) { NPC->client->ps.speed = NPCInfo->stats.walkSpeed; } ucmd.buttons |= BUTTON_WALKING; if ( Distance( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) < HOWLER_RETREAT_DIST ) {//enemy is close vec3_t moveDir; AngleVectors( NPC->r.currentAngles, moveDir, NULL, NULL ); VectorScale( moveDir, -1, moveDir ); if ( !NAV_DirSafe( NPC, moveDir, 8 ) ) {//enemy is backing me up against a wall or ledge! Start to get really mad! NPCInfo->stats.aggression += 2; } else {//back off ucmd.forwardmove = -127; } //enemy won't leave me alone, get mad... NPCInfo->stats.aggression++; } return; } else if ( TIMER_Done( NPC, "standing" ) ) {//not standing around if ( !(NPCInfo->last_ucmd.forwardmove) && !(NPCInfo->last_ucmd.rightmove) ) {//stood last frame if ( TIMER_Done( NPC, "walking" ) && TIMER_Done( NPC, "running" ) ) {//not walking or running if ( Q_irand( 0, 2 ) ) {//run for a while TIMER_Set( NPC, "walking", Q_irand( 4000, 8000 ) ); } else {//walk for a bit TIMER_Set( NPC, "running", Q_irand( 2500, 5000 ) ); } } } else if ( (NPCInfo->last_ucmd.buttons&BUTTON_WALKING) ) {//walked last frame if ( TIMER_Done( NPC, "walking" ) ) {//just finished walking if ( Q_irand( 0, 5 ) || DistanceSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) < MAX_DISTANCE_SQR ) {//run for a while TIMER_Set( NPC, "running", Q_irand( 4000, 20000 ) ); } else {//stand for a bit TIMER_Set( NPC, "standing", Q_irand( 2000, 6000 ) ); } } } else {//ran last frame if ( TIMER_Done( NPC, "running" ) ) {//just finished running if ( Q_irand( 0, 8 ) || DistanceSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) < MAX_DISTANCE_SQR ) {//walk for a while TIMER_Set( NPC, "walking", Q_irand( 3000, 10000 ) ); } else {//stand for a bit TIMER_Set( NPC, "standing", Q_irand( 2000, 6000 ) ); } } } } if ( NPC_ValidEnemy( NPC->enemy ) == qfalse ) { TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now if ( !NPC->enemy->inuse || level.time - 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 NPC->enemy = NULL; Howler_Patrol(); NPC_UpdateAngles( qtrue, qtrue ); return; } } if ( TIMER_Done( NPC, "lookForNewEnemy" ) ) { gentity_t *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy? gentity_t *newEnemy; NPC->enemy = NULL; newEnemy = NPC_CheckEnemy( (qboolean)(NPCInfo->confusionTime < level.time), qfalse, qfalse ); NPC->enemy = sav_enemy; if ( newEnemy && newEnemy != sav_enemy ) {//picked up a new enemy! NPC->lastEnemy = NPC->enemy; G_SetEnemy( NPC, newEnemy ); if ( NPC->enemy != NPC->lastEnemy ) {//clear this so that we only sniff the player the first time we pick them up //RACC - this doesn't appear to get used for Howlers //NPC->useDebounceTime = 0; } //hold this one for at least 5-15 seconds TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) ); } else {//look again in 2-5 secs TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) ); } } Howler_Combat(); if ( TIMER_Done( NPC, "speaking" ) ) { if ( !TIMER_Done( NPC, "standing" ) || !TIMER_Done( NPC, "retreating" )) { G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/idle_hiss%d.mp3", Q_irand( 1, 2 ) ) ); } else if ( !TIMER_Done( NPC, "walking" ) || NPCInfo->localState == LSTATE_FLEE ) { G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/howl_talk%d.mp3", Q_irand( 1, 5 ) ) ); } else { G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/howl_yell%d.mp3", Q_irand( 1, 5 ) ) ); } if ( NPCInfo->localState == LSTATE_BERZERK || NPCInfo->localState == LSTATE_FLEE ) { TIMER_Set( NPC, "speaking", Q_irand( 1000, 4000 ) ); } else { TIMER_Set( NPC, "speaking", Q_irand( 3000, 8000 ) ); } } return; } else { if ( TIMER_Done( NPC, "speaking" ) ) { if ( !Q_irand( 0, 3 ) ) { G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/idle_hiss%d.mp3", Q_irand( 1, 2 ) ) ); } else { G_SoundOnEnt( NPC, CHAN_VOICE, va( "sound/chars/howler/howl_talk%d.mp3", Q_irand( 1, 5 ) ) ); } TIMER_Set( NPC, "speaking", Q_irand( 4000, 12000 ) ); } if ( NPCInfo->stats.aggression > 0 ) { if ( TIMER_Done( NPC, "aggressionDecay" ) ) { NPCInfo->stats.aggression--; TIMER_Set( NPC, "aggressionDecay", 200 ); } } if ( TIMER_Done( NPC, "standing" ) ) {//not standing around if ( !(NPCInfo->last_ucmd.forwardmove) && !(NPCInfo->last_ucmd.rightmove) ) {//stood last frame if ( TIMER_Done( NPC, "walking" ) && TIMER_Done( NPC, "running" ) ) {//not walking or running if ( NPCInfo->goalEntity ) {//have somewhere to go if ( Q_irand( 0, 2 ) ) {//walk for a while TIMER_Set( NPC, "walking", Q_irand( 3000, 10000 ) ); } else {//run for a bit TIMER_Set( NPC, "running", Q_irand( 2500, 5000 ) ); } } } } else if ( (NPCInfo->last_ucmd.buttons&BUTTON_WALKING) ) {//walked last frame if ( TIMER_Done( NPC, "walking" ) ) {//just finished walking if ( Q_irand( 0, 3 ) ) {//run for a while TIMER_Set( NPC, "running", Q_irand( 3000, 6000 ) ); } else {//stand for a bit TIMER_Set( NPC, "standing", Q_irand( 2500, 5000 ) ); } } } else {//ran last frame if ( TIMER_Done( NPC, "running" ) ) {//just finished running if ( Q_irand( 0, 2 ) ) {//walk for a while TIMER_Set( NPC, "walking", Q_irand( 6000, 15000 ) ); } else {//stand for a bit TIMER_Set( NPC, "standing", Q_irand( 4000, 6000 ) ); } } } } if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES ) { Howler_Patrol(); } else { Howler_Idle(); } } NPC_UpdateAngles( qfalse, qtrue ); }
/* ------------------------- 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 ); }
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; } } } }
/* ------------------------- Mark2_AttackDecision ------------------------- */ void Mark2_AttackDecision( void ) { float distance; qboolean visible; qboolean advance; NPC_FaceEnemy( qtrue ); distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); visible = NPC_ClearLOS4( NPC->enemy ); advance = (qboolean)(distance > MIN_DISTANCE_SQR); // He's been ordered to get up if (NPCInfo->localState == LSTATE_RISINGUP) { NPC->flags &= ~FL_SHIELDED; NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1START, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE ); if ((NPC->client->ps.legsTimer<=0) && NPC->client->ps.torsoAnim == BOTH_RUN1START ) { NPCInfo->localState = LSTATE_NONE; // He's up again. } return; } // If we cannot see our target, move to see it if ((!visible) || (!NPC_FaceEnemy(qtrue))) { // If he's going down or is down, make him get up if ((NPCInfo->localState == LSTATE_DOWN) || (NPCInfo->localState == LSTATE_DROPPINGDOWN)) { if ( TIMER_Done( NPC, "downTime" ) ) // Down being down?? (The delay is so he doesn't pop up and down when the player goes in and out of range) { NPCInfo->localState = LSTATE_RISINGUP; NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1STOP, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE ); TIMER_Set( NPC, "runTime", Q_irand( 3000, 8000) ); // So he runs for a while before testing to see if he should drop down. } } else { Mark2_Hunt(); } return; } // He's down but he could advance if he wants to. if ((advance) && (TIMER_Done( NPC, "downTime" )) && (NPCInfo->localState == LSTATE_DOWN)) { NPCInfo->localState = LSTATE_RISINGUP; NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1STOP, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE ); TIMER_Set( NPC, "runTime", Q_irand( 3000, 8000) ); // So he runs for a while before testing to see if he should drop down. } NPC_FaceEnemy( qtrue ); // Dropping down to shoot if (NPCInfo->localState == LSTATE_DROPPINGDOWN) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1STOP, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE ); TIMER_Set( NPC, "downTime", Q_irand( 3000, 9000) ); if ((NPC->client->ps.legsTimer<=0) && NPC->client->ps.torsoAnim == BOTH_RUN1STOP ) { NPC->flags |= FL_SHIELDED; NPCInfo->localState = LSTATE_DOWN; } } // He's down and shooting else if (NPCInfo->localState == LSTATE_DOWN) { NPC->flags |= FL_SHIELDED;//only damagable by lightsabers and missiles Mark2_BlasterAttack(qfalse); } else if (TIMER_Done( NPC, "runTime" )) // Lowering down to attack. But only if he's done running at you. { NPCInfo->localState = LSTATE_DROPPINGDOWN; } else if (advance) { // We can see enemy so shoot him if timer lets you. Mark2_BlasterAttack(advance); } }
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 ); } } } }
//------------------------------------ void Seeker_Hunt( qboolean visible, qboolean advance ) { float distance, speed; vec3_t forward; NPC_FaceEnemy( qtrue ); //[SeekerItemNpc] if(NPC->genericValue3) { if ( TIMER_Done( NPC, "seekerAlert" )) { //TODO: different sound for 'found enemy' or 'searching' ? /* if(visible) G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); else */ TIMER_Set(NPC, "seekerAlert", level.time + 900); G_Sound(NPC, CHAN_AUTO, NPC->genericValue3); } } //[/SeekerItemNpc] // If we're not supposed to stand still, pursue the player if ( NPCInfo->standTime < level.time ) { // Only strafe when we can see the player if ( visible ) { Seeker_Strafe(); return; } } // If we don't want to advance, stop here if ( advance == qfalse ) { return; } // Only try and navigate if the player is visible if ( visible == qfalse ) { // Move towards our goal NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 24; //[CoOp] NPC_MoveToGoal(qtrue); return; /* // Get our direction from the navigator if we can't see our target if ( NPC_GetMoveDirection( forward, &distance ) == qfalse ) { return; } */ //[/CoOp] } else { VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward ); distance = VectorNormalize( forward ); } speed = SEEKER_FORWARD_BASE_SPEED + SEEKER_FORWARD_MULTIPLIER * g_spskill.integer; VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity ); }
/* ------------------------- ATST_Attack ------------------------- */ void ATST_Attack( void ) { qboolean altAttack=qfalse; int blasterTest,chargerTest,weapon; if ( NPC_CheckEnemyExt() == qfalse )//!NPC->enemy )// { NPC->enemy = NULL; return; } NPC_FaceEnemy( qtrue ); // Rate our distance to the target, and our visibilty float distance = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); distance_e distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE; qboolean visible = NPC_ClearLOS( NPC->enemy ); qboolean advance = (qboolean)(distance > MIN_DISTANCE_SQR); // If we cannot see our target, move to see it if ( visible == qfalse ) { if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES ) { ATST_Hunt( visible, advance ); return; } } // Decide what type of attack to do switch ( distRate ) { case DIST_MELEE: NPC_ChangeWeapon( WP_ATST_MAIN ); break; case DIST_LONG: NPC_ChangeWeapon( WP_ATST_SIDE ); // See if the side weapons are there blasterTest = gi.G2API_GetSurfaceRenderStatus( &NPC->ghoul2[NPC->playerModel], "head_light_blaster_cann" ); chargerTest = gi.G2API_GetSurfaceRenderStatus( &NPC->ghoul2[NPC->playerModel], "head_concussion_charger" ); // It has both side weapons if (!(blasterTest & TURN_OFF) && !(chargerTest & TURN_OFF)) { weapon = Q_irand( 0, 1); // 0 is blaster, 1 is charger (ALT SIDE) if (weapon) // Fire charger { altAttack = qtrue; } else { altAttack = qfalse; } } else if (!(blasterTest & TURN_OFF)) // Blaster is on { altAttack = qfalse; } else if (!(chargerTest & TURN_OFF)) // Blaster is on { altAttack = qtrue; } else { NPC_ChangeWeapon( WP_NONE ); } break; } NPC_FaceEnemy( qtrue ); ATST_Ranged( visible, advance,altAttack ); }