/* void NPC_BSPatrol( void ) Same as idle, but you look for enemies every "vigilance" using your angles, HFOV, VFOV and visrange, and listen for sounds within earshot... */ void NPC_BSPatrol( void ) { //int alertEventNum; if(level.time > NPCInfo->enemyCheckDebounceTime) { NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 1000); NPC_CheckEnemy(qtrue, qfalse, qtrue); if(NPC->enemy) {//FIXME: do anger script NPCInfo->behaviorState = BS_HUNT_AND_KILL; //NPC_AngerSound(); return; } } //FIXME: Implement generic sound alerts /* alertEventNum = NPC_CheckAlertEvents( qtrue, qtrue ); if( alertEventNum != -1 ) {//If we heard something, see if we should check it out if ( NPC_CheckInvestigate( alertEventNum ) ) { return; } } */ NPCInfo->investigateSoundDebounceTime = 0; //FIXME if there is no nav data, we need to do something else // if we're stuck, try to move around it if ( UpdateGoal() ) { NPC_MoveToGoal( qtrue ); } NPC_UpdateAngles( qtrue, qtrue ); ucmd.buttons |= BUTTON_WALKING; }
/* void NPC_BSMove(void) Move in a direction, face another */ void NPC_BSMove(void) { gentity_t *goal = NULL; NPC_CheckEnemy(qtrue, qfalse, qtrue); if(NPC->enemy) { NPC_CheckCanAttack(1.0, qfalse); } else { NPC_UpdateAngles(qtrue, qtrue); } goal = UpdateGoal(); if(goal) { // NPCInfo->moveToGoalMod = 1.0; NPC_SlideMoveToGoal(); } }
void NPC_BSSearch (void) { NPC_CheckEnemy(qtrue, qfalse); //Look for enemies, if find one: if ( NPC->enemy ) { if( NPCInfo->tempBehavior == BS_SEARCH ) {//if tempbehavior, set tempbehavior to default NPCInfo->tempBehavior = BS_DEFAULT; } else {//if bState, change to run and shoot NPCInfo->behaviorState = BS_HUNT_AND_KILL; NPC_BSRunAndShoot(); } return; } //FIXME: what if our goalEntity is not NULL and NOT our tempGoal - they must //want us to do something else? If tempBehavior, just default, else set //to run and shoot...? //FIXME: Reimplement if ( !NPCInfo->investigateDebounceTime ) {//On our way to a tempGoal float minGoalReachedDistSquared = 32*32; vec3_t vec; //Keep moving toward our tempGoal NPCInfo->goalEntity = NPCInfo->tempGoal; VectorSubtract ( NPCInfo->tempGoal->currentOrigin, NPC->currentOrigin, vec); if ( vec[2] < 24 ) { vec[2] = 0; } if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE ) { /* //FIXME: can't get the radius... float wpRadSq = waypoints[NPCInfo->tempGoal->waypoint].radius * waypoints[NPCInfo->tempGoal->waypoint].radius; if ( minGoalReachedDistSquared > wpRadSq ) { minGoalReachedDistSquared = wpRadSq; } */ minGoalReachedDistSquared = 32*32;//12*12; } if ( VectorLengthSquared( vec ) < minGoalReachedDistSquared ) { //Close enough, just got there NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE ); if ( ( NPCInfo->homeWp == WAYPOINT_NONE ) || ( NPC->waypoint == WAYPOINT_NONE ) ) { //Heading for or at an invalid waypoint, get out of this bState if( NPCInfo->tempBehavior == BS_SEARCH ) {//if tempbehavior, set tempbehavior to default NPCInfo->tempBehavior = BS_DEFAULT; } else {//if bState, change to stand guard NPCInfo->behaviorState = BS_STAND_GUARD; NPC_BSRunAndShoot(); } return; } if ( NPC->waypoint == NPCInfo->homeWp ) { //Just Reached our homeWp, if this is the first time, run your lostenemyscript if ( NPCInfo->aiFlags & NPCAI_ENROUTE_TO_HOMEWP ) { NPCInfo->aiFlags &= ~NPCAI_ENROUTE_TO_HOMEWP; G_ActivateBehavior( NPC, BSET_LOSTENEMY ); } } //gi.Printf("Got there.\n"); //gi.Printf("Looking..."); if( !Q_irand(0, 1) ) { NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL); } else { NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL); } NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000); } else { NPC_MoveToGoal( qtrue ); } } else { //We're there if ( NPCInfo->investigateDebounceTime > level.time ) { //Still waiting around for a bit //Turn angles every now and then to look around if ( NPCInfo->tempGoal->waypoint != WAYPOINT_NONE ) { if ( !Q_irand( 0, 30 ) ) { int numEdges = navigator.GetNodeNumEdges( NPCInfo->tempGoal->waypoint ); if ( numEdges != WAYPOINT_NONE ) { int branchNum = Q_irand( 0, numEdges - 1 ); vec3_t branchPos, lookDir; int nextWp = navigator.GetNodeEdge( NPCInfo->tempGoal->waypoint, branchNum ); navigator.GetNodePosition( nextWp, branchPos ); VectorSubtract( branchPos, NPCInfo->tempGoal->currentOrigin, lookDir ); NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + Q_flrand( -45, 45 ) ); } //pick an angle +-45 degrees off of the dir of a random branch //from NPCInfo->tempGoal->waypoint //int branch = Q_irand( 0, (waypoints[NPCInfo->tempGoal->waypoint].numNeighbors - 1) ); //int nextWp = waypoints[NPCInfo->tempGoal->waypoint].nextWaypoint[branch][NPCInfo->stats.moveType]; //vec3_t lookDir; //VectorSubtract( waypoints[nextWp].origin, NPCInfo->tempGoal->currentOrigin, lookDir ); //Look in that direction +- 45 degrees //NPCInfo->desiredYaw = AngleNormalize360( vectoyaw( lookDir ) + Q_flrand( -45, 45 ) ); } } //gi.Printf("."); } else {//Just finished waiting NPC->waypoint = NAV_FindClosestWaypointForEnt( NPC, WAYPOINT_NONE ); if ( NPC->waypoint == NPCInfo->homeWp ) { int numEdges = navigator.GetNodeNumEdges( NPCInfo->tempGoal->waypoint ); if ( numEdges != WAYPOINT_NONE ) { int branchNum = Q_irand( 0, numEdges - 1 ); int nextWp = navigator.GetNodeEdge( NPCInfo->homeWp, branchNum ); navigator.GetNodePosition( nextWp, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = nextWp; } /* //Pick a random branch int branch = Q_irand( 0, (waypoints[NPCInfo->homeWp].numNeighbors - 1) ); int nextWp = waypoints[NPCInfo->homeWp].nextWaypoint[branch][NPCInfo->stats.moveType]; VectorCopy( waypoints[nextWp].origin, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = nextWp; //gi.Printf("\nHeading for wp %d...\n", waypoints[NPCInfo->homeWp].nextWaypoint[branch][NPCInfo->stats.moveType]); */ } else {//At a branch, so return home navigator.GetNodePosition( NPCInfo->homeWp, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = NPCInfo->homeWp; /* VectorCopy( waypoints[NPCInfo->homeWp].origin, NPCInfo->tempGoal->currentOrigin ); NPCInfo->tempGoal->waypoint = NPCInfo->homeWp; //gi.Printf("\nHeading for wp %d...\n", NPCInfo->homeWp); */ } NPCInfo->investigateDebounceTime = 0; //Start moving toward our tempGoal NPCInfo->goalEntity = NPCInfo->tempGoal; NPC_MoveToGoal( qtrue ); } } NPC_UpdateAngles( qtrue, qtrue ); }
void NPC_BSFollowLeader (void) { vec3_t vec; float leaderDist; visibility_t leaderVis; int curAnim; if ( !NPC->client->leader ) {//ok, stand guard until we find an enemy if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL ) { NPCInfo->tempBehavior = BS_DEFAULT; } else { NPCInfo->tempBehavior = BS_STAND_GUARD; NPC_BSStandGuard(); } return; } if ( !NPC->enemy ) {//no enemy, find one NPC_CheckEnemy( NPCInfo->confusionTime<level.time, qfalse );//don't find new enemy if this is tempbehav if ( NPC->enemy ) {//just found one NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); } else { if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) { int eventID = NPC_CheckAlertEvents( qtrue, qtrue ); if ( level.alertEvents[eventID].level >= AEL_SUSPICIOUS && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) ) { NPCInfo->lastAlertID = level.alertEvents[eventID].ID; if ( !level.alertEvents[eventID].owner || !level.alertEvents[eventID].owner->client || level.alertEvents[eventID].owner->health <= 0 || level.alertEvents[eventID].owner->client->playerTeam != NPC->client->enemyTeam ) {//not an enemy } else { //FIXME: what if can't actually see enemy, don't know where he is... should we make them just become very alert and start looking for him? Or just let combat AI handle this... (act as if you lost him) G_SetEnemy( NPC, level.alertEvents[eventID].owner ); NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); NPCInfo->enemyLastSeenTime = level.time; TIMER_Set( NPC, "attackDelay", Q_irand( 500, 1000 ) ); } } } } if ( !NPC->enemy ) { if ( NPC->client->leader && NPC->client->leader->enemy && NPC->client->leader->enemy != NPC && ( (NPC->client->leader->enemy->client&&NPC->client->leader->enemy->client->playerTeam==NPC->client->enemyTeam) ||(NPC->client->leader->enemy->svFlags&SVF_NONNPC_ENEMY&&NPC->client->leader->enemy->noDamageTeam==NPC->client->enemyTeam) ) && NPC->client->leader->enemy->health > 0 ) { G_SetEnemy( NPC, NPC->client->leader->enemy ); NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); NPCInfo->enemyLastSeenTime = level.time; } } } else { if ( NPC->enemy->health <= 0 || (NPC->enemy->flags&FL_NOTARGET) ) { G_ClearEnemy( NPC ); if ( NPCInfo->enemyCheckDebounceTime > level.time + 1000 ) { NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 2000 ); } } else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time ) { NPC_CheckEnemy( (NPCInfo->confusionTime<level.time||NPCInfo->tempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav } } if ( NPC->enemy && NPC->client->ps.weapon ) {//If have an enemy, face him and fire if ( NPC->client->ps.weapon == WP_SABER )//|| NPCInfo->confusionTime>level.time ) {//lightsaber user or charmed enemy if ( NPCInfo->tempBehavior != BS_FOLLOW_LEADER ) {//not already in a temp bState //go after the guy NPCInfo->tempBehavior = BS_HUNT_AND_KILL; NPC_UpdateAngles(qtrue, qtrue); return; } } enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|CHECK_PVS| if ( enemyVisibility > VIS_PVS ) {//face vec3_t enemy_org, muzzle, delta, angleToEnemy; float distanceToEnemy; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org ); NPC_AimWiggle( enemy_org ); CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); VectorSubtract( enemy_org, muzzle, delta); vectoangles( delta, angleToEnemy ); distanceToEnemy = VectorNormalize( delta ); NPCInfo->desiredYaw = angleToEnemy[YAW]; NPCInfo->desiredPitch = angleToEnemy[PITCH]; NPC_UpdateFiringAngles( qtrue, qtrue ); if ( enemyVisibility >= VIS_SHOOT ) {//shoot NPC_AimAdjust( 2 ); if ( NPC_GetHFOVPercentage( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.hfov ) > 0.6f && NPC_GetHFOVPercentage( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.vfov ) > 0.5f ) {//actually withing our front cone WeaponThink( qtrue ); } } else { NPC_AimAdjust( 1 ); } //NPC_CheckCanAttack(1.0, qfalse); } else { NPC_AimAdjust( -1 ); } } else {//FIXME: combine with vector calc below vec3_t head, leaderHead, delta, angleToLeader; CalcEntitySpot( NPC->client->leader, SPOT_HEAD, leaderHead ); CalcEntitySpot( NPC, SPOT_HEAD, head ); VectorSubtract (leaderHead, head, delta); vectoangles ( delta, angleToLeader ); VectorNormalize(delta); NPC->NPC->desiredYaw = angleToLeader[YAW]; NPC->NPC->desiredPitch = angleToLeader[PITCH]; NPC_UpdateAngles(qtrue, qtrue); } //leader visible? leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_SHOOT );// ent->e_UseFunc = useF_NULL; //Follow leader, stay within visibility and a certain distance, maintain a distance from. curAnim = NPC->client->ps.legsAnim; if ( curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 ) {//Don't move toward leader if we're in a full-body attack anim //FIXME, use IdealDistance to determine if we need to close distance float followDist = 96.0f;//FIXME: If there are enmies, make this larger? float backupdist, walkdist, minrundist; if ( NPCInfo->followDist ) { followDist = NPCInfo->followDist; } backupdist = followDist/2.0f; walkdist = followDist*0.83; minrundist = followDist*1.33; VectorSubtract(NPC->client->leader->currentOrigin, NPC->currentOrigin, vec); leaderDist = VectorLength( vec );//FIXME: make this just nav distance? //never get within their radius horizontally vec[2] = 0; float leaderHDist = VectorLength( vec ); if( leaderHDist > backupdist && (leaderVis != VIS_SHOOT || leaderDist > walkdist) ) {//We should close in? NPCInfo->goalEntity = NPC->client->leader; NPC_SlideMoveToGoal(); if ( leaderVis == VIS_SHOOT && leaderDist < minrundist ) { ucmd.buttons |= BUTTON_WALKING; } } else if ( leaderDist < backupdist ) {//We should back off? NPCInfo->goalEntity = NPC->client->leader; NPC_SlideMoveToGoal(); //reversing direction ucmd.forwardmove = -ucmd.forwardmove; ucmd.rightmove = -ucmd.rightmove; VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir ); }//otherwise, stay where we are //check for do not enter and stop if there's one there... if ( ucmd.forwardmove || ucmd.rightmove || VectorCompare( vec3_origin, NPC->client->ps.moveDir ) ) { NPC_MoveDirClear( ucmd.forwardmove, ucmd.rightmove, qtrue ); } } }
/* void NPC_BSAdvanceFight (void) Advance towards your captureGoal and shoot anyone you can along the way. */ void NPC_BSAdvanceFight (void) {//FIXME: IMPLEMENT //Head to Goal if I can //Make sure we're still headed where we want to capture if ( NPCInfo->captureGoal ) {//FIXME: if no captureGoal, what do we do? //VectorCopy( NPCInfo->captureGoal->currentOrigin, NPCInfo->tempGoal->currentOrigin ); //NPCInfo->goalEntity = NPCInfo->tempGoal; NPC_SetMoveGoal( NPC, NPCInfo->captureGoal->currentOrigin, 16, qtrue ); // NAV_ClearLastRoute(NPC); NPCInfo->goalTime = level.time + 100000; } // NPC_BSRun(); NPC_CheckEnemy(qtrue, qfalse); //FIXME: Need melee code if( NPC->enemy ) {//See if we can shoot him vec3_t delta, forward; vec3_t angleToEnemy; vec3_t hitspot, muzzle, diff, enemy_org, enemy_head; float distanceToEnemy; qboolean attack_ok = qfalse; qboolean dead_on = qfalse; float attack_scale = 1.0; float aim_off; float max_aim_off = 64; //Yaw to enemy VectorMA(NPC->enemy->absmin, 0.5, NPC->enemy->maxs, enemy_org); CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); VectorSubtract (enemy_org, muzzle, delta); vectoangles ( delta, angleToEnemy ); distanceToEnemy = VectorNormalize(delta); if(!NPC_EnemyTooFar(NPC->enemy, distanceToEnemy*distanceToEnemy, qtrue)) { attack_ok = qtrue; } if(attack_ok) { NPC_UpdateShootAngles(angleToEnemy, qfalse, qtrue); NPCInfo->enemyLastVisibility = enemyVisibility; enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV);//CHECK_360|//CHECK_PVS| if(enemyVisibility == VIS_FOV) {//He's in our FOV attack_ok = qtrue; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_head); if(attack_ok) { trace_t tr; gentity_t *traceEnt; //are we gonna hit him if we shoot at his center? gi.trace ( &tr, muzzle, NULL, NULL, enemy_org, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 ); traceEnt = &g_entities[tr.entityNum]; if( traceEnt != NPC->enemy && (!traceEnt || !traceEnt->client || !NPC->client->enemyTeam || NPC->client->enemyTeam != traceEnt->client->playerTeam) ) {//no, so shoot for the head attack_scale *= 0.75; gi.trace ( &tr, muzzle, NULL, NULL, enemy_head, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 ); traceEnt = &g_entities[tr.entityNum]; } VectorCopy( tr.endpos, hitspot ); if( traceEnt == NPC->enemy || (traceEnt->client && NPC->client->enemyTeam && NPC->client->enemyTeam == traceEnt->client->playerTeam) ) { dead_on = qtrue; } else { attack_scale *= 0.5; if(NPC->client->playerTeam) { if(traceEnt && traceEnt->client && traceEnt->client->playerTeam) { if(NPC->client->playerTeam == traceEnt->client->playerTeam) {//Don't shoot our own team attack_ok = qfalse; } } } } } if( attack_ok ) { //ok, now adjust pitch aim VectorSubtract (hitspot, muzzle, delta); vectoangles ( delta, angleToEnemy ); NPC->NPC->desiredPitch = angleToEnemy[PITCH]; NPC_UpdateShootAngles(angleToEnemy, qtrue, qfalse); if( !dead_on ) {//We're not going to hit him directly, try a suppressing fire //see if where we're going to shoot is too far from his origin AngleVectors (NPCInfo->shootAngles, forward, NULL, NULL); VectorMA ( muzzle, distanceToEnemy, forward, hitspot); VectorSubtract(hitspot, enemy_org, diff); aim_off = VectorLength(diff); if(aim_off > random() * max_aim_off)//FIXME: use aim value to allow poor aim? { attack_scale *= 0.75; //see if where we're going to shoot is too far from his head VectorSubtract(hitspot, enemy_head, diff); aim_off = VectorLength(diff); if(aim_off > random() * max_aim_off) { attack_ok = qfalse; } } attack_scale *= (max_aim_off - aim_off + 1)/max_aim_off; } } } } if( attack_ok ) { if( NPC_CheckAttack( attack_scale )) {//check aggression to decide if we should shoot enemyVisibility = VIS_SHOOT; WeaponThink(qtrue); } else attack_ok = qfalse; } //Don't do this- only for when stationary and trying to shoot an enemy // else // NPC->cantHitEnemyCounter++; } else {//FIXME: NPC_UpdateShootAngles(NPC->client->ps.viewangles, qtrue, qtrue); } if(!ucmd.forwardmove && !ucmd.rightmove) {//We reached our captureGoal if(NPC->taskManager) { Q3_TaskIDComplete( NPC, TID_BSTATE ); } } }
/* ------------------------- 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_BSDefault( void ) { // vec3_t enemyDir; // float enemyDist; // float shootDist; // qboolean enemyFOV = qfalse; // qboolean enemyShotFOV = qfalse; // qboolean enemyPVS = qfalse; // vec3_t enemyHead; // vec3_t muzzle; // qboolean enemyLOS = qfalse; // qboolean enemyCS = qfalse; qboolean move = qtrue; // qboolean shoot = qfalse; if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON ) { WeaponThink( qtrue ); } if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH ) {//being forced to walk if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START ) { NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD ); } } //look for a new enemy if don't have one and are allowed to look, validate current enemy if have one NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse, qtrue ); if ( !NPC->enemy ) {//still don't have an enemy if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) {//check for alert events //FIXME: Check Alert events, see if we should investigate or just look at it int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED ); //There is an event to look at if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID ) {//heard/saw something if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) ) {//was a big event if ( level.alertEvents[alertEvent].owner && level.alertEvents[alertEvent].owner->client && level.alertEvents[alertEvent].owner->health >= 0 && level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam ) {//an enemy G_SetEnemy( NPC, level.alertEvents[alertEvent].owner ); } } else {//FIXME: investigate lesser events } } //FIXME: also check our allies' condition? } } if ( NPC->enemy && !(NPCInfo->scriptFlags&SCF_FORCED_MARCH) ) { // just use the stormtrooper attack AI... NPC_CheckGetNewWeapon(); if ( NPC->client->leader && NPCInfo->goalEntity == NPC->client->leader && !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) ) { NPC_ClearGoal(); } NPC_BSST_Attack(); return; /* //have an enemy //FIXME: if one of these fails, meaning we can't shoot, do we really need to do the rest? VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir ); enemyDist = VectorNormalize( enemyDir ); enemyDist *= enemyDist; shootDist = NPC_MaxDistSquaredForWeapon(); enemyFOV = InFOV( NPC->enemy, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ); enemyShotFOV = InFOV( NPC->enemy, NPC, 20, 20 ); enemyPVS = gi.inPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ); if ( enemyPVS ) {//in the pvs trace_t tr; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemyHead ); enemyHead[2] -= Q_flrand( 0.0f, NPC->enemy->maxs[2]*0.5f ); CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); enemyLOS = NPC_ClearLOS( muzzle, enemyHead ); gi.trace ( &tr, muzzle, vec3_origin, vec3_origin, enemyHead, NPC->s.number, MASK_SHOT ); enemyCS = NPC_EvaluateShot( tr.entityNum, qtrue ); } else {//skip thr 2 traces since they would have to fail enemyLOS = qfalse; enemyCS = qfalse; } if ( enemyCS && enemyShotFOV ) {//can hit enemy if we want NPC->cantHitEnemyCounter = 0; } else {//can't hit NPC->cantHitEnemyCounter++; } if ( enemyCS && enemyShotFOV && enemyDist < shootDist ) {//can shoot shoot = qtrue; if ( NPCInfo->goalEntity == NPC->enemy ) {//my goal is my enemy and I have a clear shot, no need to chase right now move = qfalse; } } else {//don't shoot yet, keep chasing shoot = qfalse; move = qtrue; } //shoot decision if ( !(NPCInfo->scriptFlags&SCF_DONT_FIRE) ) {//try to shoot if ( NPC->enemy ) { if ( shoot ) { if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here { WeaponThink( qtrue ); } } } } //chase decision if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES ) {//go after him NPCInfo->goalEntity = NPC->enemy; //FIXME: don't need to chase when have a clear shot and in range? if ( !enemyCS && NPC->cantHitEnemyCounter > 60 ) {//haven't been able to shoot enemy for about 6 seconds, need to do something //FIXME: combat points? Just chase? if ( enemyPVS ) {//in my PVS, just pick a combat point //FIXME: implement } else {//just chase him } } //FIXME: in normal behavior, should we use combat Points? Do we care? Is anyone actually going to ever use this AI? } else if ( NPC->cantHitEnemyCounter > 60 ) {//pick a new one NPC_CheckEnemy( qtrue, qfalse, qtrue ); } if ( enemyPVS && enemyLOS )//&& !enemyShotFOV ) {//have a clear LOS to him//, but not looking at him //Find the desired angles vec3_t angles; GetAnglesForDirection( muzzle, enemyHead, angles ); NPCInfo->desiredYaw = AngleNormalize180( angles[YAW] ); NPCInfo->desiredPitch = AngleNormalize180( angles[PITCH] ); } */ } if ( UpdateGoal() ) {//have a goal if ( !NPC->enemy && NPC->client->leader && NPCInfo->goalEntity == NPC->client->leader && !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) ) { NPC_BSFollowLeader(); } else { //set angles if ( (NPCInfo->scriptFlags & SCF_FACE_MOVE_DIR) || NPCInfo->goalEntity != NPC->enemy ) {//face direction of movement, NOTE: default behavior when not chasing enemy NPCInfo->combatMove = qfalse; } else {//face goal.. FIXME: what if have a navgoal but want to face enemy while moving? Will this do that? vec3_t dir, angles; NPCInfo->combatMove = qfalse; VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir ); vectoangles( dir, angles ); NPCInfo->desiredYaw = angles[YAW]; if ( NPCInfo->goalEntity == NPC->enemy ) { NPCInfo->desiredPitch = angles[PITCH]; } } //set movement //override default walk/run behavior //NOTE: redundant, done in NPC_ApplyScriptFlags if ( NPCInfo->scriptFlags & SCF_RUNNING ) { ucmd.buttons &= ~BUTTON_WALKING; } else if ( NPCInfo->scriptFlags & SCF_WALKING ) { ucmd.buttons |= BUTTON_WALKING; } else if ( NPCInfo->goalEntity == NPC->enemy ) { ucmd.buttons &= ~BUTTON_WALKING; } else { ucmd.buttons |= BUTTON_WALKING; } if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH ) {//being forced to walk //if ( g_crosshairEntNum != NPC->s.number ) if (!NPC_SomeoneLookingAtMe(NPC)) {//don't walk if player isn't aiming at me move = qfalse; } } if ( move ) { //move toward goal NPC_MoveToGoal( qtrue ); } } } else if ( !NPC->enemy && NPC->client->leader ) { NPC_BSFollowLeader(); } //update angles NPC_UpdateAngles( qtrue, qtrue ); }
void NPC_BSRunAndShoot (void) { /*if(NPC->playerTeam && NPC->enemyTeam) { //FIXME: don't realize this right away- or else enemies show up and we're standing around if( teamNumbers[NPC->enemyTeam] == 0 ) {//ok, stand guard until we find another enemy //reset our rush counter teamCounter[NPC->playerTeam] = 0; NPCInfo->tempBehavior = BS_STAND_GUARD; NPC_BSStandGuard(); return; } }*/ //NOTE: are we sure we want ALL run and shoot people to move this way? //Shouldn't it check to see if we have an enemy and our enemy is our goal?! //Moved that check into NPC_MoveToGoal //NPCInfo->combatMove = qtrue; NPC_CheckEnemy( qtrue, qfalse, qtrue ); if ( NPCInfo->duckDebounceTime > level.time ) // && NPCInfo->hidingGoal ) { ucmd.upmove = -127; if ( NPC->enemy ) { NPC_CheckCanAttack( 1.0, qfalse ); } return; } if ( NPC->enemy ) { int monitor = NPC->cantHitEnemyCounter; NPC_StandTrackAndShoot( NPC, qfalse );//(NPCInfo->hidingGoal != NULL) ); if ( !(ucmd.buttons & BUTTON_ATTACK) && ucmd.upmove >= 0 && NPC->cantHitEnemyCounter > monitor ) {//not crouching and not firing vec3_t vec; VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, vec ); vec[2] = 0; if ( VectorLength( vec ) > 128 || NPC->cantHitEnemyCounter >= 10 ) {//run at enemy if too far away //The cantHitEnemyCounter getting high has other repercussions //100 (10 seconds) will make you try to pick a new enemy... //But we're chasing, so we clamp it at 50 here if ( NPC->cantHitEnemyCounter > 60 ) { NPC->cantHitEnemyCounter = 60; } if ( NPC->cantHitEnemyCounter >= (NPCInfo->stats.aggression+1) * 10 ) { NPC_LostEnemyDecideChase(); } //chase and face ucmd.angles[YAW] = 0; ucmd.angles[PITCH] = 0; NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 12; //NAV_ClearLastRoute(NPC); NPC_MoveToGoal( qtrue ); NPC_UpdateAngles(qtrue, qtrue); } else { //FIXME: this could happen if they're just on the other side //of a thin wall or something else blocking out shot. That //would make us just stand there and not go around it... //but maybe it's okay- might look like we're waiting for //him to come out...? //Current solution: runs around if cantHitEnemyCounter gets //to 10 (1 second). } } else {//Clear the can't hit enemy counter here NPC->cantHitEnemyCounter = 0; } } else { if ( NPCInfo->tempBehavior == BS_HUNT_AND_KILL ) {//lost him, go back to what we were doing before NPCInfo->tempBehavior = BS_DEFAULT; return; } // NPC_BSRun();//only moves if we have a goal } }
void NPC_BSStandAndShoot (void) { //FIXME: //When our numbers outnumber enemies 3 to 1, or only one of them, //go into hunt and kill mode //FIXME: //When they're all dead, go to some script or wander off to sickbay? if(NPC->client->playerTeam && NPC->client->enemyTeam) { //FIXME: don't realize this right away- or else enemies show up and we're standing around /* if( teamNumbers[NPC->enemyTeam] == 0 ) {//ok, stand guard until we find another enemy //reset our rush counter teamCounter[NPC->playerTeam] = 0; NPCInfo->tempBehavior = BS_STAND_GUARD; NPC_BSStandGuard(); return; }*/ /* //FIXME: whether to do this or not should be settable else if( NPC->playerTeam != TEAM_BORG )//Borg don't rush { //FIXME: In case reinforcements show up, we should wait a few seconds //and keep checking before rushing! //Also: what if not everyone on our team is going after playerTeam? //Also: our team count includes medics! if(NPC->health > 25) {//Can we rush the enemy? if(teamNumbers[NPC->enemyTeam] == 1 || teamNumbers[NPC->playerTeam] >= teamNumbers[NPC->enemyTeam]*3) {//Only one of them or we outnumber 3 to 1 if(teamStrength[NPC->playerTeam] >= 75 || (teamStrength[NPC->playerTeam] >= 50 && teamStrength[NPC->playerTeam] > teamStrength[NPC->enemyTeam])) {//Our team is strong enough to rush teamCounter[NPC->playerTeam]++; if(teamNumbers[NPC->playerTeam] * 17 <= teamCounter[NPC->playerTeam]) {//ok, we waited 1.7 think cycles on average and everyone is go, let's do it! //FIXME: Should we do this to everyone on our team? NPCInfo->behaviorState = BS_HUNT_AND_KILL; //FIXME: if the tide changes, we should retreat! //FIXME: when do we reset the counter? NPC_BSHuntAndKill (); return; } } else//Oops! Something's wrong, reset the counter to rush teamCounter[NPC->playerTeam] = 0; } else//Oops! Something's wrong, reset the counter to rush teamCounter[NPC->playerTeam] = 0; } } */ } NPC_CheckEnemy(qtrue, qfalse, qtrue); if(NPCInfo->duckDebounceTime > level.time && NPC->client->ps.weapon != WP_SABER ) { ucmd.upmove = -127; if(NPC->enemy) { NPC_CheckCanAttack(1.0, qtrue); } return; } if(NPC->enemy) { if(!NPC_StandTrackAndShoot( NPC, qtrue )) {//That func didn't update our angles NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW]; NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH]; NPC_UpdateAngles(qtrue, qtrue); } } else { NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW]; NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH]; NPC_UpdateAngles(qtrue, qtrue); // NPC_BSIdle();//only moves if we have a goal } }
void NPC_BSHuntAndKill( void ) { qboolean turned = qfalse; vec3_t vec; float enemyDist; visibility_t oEVis; int curAnim; NPC_CheckEnemy( /*NPCInfo->tempBehavior != BS_HUNT_AND_KILL*/qtrue, qfalse, qtrue );//don't find new enemy if this is tempbehav if ( NPC->enemy ) { oEVis = enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|//CHECK_PVS| if(enemyVisibility > VIS_PVS) { if ( !NPC_EnemyTooFar( NPC->enemy, 0, qtrue ) ) {//Enemy is close enough to shoot - FIXME: this next func does this also, but need to know here for info on whether ot not to turn later NPC_CheckCanAttack( 1.0, qfalse ); turned = qtrue; } } curAnim = NPC->client->ps.legsAnim; if(curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 ) {//Don't move toward enemy if we're in a full-body attack anim //FIXME, use IdealDistance to determin if we need to close distance VectorSubtract(NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, vec); enemyDist = VectorLength(vec); if( enemyDist > 48 && ((enemyDist*1.5)*(enemyDist*1.5) >= NPC_MaxDistSquaredForWeapon() || oEVis != VIS_SHOOT || //!(ucmd.buttons & BUTTON_ATTACK) || enemyDist > IdealDistance(NPC)*3 ) ) {//We should close in? NPCInfo->goalEntity = NPC->enemy; NPC_MoveToGoal( qtrue ); } else if(enemyDist < IdealDistance(NPC)) {//We should back off? //if(ucmd.buttons & BUTTON_ATTACK) { NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 12; NPC_MoveToGoal( qtrue ); ucmd.forwardmove *= -1; ucmd.rightmove *= -1; VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir ); ucmd.buttons |= BUTTON_WALKING; } }//otherwise, stay where we are } } else {//ok, stand guard until we find an enemy if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL ) { NPCInfo->tempBehavior = BS_DEFAULT; } else { NPCInfo->tempBehavior = BS_STAND_GUARD; NPC_BSStandGuard(); } return; } if(!turned) { NPC_UpdateAngles(qtrue, 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_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 ); }