void NPC_BSCinematic( void ) { if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON ) { WeaponThink( qtrue ); } if ( UpdateGoal() ) {//have a goalEntity //move toward goal, should also face that goal NPC_MoveToGoal( qtrue ); } if ( NPCInfo->watchTarget ) {//have an entity which we want to keep facing //NOTE: this will override any angles set by NPC_MoveToGoal vec3_t eyes, viewSpot, viewvec, viewangles; CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes ); CalcEntitySpot( NPCInfo->watchTarget, SPOT_HEAD_LEAN, viewSpot ); VectorSubtract( viewSpot, eyes, viewvec ); vectoangles( viewvec, viewangles ); NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw = viewangles[YAW]; NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch = viewangles[PITCH]; } NPC_UpdateAngles( qtrue, qtrue ); }
qboolean InFOV ( gentity_t *ent, gentity_t *from, int hFOV, int vFOV ) { vec3_t eyes; vec3_t spot; vec3_t deltaVector; vec3_t angles, fromAngles; vec3_t deltaAngles; if( from->client ) { if( !VectorCompare( from->client->renderInfo.eyeAngles, vec3_origin ) ) {//Actual facing of tag_head! //NOTE: Stasis aliens may have a problem with this? VectorCopy( from->client->renderInfo.eyeAngles, fromAngles ); } else { VectorCopy( from->client->ps.viewangles, fromAngles ); } } else { VectorCopy(from->s.angles, fromAngles); } CalcEntitySpot( from, SPOT_HEAD_LEAN, eyes ); CalcEntitySpot( ent, SPOT_ORIGIN, spot ); VectorSubtract ( spot, eyes, deltaVector); vectoangles ( deltaVector, angles ); deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] ); deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] ); if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) { return qtrue; } CalcEntitySpot( ent, SPOT_HEAD, spot ); VectorSubtract ( spot, eyes, deltaVector); vectoangles ( deltaVector, angles ); deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] ); deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] ); if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) { return qtrue; } CalcEntitySpot( ent, SPOT_LEGS, spot ); VectorSubtract ( spot, eyes, deltaVector); vectoangles ( deltaVector, angles ); deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] ); deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] ); if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) { return qtrue; } return qfalse; }
/* CanSee determine if NPC can see an entity This is a straight line trace check. This function does not look at PVS or FOV, or take any AI related factors (for example, the NPC's reaction time) into account FIXME do we need fat and thin version of this? */ qboolean CanSee ( gentity_t *ent ) { trace_t tr; vec3_t eyes; vec3_t spot; CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes ); CalcEntitySpot( ent, SPOT_ORIGIN, spot ); gi.trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE, (EG2_Collision)0, 0 ); ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE); if ( tr.fraction == 1.0 ) { return qtrue; } CalcEntitySpot( ent, SPOT_HEAD, spot ); gi.trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE, (EG2_Collision)0, 0 ); ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE); if ( tr.fraction == 1.0 ) { return qtrue; } CalcEntitySpot( ent, SPOT_LEGS, spot ); gi.trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE, (EG2_Collision)0, 0 ); ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE); if ( tr.fraction == 1.0 ) { return qtrue; } return qfalse; }
qboolean NPC_FacePosition( vec3_t position, qboolean doPitch ) { vec3_t muzzle; vec3_t angles; float yawDelta; qboolean facing = qtrue; //Get the positions if ( NPC->client && (NPC->client->NPC_class == CLASS_RANCOR || NPC->client->NPC_class == CLASS_WAMPA) )// || NPC->client->NPC_class == CLASS_SAND_CREATURE) ) { CalcEntitySpot( NPC, SPOT_ORIGIN, muzzle ); muzzle[2] += NPC->r.maxs[2] * 0.75f; } else if ( NPC->client && NPC->client->NPC_class == CLASS_GALAKMECH ) { CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); } else { CalcEntitySpot( NPC, SPOT_HEAD_LEAN, muzzle );//SPOT_HEAD } //Find the desired angles GetAnglesForDirection( muzzle, position, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] ); if ( NPC->enemy && NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_ATST ) { // FIXME: this is kind of dumb, but it was the easiest way to get it to look sort of ok NPCInfo->desiredYaw += flrand( -5, 5 ) + sin( level.time * 0.004f ) * 7; NPCInfo->desiredPitch += flrand( -2, 2 ); } //Face that yaw NPC_UpdateAngles( qtrue, qtrue ); //Find the delta between our goal and our current facing yawDelta = AngleNormalize360( NPCInfo->desiredYaw - ( SHORT2ANGLE( ucmd.angles[YAW] + client->ps.delta_angles[YAW] ) ) ); //See if we are facing properly if ( fabs( yawDelta ) > VALID_ATTACK_CONE ) facing = qfalse; if ( doPitch ) { //Find the delta between our goal and our current facing float currentAngles = ( SHORT2ANGLE( ucmd.angles[PITCH] + client->ps.delta_angles[PITCH] ) ); float pitchDelta = NPCInfo->desiredPitch - currentAngles; //See if we are facing properly if ( fabs( pitchDelta ) > VALID_ATTACK_CONE ) facing = qfalse; } return facing; }
/* ------------------------- Remote_Fire ------------------------- */ void Remote_Fire (void) { vec3_t delta1, enemy_org1, muzzle1; vec3_t angleToEnemy1; static vec3_t forward, vright, up; // static vec3_t muzzle; gentity_t *missile; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorCopy( NPC->r.currentOrigin, muzzle1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); missile = CreateMissile( NPC->r.currentOrigin, forward, 1000, 10000, NPC, qfalse ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), NPC->r.currentOrigin, forward ); missile->classname = "briar"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 10; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
//--------------------------------------------------------- gentity_t *WP_DropThermal( gentity_t *ent ) //--------------------------------------------------------- { AngleVectors( ent->client->ps.viewangles, wpFwd, wpVright, wpUp ); CalcEntitySpot( ent, SPOT_WEAPON, wpMuzzle ); return (WP_FireThermalDetonator( ent, qfalse )); }
//------------------------------------ void Seeker_Fire( void ) { vec3_t dir, enemy_org, muzzle; gentity_t *missile; CalcEntitySpot( NPCS.NPC->enemy, SPOT_HEAD, enemy_org ); VectorSubtract( enemy_org, NPCS.NPC->r.currentOrigin, dir ); VectorNormalize( dir ); // move a bit forward in the direction we shall shoot in so that the bolt doesn't poke out the other side of the seeker VectorMA( NPCS.NPC->r.currentOrigin, 15, dir, muzzle ); missile = CreateMissile( muzzle, dir, 1000, 10000, NPCS.NPC, qfalse ); G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), NPCS.NPC->r.currentOrigin, dir ); missile->classname = "blaster"; missile->s.weapon = WP_BLASTER; missile->damage = 5; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BLASTER; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; if ( NPCS.NPC->r.ownerNum < ENTITYNUM_NONE ) { missile->r.ownerNum = NPCS.NPC->r.ownerNum; } }
//Entity to position qboolean G_ClearLOS( gentity_t *self, gentity_t *ent, const vec3_t end ) { vec3_t eyes; CalcEntitySpot( ent, SPOT_HEAD_LEAN, eyes ); return G_ClearLOS( self, eyes, end ); }
/* ------------------------- ImperialProbe_FireBlaster ------------------------- */ void ImperialProbe_FireBlaster(void) { vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; // static vec3_t muzzle; int genBolt1; gentity_t *missile; mdxaBone_t boltMatrix; genBolt1 = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash"); //FIXME: use {0, NPC->client->ps.legsYaw, 0} trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, genBolt1, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, vec3_origin ); G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/probe/misc/fire" )); if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 ); enemy_org1[0]+= Q_irand(0,10); enemy_org1[1]+= Q_irand(0,10); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); } else { AngleVectors (NPC->r.currentAngles, forward, vright, up); } missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; if ( g_spskill.integer <= 1 ) { missile->damage = 5; } else { missile->damage = 10; } missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_UNKNOWN; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
//NPC's eyes to position qboolean G_ClearLOS( gentity_t *self, const vec3_t end ) { vec3_t eyes; //Calculate the my position CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes ); return G_ClearLOS( self, eyes, end ); }
qboolean NPC_FaceEntity( gentity_t *ent, qboolean doPitch ) { vec3_t entPos; //Get the positions CalcEntitySpot( ent, SPOT_HEAD_LEAN, entPos ); return NPC_FacePosition( entPos, doPitch ); }
//NPC's eyes to entity qboolean G_ClearLOS4( gentity_t *self, gentity_t *ent ) { vec3_t eyes; //Calculate my position CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes ); return G_ClearLOS3( self, eyes, ent ); }
//Position to entity qboolean G_ClearLOS( gentity_t *self, const vec3_t start, gentity_t *ent ) { vec3_t spot; //Look for the chest first CalcEntitySpot( ent, SPOT_ORIGIN, spot ); if ( G_ClearLOS( self, start, spot ) ) return qtrue; //Look for the head next CalcEntitySpot( ent, SPOT_HEAD_LEAN, spot ); if ( G_ClearLOS( self, start, spot ) ) return qtrue; return qfalse; }
qboolean InVisrange ( gentity_t *ent ) {//FIXME: make a calculate visibility for ents that takes into account //lighting, movement, turning, crouch/stand up, other anims, hide brushes, etc. vec3_t eyes; vec3_t spot; vec3_t deltaVector; float visrange = (NPCInfo->stats.visrange*NPCInfo->stats.visrange); CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes ); CalcEntitySpot( ent, SPOT_ORIGIN, spot ); VectorSubtract ( spot, eyes, deltaVector); /*if(ent->client) { float vel, avel; if(ent->client->ps.velocity[0] || ent->client->ps.velocity[1] || ent->client->ps.velocity[2]) { vel = VectorLength(ent->client->ps.velocity); if(vel > 128) { visrange += visrange * (vel/256); } } if(ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) {//FIXME: shouldn't they need to have line of sight to you to detect this? avel = VectorLength(ent->avelocity); if(avel > 15) { visrange += visrange * (avel/60); } } }*/ if(VectorLengthSquared(deltaVector) > visrange) { return qfalse; } return qtrue; }
/* ------------------------- ImperialProbe_FireBlaster ------------------------- */ void ImperialProbe_FireBlaster(void) { vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; static vec3_t muzzle; gentity_t *missile; mdxaBone_t boltMatrix; //FIXME: use {0, NPC->client->ps.legsYaw, 0} gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->playerModel, NPC->genericBolt1, &boltMatrix, NPC->currentAngles, NPC->currentOrigin, (cg.time?cg.time:level.time), NULL, NPC->s.modelScale ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzle1 ); G_PlayEffect( "bryar/muzzle_flash", muzzle1 ); G_Sound( NPC, G_SoundIndex( "sound/chars/probe/misc/fire" )); if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 ); enemy_org1[0]+= Q_irand(0,10); enemy_org1[1]+= Q_irand(0,10); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); } else { AngleVectors (NPC->currentAngles, forward, vright, up); } missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; if ( g_spskill->integer <= 1 ) { missile->damage = 5; } else { missile->damage = 10; } missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_ENERGY; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
int G_CheckAlertEvents( gentity_t *self, qboolean checkSight, qboolean checkSound, float maxSeeDist, float maxHearDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel ) { int bestSoundEvent = -1; int bestSightEvent = -1; int bestSoundAlert = -1; int bestSightAlert = -1; if ( &g_entities[0] == NULL || g_entities[0].health <= 0 ) { //player is dead return -1; } //get sound event bestSoundEvent = G_CheckSoundEvents( self, maxHearDist, ignoreAlert, mustHaveOwner, minAlertLevel ); //get sound event alert level if ( bestSoundEvent >= 0 ) { bestSoundAlert = level.alertEvents[bestSoundEvent].level; } //get sight event if ( self->NPC ) { bestSightEvent = G_CheckSightEvents( self, self->NPC->stats.hfov, self->NPC->stats.vfov, maxSeeDist, ignoreAlert, mustHaveOwner, minAlertLevel ); } else { bestSightEvent = G_CheckSightEvents( self, 80, 80, maxSeeDist, ignoreAlert, mustHaveOwner, minAlertLevel );//FIXME: look at cg_view to get more accurate numbers? } //get sight event alert level if ( bestSightEvent >= 0 ) { bestSightAlert = level.alertEvents[bestSightEvent].level; } //return the one that has a higher alert (or sound if equal) //FIXME: This doesn't take the distance of the event into account if ( bestSightEvent >= 0 && bestSightAlert > bestSoundAlert ) {//valid best sight event, more important than the sound event //get the light level of the alert event for this checker vec3_t eyePoint, sightDir; //get eye point CalcEntitySpot( self, SPOT_HEAD_LEAN, eyePoint ); VectorSubtract( level.alertEvents[bestSightEvent].position, eyePoint, sightDir ); level.alertEvents[bestSightEvent].light = level.alertEvents[bestSightEvent].addLight + G_GetLightLevel( level.alertEvents[bestSightEvent].position, sightDir ); //return the sight event return bestSightEvent; } //return the sound event return bestSoundEvent; }
static void Sniper_CheckFireState( void ) { if ( enemyCS ) {//if have a clear shot, always try return; } if ( NPCInfo->squadState == SQUAD_RETREAT || NPCInfo->squadState == SQUAD_TRANSITION || NPCInfo->squadState == SQUAD_SCOUT ) {//runners never try to fire at the last pos return; } if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) ) {//if moving at all, don't do this return; } if ( !TIMER_Done( NPC, "taunting" ) ) {//no shoot while taunting return; } //continue to fire on their last position if ( !Q_irand( 0, 1 ) && NPCInfo->enemyLastSeenTime && level.time - NPCInfo->enemyLastSeenTime < ((5-NPCInfo->stats.aim)*1000) )//FIXME: incorporate skill too? { if ( !VectorCompare( vec3_origin, NPCInfo->enemyLastSeenLocation ) ) { //Fire on the last known position vec3_t muzzle, dir, angles; CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir ); VectorNormalize( dir ); vectoangles( dir, angles ); NPCInfo->desiredYaw = angles[YAW]; NPCInfo->desiredPitch = angles[PITCH]; shoot = qtrue; //faceEnemy = qfalse; } return; } else if ( level.time - NPCInfo->enemyLastSeenTime > 10000 ) {//next time we see him, we'll miss few times first NPC->count = 0; } }
void Sniper_UpdateEnemyPos( void ) { int index; int i; for ( i = MAX_ENEMY_POS_LAG - ENEMY_POS_LAG_INTERVAL; i >= 0; i -= ENEMY_POS_LAG_INTERVAL ) { index = i / ENEMY_POS_LAG_INTERVAL; if ( !index ) { CalcEntitySpot( NPC->enemy, SPOT_HEAD_LEAN, &NPCInfo->enemyLaggedPos[index] ); NPCInfo->enemyLaggedPos[index].z -= flrand( 2, 16 ); } else { VectorCopy( &NPCInfo->enemyLaggedPos[index - 1], &NPCInfo->enemyLaggedPos[index] ); } } }
qboolean InFOV( 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 InFOV( origin, eyes, fromAngles, hFOV, vFOV ); }
/* ------------------------- Mark1_FireRocket ------------------------- */ void Mark1_FireRocket(void) { mdxaBone_t boltMatrix; vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; int bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash5"); gentity_t *missile; int damage = 50; trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, bolt, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); // G_PlayEffect( "blaster/muzzle_flash", muzzle1 ); CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark1/misc/mark1_fire" )); missile = CreateMissile( muzzle1, forward, BOWCASTER_VELOCITY, 10000, NPC, qfalse ); missile->classname = "bowcaster_proj"; missile->s.weapon = WP_BOWCASTER; VectorSet( missile->r.maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE ); VectorScale( missile->r.maxs, -1, missile->r.mins ); missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_ROCKET; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; missile->splashDamage = BOWCASTER_SPLASH_DAMAGE; missile->splashRadius = BOWCASTER_SPLASH_RADIUS; // we don't want it to bounce missile->bounceCount = 0; }
/* ------------------------- Mark2_FireBlaster ------------------------- */ void Mark2_FireBlaster(qboolean advance) { vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; static vec3_t muzzle; gentity_t *missile; mdxaBone_t boltMatrix; int bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash"); trap_G2API_GetBoltMatrix( NPC->ghoul2, 0, bolt, &boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time, NULL, NPC->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 ); if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); } else { AngleVectors (NPC->r.currentAngles, forward, vright, up); } G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, forward ); G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/mark2/misc/mark2_fire")); missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 1; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
int G_FindLocalInterestPoint( gentity_t *self ) { int i, bestPoint = ENTITYNUM_NONE; float dist, bestDist = Q3_INFINITE; vec3_t diffVec, eyes; CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes ); for ( i = 0; i < level.numInterestPoints; i++ ) { //Don't ignore portals? If through a portal, need to look at portal! if ( gi.inPVS( level.interestPoints[i].origin, eyes ) ) { VectorSubtract( level.interestPoints[i].origin, eyes, diffVec ); if ( (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 < 48 && fabs(diffVec[2]) > (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 ) {//Too close to look so far up or down continue; } dist = VectorLengthSquared( diffVec ); //Some priority to more interesting points //dist -= ((int)level.interestPoints[i].lookMode * 5) * ((int)level.interestPoints[i].lookMode * 5); if ( dist < MAX_INTEREST_DIST && dist < bestDist ) { if ( G_ClearLineOfSight( eyes, level.interestPoints[i].origin, self->s.number, MASK_OPAQUE ) ) { bestDist = dist; bestPoint = i; } } } } if ( bestPoint != ENTITYNUM_NONE && level.interestPoints[bestPoint].target ) { G_UseTargets2( self, self, level.interestPoints[bestPoint].target ); } return bestPoint; }
/* ------------------------- Remote_Fire ------------------------- */ void Remote_Fire (void) { vec3_t delta1, enemy_org1, muzzle1; vec3_t angleToEnemy1; //[SeekerItemNpc] //these dont need to be static, and vright and up arent used /*static*/ vec3_t forward;//, vright, up; /*static vec3_t muzzle;*/ //[/SeekerItemNpc] gentity_t *missile; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorCopy( NPC->r.currentOrigin, muzzle1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); //[SeekerItemNpc] AngleVectors (angleToEnemy1, forward, NULL, NULL); //AngleVectors (angleToEnemy1, forward, vright, up); //[/SeekerItemNpc] missile = CreateMissile( NPC->r.currentOrigin, forward, 1000, 10000, NPC, qfalse ); G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), NPC->r.currentOrigin, forward ); missile->classname = "briar"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 10; missile->dflags = DAMAGE_DEATH_KNOCKBACK; //RAFIXME - impliment this MOD? //missile->methodOfDeath = MOD_ENERGY; missile->methodOfDeath = MOD_BRYAR_PISTOL; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }
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 Sniper_FaceEnemy( void ) { //FIXME: the ones behind kill holes are facing some arbitrary direction and not firing //FIXME: If actually trying to hit enemy, don't fire unless enemy is at least in front of me? //FIXME: need to give designers option to make them not miss first few shots if ( NPC->enemy ) { vec3_t muzzle, target, angles, forward, right, up; //Get the positions AngleVectors( NPC->client->ps.viewangles, forward, right, up ); CalcMuzzlePoint( NPC, forward, right, up, muzzle, 0 ); //CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); CalcEntitySpot( NPC->enemy, SPOT_ORIGIN, target ); if ( enemyDist > 65536 && NPCInfo->stats.aim < 5 )//is 256 squared, was 16384 (128*128) { if ( NPC->count < (5-NPCInfo->stats.aim) ) {//miss a few times first if ( shoot && TIMER_Done( NPC, "attackDelay" ) && level.time >= NPCInfo->shotTime ) {//ready to fire again qboolean aimError = qfalse; qboolean hit = qtrue; int tryMissCount = 0; trace_t trace; GetAnglesForDirection( muzzle, target, angles ); AngleVectors( angles, forward, right, up ); while ( hit && tryMissCount < 10 ) { tryMissCount++; if ( !Q_irand( 0, 1 ) ) { aimError = qtrue; if ( !Q_irand( 0, 1 ) ) { VectorMA( target, NPC->enemy->maxs[2]*Q_flrand(1.5, 4), right, target ); } else { VectorMA( target, NPC->enemy->mins[2]*Q_flrand(1.5, 4), right, target ); } } if ( !aimError || !Q_irand( 0, 1 ) ) { if ( !Q_irand( 0, 1 ) ) { VectorMA( target, NPC->enemy->maxs[2]*Q_flrand(1.5, 4), up, target ); } else { VectorMA( target, NPC->enemy->mins[2]*Q_flrand(1.5, 4), up, target ); } } gi.trace( &trace, muzzle, vec3_origin, vec3_origin, target, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 ); hit = Sniper_EvaluateShot( trace.entityNum ); } NPC->count++; } else { if ( !enemyLOS ) { NPC_UpdateAngles( qtrue, qtrue ); return; } } } else {//based on distance, aim value, difficulty and enemy movement, miss //FIXME: incorporate distance as a factor? int missFactor = 8-(NPCInfo->stats.aim+g_spskill->integer) * 3; if ( missFactor > ENEMY_POS_LAG_STEPS ) { missFactor = ENEMY_POS_LAG_STEPS; } else if ( missFactor < 0 ) {//??? missFactor = 0 ; } VectorCopy( NPCInfo->enemyLaggedPos[missFactor], target ); } GetAnglesForDirection( muzzle, target, angles ); } else { target[2] += Q_flrand( 0, NPC->enemy->maxs[2] ); //CalcEntitySpot( NPC->enemy, SPOT_HEAD_LEAN, target ); GetAnglesForDirection( muzzle, target, angles ); } NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] ); } NPC_UpdateAngles( qtrue, 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 ); } } }
//Entity to entity qboolean InFOVFromPlayerView ( gentity_t *ent, int hFOV, int vFOV ) { vec3_t eyes; vec3_t spot; vec3_t deltaVector; vec3_t angles, fromAngles; vec3_t deltaAngles; if ( !player || !player->client ) { return qfalse; } if ( cg.time ) { VectorCopy( cg.refdefViewAngles, fromAngles ); } else { VectorCopy( player->client->ps.viewangles, fromAngles ); } if( cg.time ) { VectorCopy( cg.refdef.vieworg, eyes ); } else { CalcEntitySpot( player, SPOT_HEAD_LEAN, eyes ); } CalcEntitySpot( ent, SPOT_ORIGIN, spot ); VectorSubtract ( spot, eyes, deltaVector); vectoangles ( deltaVector, angles ); deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] ); deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] ); if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) { return qtrue; } CalcEntitySpot( ent, SPOT_HEAD, spot ); VectorSubtract ( spot, eyes, deltaVector); vectoangles ( deltaVector, angles ); deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] ); deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] ); if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) { return qtrue; } CalcEntitySpot( ent, SPOT_LEGS, spot ); VectorSubtract ( spot, eyes, deltaVector); vectoangles ( deltaVector, angles ); deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] ); deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] ); if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV ) { return qtrue; } return qfalse; }
void Boba_Update() { // Never Forget The Player... Never. //----------------------------------- if (player && player->inuse && !NPC->enemy) { G_SetEnemy(NPC, player); NPC->svFlags |= SVF_LOCKEDENEMY; // Don't forget about the enemy once you've found him } // Hey, This Is Boba, He Tests The Trace All The Time //---------------------------------------------------- if (NPC->enemy) { if (!(NPC->svFlags&SVF_NOCLIENT)) { trace_t testTrace; vec3_t eyes; CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes ); gi.trace (&testTrace, eyes, NULL, NULL, NPC->enemy->currentOrigin, NPC->s.number, MASK_SHOT, (EG2_Collision)0, 0); bool wasSeen = Boba_CanSeeEnemy(NPC); if (!testTrace.startsolid && !testTrace.allsolid && testTrace.entityNum == NPC->enemy->s.number) { NPCInfo->enemyLastSeenTime = level.time; NPCInfo->enemyLastHeardTime = level.time; VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation); VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation); } else if (gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin)) { NPCInfo->enemyLastHeardTime = level.time; VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation); } if (g_bobaDebug->integer) { bool nowSeen = Boba_CanSeeEnemy(NPC); if (!wasSeen && nowSeen) { Boba_Printf("Enemy Seen"); } if (wasSeen && !nowSeen) { Boba_Printf("Enemy Lost"); } CG_DrawEdge(NPC->currentOrigin, NPC->enemy->currentOrigin, (nowSeen)?(EDGE_IMPACT_SAFE):(EDGE_IMPACT_POSSIBLE)); } } if (!NPCInfo->surrenderTime) { if ((level.time - NPCInfo->enemyLastSeenTime)>20000 && TIMER_Done(NPC, "TooLongGoneRespawn")) { TIMER_Set(NPC, "TooLongGoneRespawn", 30000); // Give him some time to get to you before trying again Boba_Printf("Gone Too Long, Attempting Respawn Even Though Not Hiding"); Boba_Respawn(); } } } // Make Sure He Always Appears In The Last Area With Full Health When His Death Script Is Turned On //-------------------------------------------------------------------------------------------------- if (!BobaHadDeathScript && NPC->behaviorSet[BSET_DEATH]!=0) { if (!gi.inPVS(NPC->enemy->currentOrigin, NPC->currentOrigin)) { Boba_Printf("Attempting Final Battle Spawn..."); if (Boba_Respawn()) { BobaHadDeathScript = true; } else { Boba_Printf("Failed"); } } } // Don't Forget To Turn Off That Flame Thrower, Mr. Fett - You're Waisting Precious Natural Gases //------------------------------------------------------------------------------------------------ if ((NPCInfo->aiFlags&NPCAI_FLAMETHROW) && (TIMER_Done(NPC, "flameTime"))) { Boba_StopFlameThrower(NPC); } // Occasionally A Jump Turns Into A Rocket Fly //--------------------------------------------- if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE && NPC->client->ps.forceJumpZStart && !Q_irand( 0, 10 ) ) {//take off Boba_FlyStart( NPC ); } // If Hurting, Try To Run Away //----------------------------- if (!NPCInfo->surrenderTime && (NPC->health<NPC->max_health/10)) { Boba_Printf("Time To Surrender, Searching For Flee Point"); // Find The Closest Flee Point That I Can Get To //----------------------------------------------- int cp = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_FLEE|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1); if (cp!=-1) { NPC_SetCombatPoint( cp ); NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp ); if (NPC->count<6) { NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000) + 1000*(6-NPC->count); } else { NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000); } } else { Boba_Printf(" Failure"); } } }
void RT_FireDecide( void ) { qboolean enemyLOS = qfalse; qboolean enemyCS = qfalse; qboolean enemyInFOV = qfalse; //qboolean move = qtrue; qboolean faceEnemy = qfalse; qboolean shoot = qfalse; qboolean hitAlly = qfalse; vec3_t impactPos; float enemyDist; if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE && NPC->client->ps.forceJumpZStart && !PM_FlippingAnim( NPC->client->ps.legsAnim ) && !Q_irand( 0, 10 ) ) {//take off RT_FlyStart( NPC ); } if ( !NPC->enemy ) { return; } VectorClear( impactPos ); enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); vec3_t enemyDir, shootDir; VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, enemyDir ); VectorNormalize( enemyDir ); AngleVectors( NPC->client->ps.viewangles, shootDir, NULL, NULL ); float dot = DotProduct( enemyDir, shootDir ); if ( dot > 0.5f ||( enemyDist * (1.0f-dot)) < 10000 ) {//enemy is in front of me or they're very close and not behind me enemyInFOV = qtrue; } if ( enemyDist < MIN_ROCKET_DIST_SQUARED )//128 {//enemy within 128 if ( (NPC->client->ps.weapon == WP_FLECHETTE || 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; //FIXME: we can never go back to alt-fire this way since, after this, we don't know if we were initially supposed to use alt-fire or not... } } //can we see our target? if ( TIMER_Done( NPC, "nextAttackDelay" ) && TIMER_Done( NPC, "flameTime" ) ) { if ( NPC_ClearLOS( NPC->enemy ) ) { NPCInfo->enemyLastSeenTime = level.time; enemyLOS = qtrue; if ( NPC->client->ps.weapon == WP_NONE ) { enemyCS = qfalse;//not true, but should stop us from firing } else {//can we shoot our target? if ( (NPC->client->ps.weapon == WP_ROCKET_LAUNCHER || (NPC->client->ps.weapon == WP_CONCUSSION && !(NPCInfo->scriptFlags&SCF_ALT_FIRE)) || (NPC->client->ps.weapon == WP_FLECHETTE && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist < MIN_ROCKET_DIST_SQUARED )//128*128 { enemyCS = qfalse;//not true, but should stop us from firing hitAlly = qtrue;//us! //FIXME: if too close, run away! } else if ( enemyInFOV ) {//if enemy is FOV, go ahead and check for shooting 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 && ((hitEnt->svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) ) {//can hit enemy or enemy ally 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 {//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 { enemyCS = qfalse;//not true, but should stop us from firing } } } 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 ( NPC->client->ps.weapon == WP_NONE ) { faceEnemy = qfalse; shoot = qfalse; } else { 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 ( !enemyCS ) {//if have a clear shot, always try //See if we should continue to fire on their last position //!TIMER_Done( NPC, "stick" ) || if ( !hitAlly //we're not going to hit an ally && enemyInFOV //enemy is in our FOV //FIXME: or we don't have a clear LOS? && NPCInfo->enemyLastSeenTime > 0 )//we've seen the enemy { if ( level.time - NPCInfo->enemyLastSeenTime < 10000 )//we have seem the enemy in the last 10 seconds { if ( !Q_irand( 0, 10 ) ) { //Fire on the last known position vec3_t muzzle, dir, angles; qboolean tooClose = qfalse; qboolean tooFar = qfalse; CalcEntitySpot( NPC, SPOT_HEAD, muzzle ); if ( VectorCompare( impactPos, vec3_origin ) ) {//never checked ShotEntity this frame, so must do a trace... trace_t tr; //vec3_t mins = {-2,-2,-2}, maxs = {2,2,2}; vec3_t forward, end; AngleVectors( NPC->client->ps.viewangles, forward, NULL, NULL ); VectorMA( muzzle, 8192, forward, end ); gi.trace( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT ); VectorCopy( tr.endpos, impactPos ); } //see if impact would be too close to me float distThreshold = 16384/*128*128*/;//default switch ( NPC->s.weapon ) { case WP_ROCKET_LAUNCHER: case WP_FLECHETTE: case WP_THERMAL: case WP_TRIP_MINE: case WP_DET_PACK: distThreshold = 65536/*256*256*/; break; case WP_REPEATER: if ( NPCInfo->scriptFlags&SCF_ALT_FIRE ) { distThreshold = 65536/*256*256*/; } break; case WP_CONCUSSION: if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) ) { distThreshold = 65536/*256*256*/; } break; default: break; } float dist = DistanceSquared( impactPos, muzzle ); if ( dist < distThreshold ) {//impact would be too close to me tooClose = qtrue; } else if ( level.time - NPCInfo->enemyLastSeenTime > 5000 || (NPCInfo->group && level.time - NPCInfo->group->lastSeenEnemyTime > 5000 )) {//we've haven't seen them in the last 5 seconds //see if it's too far from where he is distThreshold = 65536/*256*256*/;//default switch ( NPC->s.weapon ) { case WP_ROCKET_LAUNCHER: case WP_FLECHETTE: case WP_THERMAL: case WP_TRIP_MINE: case WP_DET_PACK: distThreshold = 262144/*512*512*/; break; case WP_REPEATER: if ( NPCInfo->scriptFlags&SCF_ALT_FIRE ) { distThreshold = 262144/*512*512*/; } break; case WP_CONCUSSION: if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) ) { distThreshold = 262144/*512*512*/; } break; default: break; } dist = DistanceSquared( impactPos, NPCInfo->enemyLastSeenLocation ); if ( dist > distThreshold ) {//impact would be too far from enemy tooFar = qtrue; } } if ( !tooClose && !tooFar ) {//okay too shoot at last pos VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir ); VectorNormalize( dir ); vectoangles( dir, angles ); NPCInfo->desiredYaw = angles[YAW]; NPCInfo->desiredPitch = angles[PITCH]; shoot = qtrue; faceEnemy = qfalse; } } } } } //FIXME: don't shoot right away! if ( NPC->client->fireDelay ) { if ( NPC->s.weapon == WP_ROCKET_LAUNCHER || (NPC->s.weapon == WP_CONCUSSION&&!(NPCInfo->scriptFlags&SCF_ALT_FIRE)) ) { if ( !enemyLOS || !enemyCS ) {//cancel it NPC->client->fireDelay = 0; } else {//delay our next attempt TIMER_Set( NPC, "nextAttackDelay", Q_irand( 1000, 3000 ) );//FIXME: base on g_spskill } } } else if ( shoot ) {//try to shoot if it's time if ( TIMER_Done( NPC, "nextAttackDelay" ) ) { if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here { WeaponThink( qtrue ); } //NASTY int altChance = 6;//FIXME: base on g_spskill if ( NPC->s.weapon == WP_ROCKET_LAUNCHER ) { if ( (ucmd.buttons&BUTTON_ATTACK) && !Q_irand( 0, altChance ) ) {//every now and then, shoot a homing rocket ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons |= BUTTON_ALT_ATTACK; NPC->client->fireDelay = Q_irand( 1000, 3000 );//FIXME: base on g_spskill } } else if ( NPC->s.weapon == WP_CONCUSSION ) { if ( (ucmd.buttons&BUTTON_ATTACK) && Q_irand( 0, altChance*5 ) ) {//fire the beam shot ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons |= BUTTON_ALT_ATTACK; TIMER_Set( NPC, "nextAttackDelay", Q_irand( 1500, 2500 ) );//FIXME: base on g_spskill } else {//fire the rocket-like shot TIMER_Set( NPC, "nextAttackDelay", Q_irand( 3000, 5000 ) );//FIXME: base on g_spskill } } } } } }
/* ------------------------- Mark1_FireBlaster - Shoot the left weapon, the multi-blaster ------------------------- */ void Mark1_FireBlaster(void) { vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1; static vec3_t forward, vright, up; gentity_t *missile; mdxaBone_t boltMatrix; int bolt; // Which muzzle to fire from? if ((NPCInfo->localState <= LSTATE_FIRED0) || (NPCInfo->localState == LSTATE_FIRED4)) { NPCInfo->localState = LSTATE_FIRED1; bolt = NPC->genericBolt1; } else if (NPCInfo->localState == LSTATE_FIRED1) { NPCInfo->localState = LSTATE_FIRED2; bolt = NPC->genericBolt2; } else if (NPCInfo->localState == LSTATE_FIRED2) { NPCInfo->localState = LSTATE_FIRED3; bolt = NPC->genericBolt3; } else { NPCInfo->localState = LSTATE_FIRED4; bolt = NPC->genericBolt4; } gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->playerModel, bolt, &boltMatrix, NPC->currentAngles, NPC->currentOrigin, (cg.time?cg.time:level.time), NULL, NPC->s.modelScale ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzle1 ); if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); VectorSubtract (enemy_org1, muzzle1, delta1); vectoangles ( delta1, angleToEnemy1 ); AngleVectors (angleToEnemy1, forward, vright, up); } else { AngleVectors (NPC->currentAngles, forward, vright, up); } G_PlayEffect( "bryar/muzzle_flash", muzzle1, forward ); G_Sound( NPC, G_SoundIndex("sound/chars/mark1/misc/mark1_fire")); missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC ); missile->classname = "bryar_proj"; missile->s.weapon = WP_BRYAR_PISTOL; missile->damage = 1; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_ENERGY; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; }