#include "b_local.h"#include "g_nav.h"Go to the source code of this file.
Defines | |
| #define | VELOCITY_DECAY 0.7f |
| #define | MIN_MELEE_RANGE 320 |
| #define | MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE ) |
| #define | MIN_DISTANCE 80 |
| #define | MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE ) |
| #define | SEEKER_STRAFE_VEL 100 |
| #define | SEEKER_STRAFE_DIS 200 |
| #define | SEEKER_UPWARD_PUSH 32 |
| #define | SEEKER_FORWARD_BASE_SPEED 10 |
| #define | SEEKER_FORWARD_MULTIPLIER 2 |
| #define | SEEKER_SEEK_RADIUS 1024 |
Functions | |
| void | Boba_FireDecide (void) |
| void | Seeker_Strafe (void) |
| void | NPC_Seeker_Precache (void) |
| void | NPC_Seeker_Pain (gentity_t *self, gentity_t *attacker, int damage) |
| void | Seeker_MaintainHeight (void) |
| void | Seeker_Hunt (qboolean visible, qboolean advance) |
| void | Seeker_Fire (void) |
| void | Seeker_Ranged (qboolean visible, qboolean advance) |
| void | Seeker_Attack (void) |
| void | Seeker_FindEnemy (void) |
| void | Seeker_FollowOwner (void) |
| void | NPC_BSSeeker_Default (void) |
|
|
Definition at line 13 of file NPC_AI_Seeker.c. |
|
|
Definition at line 14 of file NPC_AI_Seeker.c. |
|
|
Definition at line 10 of file NPC_AI_Seeker.c. |
|
|
Definition at line 11 of file NPC_AI_Seeker.c. |
|
|
Definition at line 20 of file NPC_AI_Seeker.c. Referenced by Seeker_Hunt(). |
|
|
Definition at line 21 of file NPC_AI_Seeker.c. Referenced by Seeker_Hunt(). |
|
|
Definition at line 23 of file NPC_AI_Seeker.c. Referenced by Seeker_FindEnemy(). |
|
|
Definition at line 17 of file NPC_AI_Seeker.c. Referenced by Seeker_Strafe(). |
|
|
Definition at line 16 of file NPC_AI_Seeker.c. Referenced by Seeker_Strafe(). |
|
|
Definition at line 18 of file NPC_AI_Seeker.c. Referenced by Seeker_Strafe(). |
|
|
Definition at line 8 of file NPC_AI_Seeker.c. |
|
|
TIMER_Done( NPC, "stick" ) || Definition at line 481 of file NPC_AI_Jedi.c. References AngleVectors(), BG_FlippingAnim(), Boba_ChangeWeapon(), Boba_DoFlameThrower(), Boba_FlyStart(), gNPC_t::burstMax, gNPC_t::burstMean, gNPC_t::burstMin, gNPC_t::burstSpacing, BUTTON_ALT_ATTACK, BUTTON_ATTACK, usercmd_s::buttons, CalcEntitySpot(), gentity_s::client, entityShared_t::currentOrigin, gNPC_t::desiredPitch, gNPC_t::desiredYaw, DotProduct, trace_t::endpos, gentity_s::enemy, enemyDist, gNPC_t::enemyLastSeenLocation, gNPC_t::enemyLastSeenTime, gclient_s::enemyTeam, ENTITYNUM_NONE, playerState_s::fd, forcedata_s::forceJumpZStart, g_entities, gentity_t, playerState_s::groundEntityNum, gNPC_t::group, gentity_s::health, AIGroupInfo_s::lastSeenEnemyTime, playerState_s::legsAnim, level, MASK_SHOT, clientPersistant_t::maxHealth, MIN_ROCKET_DIST_SQUARED, NPC, NPC_ChangeWeapon(), NPC_ClearLOS4(), NPC_ShotEntity(), NPC_UpdateAngles(), NPCInfo, NULL, entityState_s::number, gclient_s::pers, PITCH, gclient_s::playerTeam, gclient_s::ps, Q_irand(), qboolean, qfalse, qtrue, gentity_s::r, gentity_s::s, SCF_ALT_FIRE, SCF_FIRE_WEAPON, gNPC_t::scriptFlags, SPOT_HEAD, SVF_GLASS_BRUSH, entityShared_t::svFlags, gentity_s::takedamage, level_locals_t::time, TIMER_Done(), TIMER_Set(), trap_InPVS(), trap_Trace(), ucmd, vec3_origin, vec3_t, vectoangles(), VectorClear, VectorCopy, VectorMA, VectorNormalize(), VectorSubtract, playerState_s::viewangles, playerState_s::weapon, entityState_s::weapon, WeaponThink(), playerState_s::weaponTime, WP_BLASTER, WP_DET_PACK, WP_DISRUPTOR, WP_EMPLACED_GUN, WP_FLECHETTE, WP_NONE, WP_REPEATER, WP_ROCKET_LAUNCHER, WP_SABER, WP_THERMAL, WP_TRIP_MINE, and YAW. Referenced by NPC_BSSeeker_Default().
00482 {
00483 qboolean enemyLOS = qfalse;
00484 qboolean enemyCS = qfalse;
00485 qboolean enemyInFOV = qfalse;
00486 //qboolean move = qtrue;
00487 qboolean faceEnemy = qfalse;
00488 qboolean shoot = qfalse;
00489 qboolean hitAlly = qfalse;
00490 vec3_t impactPos;
00491 float enemyDist;
00492 float dot;
00493 vec3_t enemyDir, shootDir;
00494
00495 if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE
00496 && NPC->client->ps.fd.forceJumpZStart
00497 && !BG_FlippingAnim( NPC->client->ps.legsAnim )
00498 && !Q_irand( 0, 10 ) )
00499 {//take off
00500 Boba_FlyStart( NPC );
00501 }
00502
00503 if ( !NPC->enemy )
00504 {
00505 return;
00506 }
00507
00508 /*
00509 if ( NPC->enemy->enemy != NPC && NPC->health == NPC->client->pers.maxHealth )
00510 {
00511 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00512 Boba_ChangeWeapon( WP_DISRUPTOR );
00513 }
00514 else */if ( NPC->enemy->s.weapon == WP_SABER )
00515 {
00516 NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
00517 Boba_ChangeWeapon( WP_ROCKET_LAUNCHER );
00518 }
00519 else
00520 {
00521 if ( NPC->health < NPC->client->pers.maxHealth*0.5f )
00522 {
00523 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00524 Boba_ChangeWeapon( WP_BLASTER );
00525 NPCInfo->burstMin = 3;
00526 NPCInfo->burstMean = 12;
00527 NPCInfo->burstMax = 20;
00528 NPCInfo->burstSpacing = Q_irand( 300, 750 );//attack debounce
00529 }
00530 else
00531 {
00532 NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
00533 Boba_ChangeWeapon( WP_BLASTER );
00534 }
00535 }
00536
00537 VectorClear( impactPos );
00538 enemyDist = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00539
00540 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir );
00541 VectorNormalize( enemyDir );
00542 AngleVectors( NPC->client->ps.viewangles, shootDir, NULL, NULL );
00543 dot = DotProduct( enemyDir, shootDir );
00544 if ( dot > 0.5f ||( enemyDist * (1.0f-dot)) < 10000 )
00545 {//enemy is in front of me or they're very close and not behind me
00546 enemyInFOV = qtrue;
00547 }
00548
00549 if ( (enemyDist < (128*128)&&enemyInFOV) || !TIMER_Done( NPC, "flameTime" ) )
00550 {//flamethrower
00551 Boba_DoFlameThrower( NPC );
00552 enemyCS = qfalse;
00553 shoot = qfalse;
00554 NPCInfo->enemyLastSeenTime = level.time;
00555 faceEnemy = qtrue;
00556 ucmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
00557 }
00558 else if ( enemyDist < MIN_ROCKET_DIST_SQUARED )//128
00559 {//enemy within 128
00560 if ( (NPC->client->ps.weapon == WP_FLECHETTE || NPC->client->ps.weapon == WP_REPEATER) &&
00561 (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
00562 {//shooting an explosive, but enemy too close, switch to primary fire
00563 NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
00564 //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...
00565 }
00566 }
00567 else if ( enemyDist > 65536 )//256 squared
00568 {
00569 if ( NPC->client->ps.weapon == WP_DISRUPTOR )
00570 {//sniping... should be assumed
00571 if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
00572 {//use primary fire
00573 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00574 //reset fire-timing variables
00575 NPC_ChangeWeapon( WP_DISRUPTOR );
00576 NPC_UpdateAngles( qtrue, qtrue );
00577 return;
00578 }
00579 }
00580 }
00581
00582 //can we see our target?
00583 if ( TIMER_Done( NPC, "nextAttackDelay" ) && TIMER_Done( NPC, "flameTime" ) )
00584 {
00585 if ( NPC_ClearLOS4( NPC->enemy ) )
00586 {
00587 NPCInfo->enemyLastSeenTime = level.time;
00588 enemyLOS = qtrue;
00589
00590 if ( NPC->client->ps.weapon == WP_NONE )
00591 {
00592 enemyCS = qfalse;//not true, but should stop us from firing
00593 }
00594 else
00595 {//can we shoot our target?
00596 if ( (NPC->client->ps.weapon == WP_ROCKET_LAUNCHER || (NPC->client->ps.weapon == WP_FLECHETTE && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist < MIN_ROCKET_DIST_SQUARED )//128*128
00597 {
00598 enemyCS = qfalse;//not true, but should stop us from firing
00599 hitAlly = qtrue;//us!
00600 //FIXME: if too close, run away!
00601 }
00602 else if ( enemyInFOV )
00603 {//if enemy is FOV, go ahead and check for shooting
00604 int hit = NPC_ShotEntity( NPC->enemy, impactPos );
00605 gentity_t *hitEnt = &g_entities[hit];
00606
00607 if ( hit == NPC->enemy->s.number
00608 || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
00609 || ( hitEnt && hitEnt->takedamage && ((hitEnt->r.svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) )
00610 {//can hit enemy or enemy ally or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway
00611 enemyCS = qtrue;
00612 //NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
00613 VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
00614 }
00615 else
00616 {//Hmm, have to get around this bastard
00617 //NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
00618 if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam )
00619 {//would hit an ally, don't fire!!!
00620 hitAlly = qtrue;
00621 }
00622 else
00623 {//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire
00624 }
00625 }
00626 }
00627 else
00628 {
00629 enemyCS = qfalse;//not true, but should stop us from firing
00630 }
00631 }
00632 }
00633 else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
00634 {
00635 NPCInfo->enemyLastSeenTime = level.time;
00636 faceEnemy = qtrue;
00637 //NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
00638 }
00639
00640 if ( NPC->client->ps.weapon == WP_NONE )
00641 {
00642 faceEnemy = qfalse;
00643 shoot = qfalse;
00644 }
00645 else
00646 {
00647 if ( enemyLOS )
00648 {//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
00649 faceEnemy = qtrue;
00650 }
00651 if ( enemyCS )
00652 {
00653 shoot = qtrue;
00654 }
00655 }
00656
00657 if ( !enemyCS )
00658 {//if have a clear shot, always try
00659 //See if we should continue to fire on their last position
00661 if ( !hitAlly //we're not going to hit an ally
00662 && enemyInFOV //enemy is in our FOV //FIXME: or we don't have a clear LOS?
00663 && NPCInfo->enemyLastSeenTime > 0 )//we've seen the enemy
00664 {
00665 if ( level.time - NPCInfo->enemyLastSeenTime < 10000 )//we have seem the enemy in the last 10 seconds
00666 {
00667 if ( !Q_irand( 0, 10 ) )
00668 {
00669 //Fire on the last known position
00670 vec3_t muzzle, dir, angles;
00671 qboolean tooClose = qfalse;
00672 qboolean tooFar = qfalse;
00673 float distThreshold;
00674 float dist;
00675
00676 CalcEntitySpot( NPC, SPOT_HEAD, muzzle );
00677 if ( VectorCompare( impactPos, vec3_origin ) )
00678 {//never checked ShotEntity this frame, so must do a trace...
00679 trace_t tr;
00680 //vec3_t mins = {-2,-2,-2}, maxs = {2,2,2};
00681 vec3_t forward, end;
00682 AngleVectors( NPC->client->ps.viewangles, forward, NULL, NULL );
00683 VectorMA( muzzle, 8192, forward, end );
00684 trap_Trace( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
00685 VectorCopy( tr.endpos, impactPos );
00686 }
00687
00688 //see if impact would be too close to me
00689 distThreshold = 16384/*128*128*/;//default
00690 switch ( NPC->s.weapon )
00691 {
00692 case WP_ROCKET_LAUNCHER:
00693 case WP_FLECHETTE:
00694 case WP_THERMAL:
00695 case WP_TRIP_MINE:
00696 case WP_DET_PACK:
00697 distThreshold = 65536/*256*256*/;
00698 break;
00699 case WP_REPEATER:
00700 if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
00701 {
00702 distThreshold = 65536/*256*256*/;
00703 }
00704 break;
00705 default:
00706 break;
00707 }
00708
00709 dist = DistanceSquared( impactPos, muzzle );
00710
00711 if ( dist < distThreshold )
00712 {//impact would be too close to me
00713 tooClose = qtrue;
00714 }
00715 else if ( level.time - NPCInfo->enemyLastSeenTime > 5000 ||
00716 (NPCInfo->group && level.time - NPCInfo->group->lastSeenEnemyTime > 5000 ))
00717 {//we've haven't seen them in the last 5 seconds
00718 //see if it's too far from where he is
00719 distThreshold = 65536/*256*256*/;//default
00720 switch ( NPC->s.weapon )
00721 {
00722 case WP_ROCKET_LAUNCHER:
00723 case WP_FLECHETTE:
00724 case WP_THERMAL:
00725 case WP_TRIP_MINE:
00726 case WP_DET_PACK:
00727 distThreshold = 262144/*512*512*/;
00728 break;
00729 case WP_REPEATER:
00730 if ( NPCInfo->scriptFlags&SCF_ALT_FIRE )
00731 {
00732 distThreshold = 262144/*512*512*/;
00733 }
00734 break;
00735 default:
00736 break;
00737 }
00738 dist = DistanceSquared( impactPos, NPCInfo->enemyLastSeenLocation );
00739 if ( dist > distThreshold )
00740 {//impact would be too far from enemy
00741 tooFar = qtrue;
00742 }
00743 }
00744
00745 if ( !tooClose && !tooFar )
00746 {//okay too shoot at last pos
00747 VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir );
00748 VectorNormalize( dir );
00749 vectoangles( dir, angles );
00750
00751 NPCInfo->desiredYaw = angles[YAW];
00752 NPCInfo->desiredPitch = angles[PITCH];
00753
00754 shoot = qtrue;
00755 faceEnemy = qfalse;
00756 }
00757 }
00758 }
00759 }
00760 }
00761
00762 //FIXME: don't shoot right away!
00763 if ( NPC->client->ps.weaponTime > 0 )
00764 {
00765 if ( NPC->s.weapon == WP_ROCKET_LAUNCHER )
00766 {
00767 if ( !enemyLOS || !enemyCS )
00768 {//cancel it
00769 NPC->client->ps.weaponTime = 0;
00770 }
00771 else
00772 {//delay our next attempt
00773 TIMER_Set( NPC, "nextAttackDelay", Q_irand( 500, 1000 ) );
00774 }
00775 }
00776 }
00777 else if ( shoot )
00778 {//try to shoot if it's time
00779 if ( TIMER_Done( NPC, "nextAttackDelay" ) )
00780 {
00781 if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
00782 {
00783 WeaponThink( qtrue );
00784 }
00785 //NASTY
00786 if ( NPC->s.weapon == WP_ROCKET_LAUNCHER
00787 && (ucmd.buttons&BUTTON_ATTACK)
00788 && !Q_irand( 0, 3 ) )
00789 {//every now and then, shoot a homing rocket
00790 ucmd.buttons &= ~BUTTON_ATTACK;
00791 ucmd.buttons |= BUTTON_ALT_ATTACK;
00792 NPC->client->ps.weaponTime = Q_irand( 500, 1500 );
00793 }
00794 }
00795 }
00796 }
00797 }
|
|
|
Definition at line 523 of file NPC_AI_Seeker.c. References Boba_FireDecide(), CLASS_BOBAFETT, CLASS_SEEKER, gentity_s::client, CON_DISCONNECTED, clientPersistant_t::connected, DAMAGE_NO_PROTECTION, gentity_s::enemy, ENTITYNUM_NONE, G_Damage(), g_entities, gentity_t, gentity_s::health, gentity_s::inuse, MOD_TELEFRAG, NPC, gclient_s::NPC_class, NULL, entityState_s::number, entityShared_t::ownerNum, gclient_s::pers, gentity_s::r, random, gentity_s::random, gentity_s::s, Seeker_Attack(), and Seeker_FollowOwner(). Referenced by NPC_BehaviorSet_Seeker().
00524 {
00525 /*
00526 if ( in_camera )
00527 {
00528 if ( NPC->client->NPC_class != CLASS_BOBAFETT )
00529 {
00530 // cameras make me commit suicide....
00531 G_Damage( NPC, NPC, NPC, NULL, NULL, 999, 0, MOD_UNKNOWN );
00532 }
00533 }
00534 */
00535 //N/A for MP.
00536 if ( NPC->r.ownerNum < ENTITYNUM_NONE )
00537 {
00538 gentity_t *owner = &g_entities[0];
00539 if ( owner->health <= 0
00540 || (owner->client && owner->client->pers.connected == CON_DISCONNECTED) )
00541 {//owner is dead or gone
00542 //remove me
00543 G_Damage( NPC, NULL, NULL, NULL, NULL, 10000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
00544 return;
00545 }
00546 }
00547
00548 if ( NPC->random == 0.0f )
00549 {
00550 // used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method.
00551 NPC->random = random() * 6.3f; // roughly 2pi
00552 }
00553
00554 if ( NPC->enemy && NPC->enemy->health && NPC->enemy->inuse )
00555 {
00556 if ( NPC->client->NPC_class != CLASS_BOBAFETT && ( NPC->enemy->s.number == 0 || ( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_SEEKER )) )
00557 {
00558 //hacked to never take the player as an enemy, even if the player shoots at it
00559 NPC->enemy = NULL;
00560 }
00561 else
00562 {
00563 Seeker_Attack();
00564 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00565 {
00566 Boba_FireDecide();
00567 }
00568 return;
00569 }
00570 }
00571
00572 // In all other cases, follow the player and look for enemies to take on
00573 Seeker_FollowOwner();
00574 }
|
|
||||||||||||||||
|
Definition at line 34 of file NPC_AI_Seeker.c. References gNPC_t::aiFlags, G_Damage(), gentity_t, MOD_FALLING, gentity_s::NPC, NPC_Pain(), NPCAI_CUSTOM_GRAVITY, NULL, RestoreNPCGlobals(), SaveNPCGlobals(), Seeker_Strafe(), SetNPCGlobals(), and vec3_origin. Referenced by NPC_PainFunc().
00035 {
00036 if ( !(self->NPC->aiFlags&NPCAI_CUSTOM_GRAVITY ))
00037 {//void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod, int hitLoc=HL_NONE );
00038 G_Damage( self, NULL, NULL, (float*)vec3_origin, (float*)vec3_origin, 999, 0, MOD_FALLING );
00039 }
00040
00041 SaveNPCGlobals();
00042 SetNPCGlobals( self );
00043 Seeker_Strafe();
00044 RestoreNPCGlobals();
00045 NPC_Pain( self, attacker, damage );
00046 }
|
|
|
Definition at line 26 of file NPC_AI_Seeker.c. References G_EffectIndex(), and G_SoundIndex(). Referenced by NPC_SpawnType(), and SP_NPC_Droid_Seeker().
00027 {
00028 G_SoundIndex("sound/chars/seeker/misc/fire.wav");
00029 G_SoundIndex( "sound/chars/seeker/misc/hiss.wav");
00030 G_EffectIndex( "env/small_explode");
00031 }
|
|
|
Definition at line 350 of file NPC_AI_Seeker.c. References CLASS_BOBAFETT, gentity_s::client, entityShared_t::currentOrigin, DistanceHorizontalSquared(), gentity_s::enemy, MIN_DISTANCE_SQR, NPC, gclient_s::NPC_class, NPC_ClearLOS4(), NPCInfo, qboolean, qfalse, gentity_s::r, SCF_CHASE_ENEMIES, gNPC_t::scriptFlags, Seeker_Hunt(), Seeker_MaintainHeight(), and Seeker_Ranged(). Referenced by NPC_BSSeeker_Default().
00351 {
00352 float distance;
00353 qboolean visible;
00354 qboolean advance;
00355
00356 // Always keep a good height off the ground
00357 Seeker_MaintainHeight();
00358
00359 // Rate our distance to the target, and our visibilty
00360 distance = DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00361 visible = NPC_ClearLOS4( NPC->enemy );
00362 advance = (qboolean)(distance > MIN_DISTANCE_SQR);
00363
00364 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00365 {
00366 advance = (qboolean)(distance>(200.0f*200.0f));
00367 }
00368
00369 // If we cannot see our target, move to see it
00370 if ( visible == qfalse )
00371 {
00372 if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00373 {
00374 Seeker_Hunt( visible, advance );
00375 return;
00376 }
00377 }
00378
00379 Seeker_Ranged( visible, advance );
00380 }
|
|
|
Definition at line 383 of file NPC_AI_Seeker.c. References gentity_s::client, entityShared_t::currentOrigin, DistanceHorizontalSquared(), gentity_s::enemy, g_entities, gentity_t, gentity_s::health, gentity_s::inuse, MAX_GENTITIES, NPC, NPC_ClearLOS4(), NPCTEAM_NEUTRAL, NULL, entityState_s::number, gclient_s::playerTeam, gentity_s::r, random, gentity_s::random, gentity_s::s, SEEKER_SEEK_RADIUS, trap_EntitiesInBox(), vec3_t, VectorScale, and VectorSet. Referenced by Seeker_FollowOwner().
00384 {
00385 int numFound;
00386 float dis, bestDis = SEEKER_SEEK_RADIUS * SEEKER_SEEK_RADIUS + 1;
00387 vec3_t mins, maxs;
00388 int entityList[MAX_GENTITIES];
00389 gentity_t *ent, *best = NULL;
00390 int i;
00391
00392 VectorSet( maxs, SEEKER_SEEK_RADIUS, SEEKER_SEEK_RADIUS, SEEKER_SEEK_RADIUS );
00393 VectorScale( maxs, -1, mins );
00394
00395 numFound = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
00396
00397 for ( i = 0 ; i < numFound ; i++ )
00398 {
00399 ent = &g_entities[entityList[i]];
00400
00401 if ( ent->s.number == NPC->s.number
00402 || !ent->client //&& || !ent->NPC
00403 || ent->health <= 0
00404 || !ent->inuse )
00405 {
00406 continue;
00407 }
00408
00409 if ( ent->client->playerTeam == NPC->client->playerTeam || ent->client->playerTeam == NPCTEAM_NEUTRAL ) // don't attack same team or bots
00410 {
00411 continue;
00412 }
00413
00414 // try to find the closest visible one
00415 if ( !NPC_ClearLOS4( ent ))
00416 {
00417 continue;
00418 }
00419
00420 dis = DistanceHorizontalSquared( NPC->r.currentOrigin, ent->r.currentOrigin );
00421
00422 if ( dis <= bestDis )
00423 {
00424 bestDis = dis;
00425 best = ent;
00426 }
00427 }
00428
00429 if ( best )
00430 {
00431 // used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method.
00432 NPC->random = random() * 6.3f; // roughly 2pi
00433
00434 NPC->enemy = best;
00435 }
00436 }
|
|
|
|
Definition at line 439 of file NPC_AI_Seeker.c. References CHAN_AUTO, CLASS_BOBAFETT, gentity_s::client, cos(), entityShared_t::currentOrigin, DistanceHorizontalSquared(), gentity_s::enemy, gNPC_t::enemyCheckDebounceTime, g_entities, G_Sound(), G_SoundIndex(), gentity_t, gNPC_t::goalEntity, gNPC_t::goalRadius, gclient_s::jetPackTime, level, MIN_DISTANCE_SQR, NPC, gclient_s::NPC_class, NPC_MoveToGoal(), NPC_UpdateAngles(), NPCInfo, entityState_s::owner, gentity_s::parent, gclient_s::ps, qtrue, gentity_s::r, random, gentity_s::random, gentity_s::s, Seeker_FindEnemy(), Seeker_MaintainHeight(), sin(), level_locals_t::time, TIMER_Done(), TIMER_Set(), vec3_t, VectorMA, VectorSubtract, and playerState_s::velocity. Referenced by NPC_BSSeeker_Default().
00440 {
00441 float dis, minDistSqr;
00442 vec3_t pt, dir;
00443 gentity_t *owner = &g_entities[NPC->s.owner];
00444
00445 Seeker_MaintainHeight();
00446
00447 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00448 {
00449 owner = NPC->enemy;
00450 }
00451 if ( !owner || owner == NPC || !owner->client )
00452 {
00453 return;
00454 }
00455 //rwwFIXMEFIXME: Care about all clients not just 0
00456 dis = DistanceHorizontalSquared( NPC->r.currentOrigin, owner->r.currentOrigin );
00457
00458 minDistSqr = MIN_DISTANCE_SQR;
00459
00460 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00461 {
00462 if ( TIMER_Done( NPC, "flameTime" ) )
00463 {
00464 minDistSqr = 200*200;
00465 }
00466 }
00467
00468 if ( dis < minDistSqr )
00469 {
00470 // generally circle the player closely till we take an enemy..this is our target point
00471 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00472 {
00473 pt[0] = owner->r.currentOrigin[0] + cos( level.time * 0.001f + NPC->random ) * 250;
00474 pt[1] = owner->r.currentOrigin[1] + sin( level.time * 0.001f + NPC->random ) * 250;
00475 if ( NPC->client->jetPackTime < level.time )
00476 {
00477 pt[2] = NPC->r.currentOrigin[2] - 64;
00478 }
00479 else
00480 {
00481 pt[2] = owner->r.currentOrigin[2] + 200;
00482 }
00483 }
00484 else
00485 {
00486 pt[0] = owner->r.currentOrigin[0] + cos( level.time * 0.001f + NPC->random ) * 56;
00487 pt[1] = owner->r.currentOrigin[1] + sin( level.time * 0.001f + NPC->random ) * 56;
00488 pt[2] = owner->r.currentOrigin[2] + 40;
00489 }
00490
00491 VectorSubtract( pt, NPC->r.currentOrigin, dir );
00492 VectorMA( NPC->client->ps.velocity, 0.8f, dir, NPC->client->ps.velocity );
00493 }
00494 else
00495 {
00496 if ( NPC->client->NPC_class != CLASS_BOBAFETT )
00497 {
00498 if ( TIMER_Done( NPC, "seekerhiss" ))
00499 {
00500 TIMER_Set( NPC, "seekerhiss", 1000 + random() * 1000 );
00501 G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
00502 }
00503 }
00504
00505 // Hey come back!
00506 NPCInfo->goalEntity = owner;
00507 NPCInfo->goalRadius = 32;
00508 NPC_MoveToGoal( qtrue );
00509 NPC->parent = owner;
00510 }
00511
00512 if ( NPCInfo->enemyCheckDebounceTime < level.time )
00513 {
00514 // check twice a second to find a new enemy
00515 Seeker_FindEnemy();
00516 NPCInfo->enemyCheckDebounceTime = level.time + 500;
00517 }
00518
00519 NPC_UpdateAngles( qtrue, qtrue );
00520 }
|
|
||||||||||||
|
Definition at line 242 of file NPC_AI_Seeker.c. References gentity_s::client, entityShared_t::currentOrigin, gentity_s::enemy, g_spskill, gNPC_t::goalEntity, gNPC_t::goalRadius, vmCvar_t::integer, level, NPC, NPC_FaceEnemy(), NPC_GetMoveDirection(), NPCInfo, gclient_s::ps, qfalse, qtrue, gentity_s::r, SEEKER_FORWARD_BASE_SPEED, SEEKER_FORWARD_MULTIPLIER, Seeker_Strafe(), gNPC_t::standTime, level_locals_t::time, vec3_t, VectorMA, VectorNormalize(), VectorSubtract, and playerState_s::velocity. Referenced by Seeker_Attack(), and Seeker_Ranged().
00243 {
00244 float distance, speed;
00245 vec3_t forward;
00246
00247 NPC_FaceEnemy( qtrue );
00248
00249 // If we're not supposed to stand still, pursue the player
00250 if ( NPCInfo->standTime < level.time )
00251 {
00252 // Only strafe when we can see the player
00253 if ( visible )
00254 {
00255 Seeker_Strafe();
00256 return;
00257 }
00258 }
00259
00260 // If we don't want to advance, stop here
00261 if ( advance == qfalse )
00262 {
00263 return;
00264 }
00265
00266 // Only try and navigate if the player is visible
00267 if ( visible == qfalse )
00268 {
00269 // Move towards our goal
00270 NPCInfo->goalEntity = NPC->enemy;
00271 NPCInfo->goalRadius = 24;
00272
00273 // Get our direction from the navigator if we can't see our target
00274 if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
00275 {
00276 return;
00277 }
00278 }
00279 else
00280 {
00281 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
00282 distance = VectorNormalize( forward );
00283 }
00284
00285 speed = SEEKER_FORWARD_BASE_SPEED + SEEKER_FORWARD_MULTIPLIER * g_spskill.integer;
00286 VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
00287 }
|
|
|
Definition at line 49 of file NPC_AI_Seeker.c. References CLASS_BOBAFETT, gentity_s::client, entityShared_t::currentOrigin, gentity_s::enemy, fabs(), flrand(), gentity_t, gNPC_t::goalEntity, gNPC_t::lastGoalEntity, entityShared_t::maxs, NPC, gclient_s::NPC_class, NPC_UpdateAngles(), NPCInfo, NULL, gclient_s::ps, Q_irand(), qtrue, gentity_s::r, TIMER_Done(), TIMER_Set(), ucmd, usercmd_s::upmove, playerState_s::velocity, and VELOCITY_DECAY. Referenced by Seeker_Attack(), and Seeker_FollowOwner().
00050 {
00051 float dif;
00052
00053 // Update our angles regardless
00054 NPC_UpdateAngles( qtrue, qtrue );
00055
00056 // If we have an enemy, we should try to hover at or a little below enemy eye level
00057 if ( NPC->enemy )
00058 {
00059 if (TIMER_Done( NPC, "heightChange" ))
00060 {
00061 float difFactor;
00062
00063 TIMER_Set( NPC,"heightChange",Q_irand( 1000, 3000 ));
00064
00065 // Find the height difference
00066 dif = (NPC->enemy->r.currentOrigin[2] + flrand( NPC->enemy->r.maxs[2]/2, NPC->enemy->r.maxs[2]+8 )) - NPC->r.currentOrigin[2];
00067
00068 difFactor = 1.0f;
00069 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00070 {
00071 if ( TIMER_Done( NPC, "flameTime" ) )
00072 {
00073 difFactor = 10.0f;
00074 }
00075 }
00076
00077 // cap to prevent dramatic height shifts
00078 if ( fabs( dif ) > 2*difFactor )
00079 {
00080 if ( fabs( dif ) > 24*difFactor )
00081 {
00082 dif = ( dif < 0 ? -24*difFactor : 24*difFactor );
00083 }
00084
00085 NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
00086 }
00087 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00088 {
00089 NPC->client->ps.velocity[2] *= flrand( 0.85f, 3.0f );
00090 }
00091 }
00092 }
00093 else
00094 {
00095 gentity_t *goal = NULL;
00096
00097 if ( NPCInfo->goalEntity ) // Is there a goal?
00098 {
00099 goal = NPCInfo->goalEntity;
00100 }
00101 else
00102 {
00103 goal = NPCInfo->lastGoalEntity;
00104 }
00105 if ( goal )
00106 {
00107 dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
00108
00109 if ( fabs( dif ) > 24 )
00110 {
00111 ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
00112 }
00113 else
00114 {
00115 if ( NPC->client->ps.velocity[2] )
00116 {
00117 NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
00118
00119 if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
00120 {
00121 NPC->client->ps.velocity[2] = 0;
00122 }
00123 }
00124 }
00125 }
00126 }
00127
00128 // Apply friction
00129 if ( NPC->client->ps.velocity[0] )
00130 {
00131 NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
00132
00133 if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
00134 {
00135 NPC->client->ps.velocity[0] = 0;
00136 }
00137 }
00138
00139 if ( NPC->client->ps.velocity[1] )
00140 {
00141 NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
00142
00143 if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
00144 {
00145 NPC->client->ps.velocity[1] = 0;
00146 }
00147 }
00148 }
|
|
||||||||||||
|
Definition at line 320 of file NPC_AI_Seeker.c. References CLASS_BOBAFETT, gentity_s::client, gentity_s::count, G_Damage(), MOD_UNKNOWN, NPC, gclient_s::NPC_class, NPCInfo, NULL, Q_irand(), SCF_CHASE_ENEMIES, gNPC_t::scriptFlags, Seeker_Fire(), Seeker_Hunt(), TIMER_Done(), and TIMER_Set(). Referenced by Seeker_Attack().
00321 {
00322 if ( NPC->client->NPC_class != CLASS_BOBAFETT )
00323 {
00324 if ( NPC->count > 0 )
00325 {
00326 if ( TIMER_Done( NPC, "attackDelay" )) // Attack?
00327 {
00328 TIMER_Set( NPC, "attackDelay", Q_irand( 250, 2500 ));
00329 Seeker_Fire();
00330 NPC->count--;
00331 }
00332 }
00333 else
00334 {
00335 // out of ammo, so let it die...give it a push up so it can fall more and blow up on impact
00336 // NPC->client->ps.gravity = 900;
00337 // NPC->svFlags &= ~SVF_CUSTOM_GRAVITY;
00338 // NPC->client->ps.velocity[2] += 16;
00339 G_Damage( NPC, NPC, NPC, NULL, NULL, 999, 0, MOD_UNKNOWN );
00340 }
00341 }
00342
00343 if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
00344 {
00345 Seeker_Hunt( visible, advance );
00346 }
00347 }
|
|
|
Definition at line 151 of file NPC_AI_Seeker.c. References AngleVectors(), CHAN_AUTO, CLASS_BOBAFETT, gentity_s::client, crandom, entityShared_t::currentOrigin, trace_t::endpos, gentity_s::enemy, renderInfo_s::eyeAngles, trace_t::fraction, G_Sound(), G_SoundIndex(), level, MASK_SOLID, NPC, gclient_s::NPC_class, NPCInfo, NULL, entityState_s::number, gclient_s::ps, gentity_s::r, rand(), random, gclient_s::renderInfo, gentity_s::s, SEEKER_STRAFE_DIS, SEEKER_STRAFE_VEL, SEEKER_UPWARD_PUSH, gNPC_t::standTime, level_locals_t::time, trap_Trace(), vec3_t, VectorMA, VectorNormalize(), VectorSubtract, and playerState_s::velocity. Referenced by NPC_Seeker_Pain(), and Seeker_Hunt().
00152 {
00153 int side;
00154 vec3_t end, right, dir;
00155 trace_t tr;
00156
00157 if ( random() > 0.7f || !NPC->enemy || !NPC->enemy->client )
00158 {
00159 // Do a regular style strafe
00160 AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
00161
00162 // Pick a random strafe direction, then check to see if doing a strafe would be
00163 // reasonably valid
00164 side = ( rand() & 1 ) ? -1 : 1;
00165 VectorMA( NPC->r.currentOrigin, SEEKER_STRAFE_DIS * side, right, end );
00166
00167 trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
00168
00169 // Close enough
00170 if ( tr.fraction > 0.9f )
00171 {
00172 float vel = SEEKER_STRAFE_VEL;
00173 float upPush = SEEKER_UPWARD_PUSH;
00174 if ( NPC->client->NPC_class != CLASS_BOBAFETT )
00175 {
00176 G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
00177 }
00178 else
00179 {
00180 vel *= 3.0f;
00181 upPush *= 4.0f;
00182 }
00183 VectorMA( NPC->client->ps.velocity, vel*side, right, NPC->client->ps.velocity );
00184 // Add a slight upward push
00185 NPC->client->ps.velocity[2] += upPush;
00186
00187 NPCInfo->standTime = level.time + 1000 + random() * 500;
00188 }
00189 }
00190 else
00191 {
00192 float stDis;
00193
00194 // Do a strafe to try and keep on the side of their enemy
00195 AngleVectors( NPC->enemy->client->renderInfo.eyeAngles, dir, right, NULL );
00196
00197 // Pick a random side
00198 side = ( rand() & 1 ) ? -1 : 1;
00199 stDis = SEEKER_STRAFE_DIS;
00200 if ( NPC->client->NPC_class == CLASS_BOBAFETT )
00201 {
00202 stDis *= 2.0f;
00203 }
00204 VectorMA( NPC->enemy->r.currentOrigin, stDis * side, right, end );
00205
00206 // then add a very small bit of random in front of/behind the player action
00207 VectorMA( end, crandom() * 25, dir, end );
00208
00209 trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
00210
00211 // Close enough
00212 if ( tr.fraction > 0.9f )
00213 {
00214 float dis, upPush;
00215
00216 VectorSubtract( tr.endpos, NPC->r.currentOrigin, dir );
00217 dir[2] *= 0.25; // do less upward change
00218 dis = VectorNormalize( dir );
00219
00220 // Try to move the desired enemy side
00221 VectorMA( NPC->client->ps.velocity, dis, dir, NPC->client->ps.velocity );
00222
00223 upPush = SEEKER_UPWARD_PUSH;
00224 if ( NPC->client->NPC_class != CLASS_BOBAFETT )
00225 {
00226 G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
00227 }
00228 else
00229 {
00230 upPush *= 4.0f;
00231 }
00232
00233 // Add a slight upward push
00234 NPC->client->ps.velocity[2] += upPush;
00235
00236 NPCInfo->standTime = level.time + 2500 + random() * 500;
00237 }
00238 }
00239 }
|