qboolean NPC_FindEnemy( qboolean checkAlerts = qfalse ) { //We're ignoring all enemies for now if( NPC->svFlags & SVF_IGNORE_ENEMIES ) { G_ClearEnemy( NPC ); return qfalse; } //we can't pick up any enemies for now if( NPCInfo->confusionTime > level.time ) { return qfalse; } //Don't want a new enemy if ( ( ValidEnemy( NPC->enemy ) ) && ( NPC->svFlags & SVF_LOCKEDENEMY ) ) return qtrue; //See if the player is closer than our current enemy if ( NPC_CheckPlayerDistance() ) { return qtrue; } //Otherwise, turn off the flag NPC->svFlags &= ~SVF_LOCKEDENEMY; //If we've gotten here alright, then our target it still valid if ( NPC_ValidEnemy( NPC->enemy ) ) return qtrue; gentity_t *newenemy = NPC_PickEnemyExt( checkAlerts ); //if we found one, take it as the enemy if( NPC_ValidEnemy( newenemy ) ) { G_SetEnemy( NPC, newenemy ); return qtrue; } return qfalse; }
qboolean NPC_CheckInvestigate( int alertEventNum ) { gentity_t *owner = level.alertEvents[alertEventNum].owner; int invAdd = level.alertEvents[alertEventNum].level; vec3_t soundPos; float soundRad = level.alertEvents[alertEventNum].radius; float earshot = NPCInfo->stats.earshot; VectorCopy( level.alertEvents[alertEventNum].position, soundPos ); //NOTE: Trying to preserve previous investigation behavior if ( !owner ) { return qfalse; } if ( owner->s.eType != ET_PLAYER && owner == NPCInfo->goalEntity ) { return qfalse; } if ( owner->s.eFlags & EF_NODRAW ) { return qfalse; } if ( owner->flags & FL_NOTARGET ) { return qfalse; } if ( soundRad < earshot ) { return qfalse; } //if(!gi.inPVSIgnorePortals(ent->currentOrigin, NPC->currentOrigin))//should we be able to hear through areaportals? if ( !gi.inPVS( soundPos, NPC->currentOrigin ) ) {//can hear through doors? return qfalse; } if ( owner->client && owner->client->playerTeam && NPC->client->playerTeam && owner->client->playerTeam != NPC->client->playerTeam ) { if( (float)NPCInfo->investigateCount >= (NPCInfo->stats.vigilance*200) && owner ) {//If investigateCount == 10, just take it as enemy and go if ( ValidEnemy( owner ) ) {//FIXME: run angerscript G_SetEnemy( NPC, owner ); NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = 12; NPCInfo->behaviorState = BS_HUNT_AND_KILL; return qtrue; } } else { NPCInfo->investigateCount += invAdd; } //run awakescript G_ActivateBehavior(NPC, BSET_AWAKE); /* if ( Q_irand(0, 10) > 7 ) { NPC_AngerSound(); } */ //NPCInfo->hlookCount = NPCInfo->vlookCount = 0; NPCInfo->eventOwner = owner; VectorCopy( soundPos, NPCInfo->investigateGoal ); if ( NPCInfo->investigateCount > 20 ) { NPCInfo->investigateDebounceTime = level.time + 10000; } else { NPCInfo->investigateDebounceTime = level.time + (NPCInfo->investigateCount*500); } NPCInfo->tempBehavior = BS_INVESTIGATE; return qtrue; } return qfalse; }
/* ------------------------- 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 ); }
qboolean NPC_FindEnemy( qboolean checkAlerts ) {//RACC - checks to see if our enemy is still valid. Updates if it is not. gentity_t *newenemy; //[CoOp] SP Code //reenabling the IGNORE_ENEMIES flag if( NPC->NPC->scriptFlags & SCF_IGNORE_ENEMIES ) //We're ignoring all enemies for now //if( NPC->svFlags & SVF_IGNORE_ENEMIES ) //if (0) //rwwFIXMEFIXME: support for flag //[/CoOp] { G_ClearEnemy( NPC ); return qfalse; } //we can't pick up any enemies for now if( NPCInfo->confusionTime > level.time ) { //[CoOp] SP Code G_ClearEnemy( NPC ); //[/CoOp] return qfalse; } //[CoOp] SP Code //Don't want a new enemy if ( ( ValidEnemy( NPC->enemy ) ) && ( NPC->NPC->aiFlags & NPCAI_LOCKEDENEMY ) ) return qtrue; //See if the player is closer than our current enemy if ( NPC->client->NPC_class != CLASS_RANCOR && NPC->client->NPC_class != CLASS_WAMPA && NPC->client->NPC_class != CLASS_SAND_CREATURE && NPC_CheckPlayerDistance() ) {//rancors, wampas & sand creatures don't care if player is closer, they always go with closest return qtrue; } /* This shouldn't be here. SP Code //See if the player is closer than our current enemy if ( NPC_CheckPlayerDistance() ) { return qtrue; }*/ //Otherwise, turn off the flag since if we have a locked enemy at this point, //the enemy is invalid. NPC->NPC->aiFlags &= ~NPCAI_LOCKEDENEMY; // NPC->svFlags &= ~SVF_LOCKEDENEMY; /* Moved up. SP Code //See if the player is closer than our current enemy if ( NPC->client->NPC_class != CLASS_RANCOR && NPC->client->NPC_class != CLASS_WAMPA //&& NPC->client->NPC_class != CLASS_SAND_CREATURE && NPC_CheckPlayerDistance() ) {//rancors, wampas & sand creatures don't care if player is closer, they always go with closest return qtrue; } */ //[/CoOp] //If we've gotten here alright, then our target it still valid if ( NPC_ValidEnemy( NPC->enemy ) ) return qtrue; newenemy = NPC_PickEnemyExt( checkAlerts ); //if we found one, take it as the enemy if( NPC_ValidEnemy( newenemy ) ) { G_SetEnemy( NPC, newenemy ); return qtrue; } //[CoOp] SP Code. Remove enemy since they're not valid at this point. G_ClearEnemy( NPC ); //[/CoOp] return qfalse; }
/* ------------------------- 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 ); }