qboolean InFOV2( vec3_t origin, gentity_t *from, int hFOV, int vFOV ) { vec3_t fromAngles, eyes; if( from->client ) { VectorCopy(from->client->ps.viewangles, fromAngles); } else { VectorCopy(from->s.angles, fromAngles); } CalcEntitySpot( from, SPOT_HEAD, eyes ); return InFOV3( origin, eyes, fromAngles, hFOV, vFOV ); }
//---------------------------------- 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 ); } } }
//---------------------------------- 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 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) == 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; } enemyLOS3 = enemyCS3 = qfalse; move3 = qtrue; faceEnemy3 = qfalse; shoot3 = qfalse; enemyDist3 = DistanceSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ); //See if we should switch to melee attack if ( enemyDist3 < 16384 //128 && (!NPC->enemy->client || NPC->enemy->client->ps.weapon != WP_SABER || BG_SabersOff( &NPC->enemy->client->ps ) ) ) {//enemy is close and not using saber if ( NPC->client->ps.weapon == WP_THERMAL ) {//grenadier trace_t trace; trap_Trace ( &trace, NPC->r.currentOrigin, NPC->enemy->r.mins, NPC->enemy->r.maxs, NPC->enemy->r.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_STUN_BATON ); 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 ( enemyDist3 > 65536 || (NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && !NPC->enemy->client->ps.saberHolstered) )//256 {//enemy is far or using saber if ( NPC->client->ps.weapon == WP_STUN_BATON && (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_ClearLOS4( NPC->enemy ) ) { NPCInfo->enemyLastSeenTime = level.time; enemyLOS3 = qtrue; if ( NPC->client->ps.weapon == WP_STUN_BATON ) { if ( enemyDist3 <= 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 ); enemyCS3 = qtrue; } } else if ( InFOV3( NPC->enemy->r.currentOrigin, NPC->r.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, NULL ); gentity_t *hitEnt = &g_entities[hit]; if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) ) { float enemyHorzDist; VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation ); enemyHorzDist = DistanceHorizontalSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ); if ( enemyHorzDist < 1048576 ) {//within 1024 enemyCS3 = 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 ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) ) { NPCInfo->enemyLastSeenTime = level.time; faceEnemy3 = qtrue; } */ if ( enemyLOS3 ) {//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot? faceEnemy3 = qtrue; } if ( enemyCS3 ) { shoot3 = qtrue; if ( NPC->client->ps.weapon == WP_THERMAL ) {//don't chase and throw move3 = qfalse; } else if ( NPC->client->ps.weapon == WP_STUN_BATON && enemyDist3 < (NPC->r.maxs[0]+NPC->enemy->r.maxs[0]+16)*(NPC->r.maxs[0]+NPC->enemy->r.maxs[0]+16) ) {//close enough move3 = 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 ( move3 ) {//move toward goal if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist3 > 10000 ) )//100 squared { move3 = Grenadier_Move(); } else { move3 = qfalse; } } if ( !move3 ) { if ( !TIMER_Done( NPC, "duck" ) ) { ucmd.upmove = -127; } //FIXME: what about leaning? } else {//stop ducking! TIMER_Set( NPC, "duck", -1 ); } if ( !faceEnemy3 ) {//we want to face in the dir we're running if ( move3 ) {//don't run away and shoot NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW]; NPCInfo->desiredPitch = 0; shoot3 = qfalse; } NPC_UpdateAngles( qtrue, qtrue ); } else// if ( faceEnemy3 ) {//face the enemy NPC_FaceEnemy(qtrue); } if ( NPCInfo->scriptFlags&SCF_DONT_FIRE ) { shoot3 = qfalse; } //FIXME: don't shoot right away! if ( shoot3 ) {//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 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) ); } } } } }