1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669 |
- //NPC_utils.cpp
- // leave this line at the top for all NPC_xxxx.cpp files...
- #include "g_headers.h"
- #include "b_local.h"
- #include "Q3_Interface.h"
- extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt );
- int teamNumbers[TEAM_NUM_TEAMS];
- int teamStrength[TEAM_NUM_TEAMS];
- int teamCounter[TEAM_NUM_TEAMS];
- #define VALID_ATTACK_CONE 2.0f //Degrees
- void GetAnglesForDirection( const vec3_t p1, const vec3_t p2, vec3_t out );
- /*
- void CalcEntitySpot ( gentity_t *ent, spot_t spot, vec3_t point )
- Added: Uses shootAngles if a NPC has them
- */
- extern void ViewHeightFix(const gentity_t *const ent);
- extern void AddLeanOfs(const gentity_t *const ent, vec3_t point);
- extern void SubtractLeanOfs(const gentity_t *const ent, vec3_t point);
- void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point )
- {
- vec3_t forward, up, right;
- vec3_t start, end;
- trace_t tr;
- if ( !ent )
- {
- return;
- }
- ViewHeightFix(ent);
- switch ( spot )
- {
- case SPOT_ORIGIN:
- if(VectorCompare(ent->currentOrigin, vec3_origin))
- {//brush
- VectorSubtract(ent->absmax, ent->absmin, point);//size
- VectorMA(ent->absmin, 0.5, point, point);
- }
- else
- {
- VectorCopy ( ent->currentOrigin, point );
- }
- break;
- case SPOT_CHEST:
- case SPOT_HEAD:
- if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) && (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD) )
- {//Actual tag_head eyespot!
- //FIXME: Stasis aliens may have a problem here...
- VectorCopy( ent->client->renderInfo.eyePoint, point );
- if ( ent->client->NPC_class == CLASS_ATST )
- {//adjust up some
- point[2] += 28;//magic number :)
- }
- if ( ent->NPC )
- {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
- point[0] = ent->currentOrigin[0];
- point[1] = ent->currentOrigin[1];
- }
- else if ( !ent->s.number )
- {
- SubtractLeanOfs( ent, point );
- }
- }
- else
- {
- VectorCopy ( ent->currentOrigin, point );
- if ( ent->client )
- {
- point[2] += ent->client->ps.viewheight;
- }
- }
- if ( spot == SPOT_CHEST && ent->client )
- {
- if ( ent->client->NPC_class != CLASS_ATST )
- {//adjust up some
- point[2] -= ent->maxs[2]*0.2f;
- }
- }
- break;
- case SPOT_HEAD_LEAN:
- if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) && (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD) )
- {//Actual tag_head eyespot!
- //FIXME: Stasis aliens may have a problem here...
- VectorCopy( ent->client->renderInfo.eyePoint, point );
- if ( ent->client->NPC_class == CLASS_ATST )
- {//adjust up some
- point[2] += 28;//magic number :)
- }
- if ( ent->NPC )
- {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards
- point[0] = ent->currentOrigin[0];
- point[1] = ent->currentOrigin[1];
- }
- else if ( !ent->s.number )
- {
- SubtractLeanOfs( ent, point );
- }
- //NOTE: automatically takes leaning into account!
- }
- else
- {
- VectorCopy ( ent->currentOrigin, point );
- if ( ent->client )
- {
- point[2] += ent->client->ps.viewheight;
- }
- //AddLeanOfs ( ent, point );
- }
- break;
- //FIXME: implement...
- //case SPOT_CHEST:
- //Returns point 3/4 from tag_torso to tag_head?
- //break;
- case SPOT_LEGS:
- VectorCopy ( ent->currentOrigin, point );
- point[2] += (ent->mins[2] * 0.5);
- break;
- case SPOT_WEAPON:
- if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
- {
- AngleVectors( ent->NPC->shootAngles, forward, right, up );
- }
- else
- {
- AngleVectors( ent->client->ps.viewangles, forward, right, up );
- }
- CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point, 0 );
- //NOTE: automatically takes leaning into account!
- break;
- case SPOT_GROUND:
- // if entity is on the ground, just use it's absmin
- if ( ent->s.groundEntityNum != -1 )
- {
- VectorCopy( ent->currentOrigin, point );
- point[2] = ent->absmin[2];
- break;
- }
- // if it is reasonably close to the ground, give the point underneath of it
- VectorCopy( ent->currentOrigin, start );
- start[2] = ent->absmin[2];
- VectorCopy( start, end );
- end[2] -= 64;
- gi.trace( &tr, start, ent->mins, ent->maxs, end, ent->s.number, MASK_PLAYERSOLID );
- if ( tr.fraction < 1.0 )
- {
- VectorCopy( tr.endpos, point);
- break;
- }
- // otherwise just use the origin
- VectorCopy( ent->currentOrigin, point );
- break;
- default:
- VectorCopy ( ent->currentOrigin, point );
- break;
- }
- }
- //===================================================================================
- /*
- qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw )
- Added: option to do just pitch or just yaw
- Does not include "aim" in it's calculations
- FIXME: stop compressing angles into shorts!!!!
- */
- extern cvar_t *g_timescale;
- extern bool NPC_IsTrooper( gentity_t *ent );
- qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw )
- {
- #if 1
- float error;
- float decay;
- float targetPitch = 0;
- float targetYaw = 0;
- float yawSpeed;
- qboolean exact = qtrue;
- // if angle changes are locked; just keep the current angles
- // aimTime isn't even set anymore... so this code was never reached, but I need a way to lock NPC's yaw, so instead of making a new SCF_ flag, just use the existing render flag... - dmv
- if ( !NPC->enemy && ( (level.time < NPCInfo->aimTime) || NPC->client->renderInfo.renderFlags & RF_LOCKEDANGLE) )
- {
- if(doPitch)
- targetPitch = NPCInfo->lockedDesiredPitch;
- if(doYaw)
- targetYaw = NPCInfo->lockedDesiredYaw;
- }
- else
- {
- // we're changing the lockedDesired Pitch/Yaw below so it's lost it's original meaning, get rid of the lock flag
- NPC->client->renderInfo.renderFlags &= ~RF_LOCKEDANGLE;
- if(doPitch)
- {
- targetPitch = NPCInfo->desiredPitch;
- NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
- }
- if(doYaw)
- {
- targetYaw = NPCInfo->desiredYaw;
- NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
- }
- }
- if ( NPC->s.weapon == WP_EMPLACED_GUN )
- {
- // FIXME: this seems to do nothing, actually...
- yawSpeed = 20;
- }
- else
- {
- if ( NPC->client->NPC_class == CLASS_ROCKETTROOPER
- && !NPC->enemy )
- {//just slowly lookin' around
- yawSpeed = 1;
- }
- else
- {
- yawSpeed = NPCInfo->stats.yawSpeed;
- }
- }
- if ( NPC->s.weapon == WP_SABER && NPC->client->ps.forcePowersActive&(1<<FP_SPEED) )
- {
- yawSpeed *= 1.0f/g_timescale->value;
- }
- if (!NPC_IsTrooper(NPC)
- && NPC->enemy
- && !G_IsRidingVehicle( NPC )
- && NPC->client->NPC_class != CLASS_VEHICLE )
- {
- if (NPC->s.weapon==WP_BLASTER_PISTOL ||
- NPC->s.weapon==WP_BLASTER ||
- NPC->s.weapon==WP_BOWCASTER ||
- NPC->s.weapon==WP_REPEATER ||
- NPC->s.weapon==WP_FLECHETTE ||
- NPC->s.weapon==WP_BRYAR_PISTOL ||
- NPC->s.weapon==WP_NOGHRI_STICK)
- {
- yawSpeed *= 10.0f;
- }
- }
-
- if( doYaw )
- {
- // decay yaw error
- error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
- if( fabs(error) > MIN_ANGLE_ERROR )
- {
- if ( error )
- {
- exact = qfalse;
- decay = 60.0 + yawSpeed * 3;
- decay *= 50.0f / 1000.0f;//msec
- if ( error < 0.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- }
-
- ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
- }
- //FIXME: have a pitchSpeed?
- if( doPitch )
- {
- // decay pitch error
- error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
- if ( fabs(error) > MIN_ANGLE_ERROR )
- {
- if ( error )
- {
- exact = qfalse;
- decay = 60.0 + yawSpeed * 3;
- decay *= 50.0f / 1000.0f;//msec
- if ( error < 0.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- }
- ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
- }
- ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
- if ( exact && Q3_TaskIDPending( NPC, TID_ANGLE_FACE ) )
- {
- Q3_TaskIDComplete( NPC, TID_ANGLE_FACE );
- }
- return exact;
- #else
- float error;
- float decay;
- float targetPitch = 0;
- float targetYaw = 0;
- float yawSpeed;
- //float runningMod = NPCInfo->currentSpeed/100.0f;
- qboolean exact = qtrue;
- qboolean doSound = qfalse;
- // if angle changes are locked; just keep the current angles
- if ( level.time < NPCInfo->aimTime )
- {
- if(doPitch)
- targetPitch = NPCInfo->lockedDesiredPitch;
- if(doYaw)
- targetYaw = NPCInfo->lockedDesiredYaw;
- }
- else
- {
- if(doPitch)
- targetPitch = NPCInfo->desiredPitch;
- if(doYaw)
- targetYaw = NPCInfo->desiredYaw;
- // NPCInfo->aimTime = level.time + 250;
- if(doPitch)
- NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
- if(doYaw)
- NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
- }
- yawSpeed = NPCInfo->stats.yawSpeed;
- if(doYaw)
- {
- // decay yaw error
- error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
- if( fabs(error) > MIN_ANGLE_ERROR )
- {
- /*
- if(NPC->client->playerTeam == TEAM_BORG&&
- NPCInfo->behaviorState != BS_FACE&&NPCInfo->tempBehavior!= BS_FACE)
- {//HACK - borg turn more jittery
- if ( error )
- {
- exact = qfalse;
- decay = 60.0 + yawSpeed * 3;
- decay *= 50.0 / 1000.0;//msec
- //Snap to
- if(fabs(error) > 10)
- {
- if(random() > 0.6)
- {
- doSound = qtrue;
- }
- }
- if ( error < 0.0)//-10.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else if ( error > 0.0)//10.0 )
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- }
- else*/
-
- if ( error )
- {
- exact = qfalse;
- decay = 60.0 + yawSpeed * 3;
- decay *= 50.0 / 1000.0;//msec
- if ( error < 0.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- }
- ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
- }
- //FIXME: have a pitchSpeed?
- if(doPitch)
- {
- // decay pitch error
- error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
- if ( fabs(error) > MIN_ANGLE_ERROR )
- {
- /*
- if(NPC->client->playerTeam == TEAM_BORG&&
- NPCInfo->behaviorState != BS_FACE&&NPCInfo->tempBehavior!= BS_FACE)
- {//HACK - borg turn more jittery
- if ( error )
- {
- exact = qfalse;
- decay = 60.0 + yawSpeed * 3;
- decay *= 50.0 / 1000.0;//msec
- //Snap to
- if(fabs(error) > 10)
- {
- if(random() > 0.6)
- {
- doSound = qtrue;
- }
- }
- if ( error < 0.0)//-10.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else if ( error > 0.0)//10.0 )
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- }
- else*/
-
- if ( error )
- {
- exact = qfalse;
- decay = 60.0 + yawSpeed * 3;
- decay *= 50.0 / 1000.0;//msec
- if ( error < 0.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- }
- ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
- }
- ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
- /*
- if(doSound)
- {
- G_Sound(NPC, G_SoundIndex(va("sound/enemies/borg/borgservo%d.wav", Q_irand(1, 8))));
- }
- */
- return exact;
- #endif
- }
- void NPC_AimWiggle( vec3_t enemy_org )
- {
- //shoot for somewhere between the head and torso
- //NOTE: yes, I know this looks weird, but it works
- if ( NPCInfo->aimErrorDebounceTime < level.time )
- {
- NPCInfo->aimOfs[0] = 0.3*Q_flrand(NPC->enemy->mins[0], NPC->enemy->maxs[0]);
- NPCInfo->aimOfs[1] = 0.3*Q_flrand(NPC->enemy->mins[1], NPC->enemy->maxs[1]);
- if ( NPC->enemy->maxs[2] > 0 )
- {
- NPCInfo->aimOfs[2] = NPC->enemy->maxs[2]*Q_flrand(0.0f, -1.0f);
- }
- }
- VectorAdd( enemy_org, NPCInfo->aimOfs, enemy_org );
- }
- /*
- qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw )
- Includes aim when determining angles - so they don't always hit...
- */
- qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw )
- {
- #if 0
- float diff;
- float error;
- float targetPitch = 0;
- float targetYaw = 0;
- qboolean exact = qtrue;
- if ( level.time < NPCInfo->aimTime )
- {
- if( doPitch )
- targetPitch = NPCInfo->lockedDesiredPitch;
- if( doYaw )
- targetYaw = NPCInfo->lockedDesiredYaw;
- }
- else
- {
- if( doPitch )
- {
- targetPitch = NPCInfo->desiredPitch;
- NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
- }
- if( doYaw )
- {
- targetYaw = NPCInfo->desiredYaw;
- NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
- }
- }
- if( doYaw )
- {
- // add yaw error based on NPCInfo->aim value
- error = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
- if(Q_irand(0, 1))
- error *= -1;
- diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
- if ( diff )
- exact = qfalse;
-
- ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
- }
- if( doPitch )
- {
- // add pitch error based on NPCInfo->aim value
- error = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
- diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
- if ( diff )
- exact = qfalse;
-
- ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
- }
- ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
- return exact;
-
- #else
- float error, diff;
- float decay;
- float targetPitch = 0;
- float targetYaw = 0;
- qboolean exact = qtrue;
- // if angle changes are locked; just keep the current angles
- if ( level.time < NPCInfo->aimTime )
- {
- if(doPitch)
- targetPitch = NPCInfo->lockedDesiredPitch;
- if(doYaw)
- targetYaw = NPCInfo->lockedDesiredYaw;
- }
- else
- {
- if(doPitch)
- targetPitch = NPCInfo->desiredPitch;
- if(doYaw)
- targetYaw = NPCInfo->desiredYaw;
- // NPCInfo->aimTime = level.time + 250;
- if(doPitch)
- NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
- if(doYaw)
- NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
- }
- if ( NPCInfo->aimErrorDebounceTime < level.time )
- {
- if ( Q_irand(0, 1 ) )
- {
- NPCInfo->lastAimErrorYaw = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
- }
- if ( Q_irand(0, 1 ) )
- {
- NPCInfo->lastAimErrorPitch = ((float)(6 - NPCInfo->stats.aim)) * Q_flrand(-1, 1);
- }
- NPCInfo->aimErrorDebounceTime = level.time + Q_irand(250, 2000);
- }
- if(doYaw)
- {
- // decay yaw diff
- diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
-
- if ( diff)
- {
- exact = qfalse;
- decay = 60.0 + 80.0;
- decay *= 50.0f / 1000.0f;//msec
- if ( diff < 0.0 )
- {
- diff += decay;
- if ( diff > 0.0 )
- {
- diff = 0.0;
- }
- }
- else
- {
- diff -= decay;
- if ( diff < 0.0 )
- {
- diff = 0.0;
- }
- }
- }
-
- // add yaw error based on NPCInfo->aim value
- error = NPCInfo->lastAimErrorYaw;
- /*
- if(Q_irand(0, 1))
- {
- error *= -1;
- }
- */
- ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
- }
- if(doPitch)
- {
- // decay pitch diff
- diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
- if ( diff)
- {
- exact = qfalse;
- decay = 60.0 + 80.0;
- decay *= 50.0f / 1000.0f;//msec
- if ( diff < 0.0 )
- {
- diff += decay;
- if ( diff > 0.0 )
- {
- diff = 0.0;
- }
- }
- else
- {
- diff -= decay;
- if ( diff < 0.0 )
- {
- diff = 0.0;
- }
- }
- }
-
- error = NPCInfo->lastAimErrorPitch;
- ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
- }
- ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
- return exact;
- #endif
- }
- //===================================================================================
- /*
- static void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw )
- Does update angles on shootAngles
- */
- void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw )
- {//FIXME: shoot angles either not set right or not used!
- float error;
- float decay;
- float targetPitch = 0;
- float targetYaw = 0;
- if(doPitch)
- targetPitch = angles[PITCH];
- if(doYaw)
- targetYaw = angles[YAW];
- if(doYaw)
- {
- // decay yaw error
- error = AngleDelta ( NPCInfo->shootAngles[YAW], targetYaw );
- if ( error )
- {
- decay = 60.0 + 80.0 * NPCInfo->stats.aim;
- decay *= 100.0f / 1000.0f;//msec
- if ( error < 0.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- NPCInfo->shootAngles[YAW] = targetYaw + error;
- }
- if(doPitch)
- {
- // decay pitch error
- error = AngleDelta ( NPCInfo->shootAngles[PITCH], targetPitch );
- if ( error )
- {
- decay = 60.0 + 80.0 * NPCInfo->stats.aim;
- decay *= 100.0f / 1000.0f;//msec
- if ( error < 0.0 )
- {
- error += decay;
- if ( error > 0.0 )
- {
- error = 0.0;
- }
- }
- else
- {
- error -= decay;
- if ( error < 0.0 )
- {
- error = 0.0;
- }
- }
- }
- NPCInfo->shootAngles[PITCH] = targetPitch + error;
- }
- }
- /*
- void SetTeamNumbers (void)
- Sets the number of living clients on each team
- FIXME: Does not account for non-respawned players!
- FIXME: Don't include medics?
- */
- void SetTeamNumbers (void)
- {
- gentity_t *found;
- int i;
- for( i = 0; i < TEAM_NUM_TEAMS; i++ )
- {
- teamNumbers[i] = 0;
- teamStrength[i] = 0;
- }
- for( i = 0; i < 1 ; i++ )
- {
- found = &g_entities[i];
- if( found->client )
- {
- if( found->health > 0 )//FIXME: or if a player!
- {
- teamNumbers[found->client->playerTeam]++;
- teamStrength[found->client->playerTeam] += found->health;
- }
- }
- }
- for( i = 0; i < TEAM_NUM_TEAMS; i++ )
- {//Get the average health
- teamStrength[i] = floor( ((float)(teamStrength[i])) / ((float)(teamNumbers[i])) );
- }
- }
- extern stringID_table_t BSTable[];
- extern stringID_table_t BSETTable[];
- qboolean G_ActivateBehavior (gentity_t *self, int bset )
- {
- bState_t bSID = (bState_t)-1;
- char *bs_name = NULL;
- if ( !self )
- {
- return qfalse;
- }
- bs_name = self->behaviorSet[bset];
-
- if( !(VALIDSTRING( bs_name )) )
- {
- return qfalse;
- }
- if ( self->NPC )
- {
- bSID = (bState_t)(GetIDForString( BSTable, bs_name ));
- }
- if(bSID > -1)
- {
- self->NPC->tempBehavior = BS_DEFAULT;
- self->NPC->behaviorState = bSID;
- if ( bSID == BS_SEARCH || bSID == BS_WANDER )
- {
- //FIXME: Reimplement?
- if( self->waypoint != WAYPOINT_NONE )
- {
- NPC_BSSearchStart( self->waypoint, bSID );
- }
- else
- {
- self->waypoint = NAV::GetNearestNode(self);
- if( self->waypoint != WAYPOINT_NONE )
- {
- NPC_BSSearchStart( self->waypoint, bSID );
- }
- }
- }
- }
- else
- {
- Quake3Game()->DebugPrint( IGameInterface::WL_VERBOSE, "%s attempting to run bSet %s (%s)\n", self->targetname, GetStringForID( BSETTable, bset ), bs_name );
- Quake3Game()->RunScript( self, bs_name );
- }
- return qtrue;
- }
- /*
- =============================================================================
- Extended Functions
- =============================================================================
- */
- /*
- -------------------------
- NPC_ValidEnemy
- -------------------------
- */
- qboolean G_ValidEnemy( gentity_t *self, gentity_t *enemy )
- {
- //Must be a valid pointer
- if ( enemy == NULL )
- return qfalse;
- //Must not be me
- if ( enemy == self )
- return qfalse;
- //Must not be deleted
- if ( enemy->inuse == qfalse )
- return qfalse;
- //Must be alive
- if ( enemy->health <= 0 )
- return qfalse;
- //In case they're in notarget mode
- if ( enemy->flags & FL_NOTARGET )
- return qfalse;
- //Must be an NPC
- if ( enemy->client == NULL )
- {
- if ( enemy->svFlags&SVF_NONNPC_ENEMY )
- {//still potentially valid
- if (self->client)
- {
- if ( enemy->noDamageTeam == self->client->playerTeam )
- {
- return qfalse;
- }
- else
- {
- return qtrue;
- }
- }
- else
- {
- if ( enemy->noDamageTeam == self->noDamageTeam )
- {
- return qfalse;
- }
- else
- {
- return qtrue;
- }
- }
- }
- else
- {
- return qfalse;
- }
- }
- if ( enemy->client->playerTeam == TEAM_FREE && enemy->s.number < MAX_CLIENTS )
- {//An evil player, everyone attacks him
- return qtrue;
- }
- //Can't be on the same team
- if ( enemy->client->playerTeam == self->client->playerTeam )
- {
- return qfalse;
- }
- //if haven't seen him in a while, give up
- //if ( NPCInfo->enemyLastSeenTime != 0 && level.time - NPCInfo->enemyLastSeenTime > 7000 )//FIXME: make a stat?
- // return qfalse;
- if ( enemy->client->playerTeam == self->client->enemyTeam //simplest case: they're on my enemy team
- || (self->client->enemyTeam == TEAM_FREE && enemy->client->NPC_class != self->client->NPC_class )//I get mad at anyone and this guy isn't the same class as me
- || (enemy->client->NPC_class == CLASS_WAMPA && enemy->enemy )//a rampaging wampa
- || (enemy->client->NPC_class == CLASS_RANCOR && enemy->enemy )//a rampaging rancor
- || (enemy->client->playerTeam == TEAM_FREE && enemy->client->enemyTeam == TEAM_FREE && enemy->enemy && enemy->enemy->client && (enemy->enemy->client->playerTeam == self->client->playerTeam||(enemy->enemy->client->playerTeam != TEAM_ENEMY&&self->client->playerTeam==TEAM_PLAYER))) //enemy is a rampaging non-aligned creature who is attacking someone on our team or a non-enemy (this last condition is used only if we're a good guy - in effect, we protect the innocent)
- )
- {
- return qtrue;
- }
- //all other cases = false?
- return qfalse;
- }
- qboolean NPC_ValidEnemy( gentity_t *ent )
- {
- return G_ValidEnemy( NPC, ent );
- }
- /*
- -------------------------
- NPC_TargetVisible
- -------------------------
- */
- qboolean NPC_TargetVisible( gentity_t *ent )
- {
- //Make sure we're in a valid range
- if ( DistanceSquared( ent->currentOrigin, NPC->currentOrigin ) > ( NPCInfo->stats.visrange * NPCInfo->stats.visrange ) )
- return qfalse;
- //Check our FOV
- if ( InFOV( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
- return qfalse;
- //Check for sight
- if ( NPC_ClearLOS( ent ) == qfalse )
- return qfalse;
- return qtrue;
- }
- /*
- -------------------------
- NPC_GetCheckDelta
- -------------------------
- */
- /*
- #define CHECK_TIME_BASE 250
- #define CHECK_TIME_BASE_SQUARED ( CHECK_TIME_BASE * CHECK_TIME_BASE )
- static int NPC_GetCheckDelta( void )
- {
- if ( NPC_ValidEnemy( NPC->enemy ) == qfalse )
- {
- int distance = DistanceSquared( NPC->currentOrigin, g_entities[0].currentOrigin );
- distance /= CHECK_TIME_BASE_SQUARED;
- return ( CHECK_TIME_BASE * distance );
- }
- return 0;
- }
- */
- /*
- -------------------------
- NPC_FindNearestEnemy
- -------------------------
- */
- #define MAX_RADIUS_ENTS 256 //NOTE: This can cause entities to be lost
- #define NEAR_DEFAULT_RADIUS 256
- extern gentity_t *G_CheckControlledTurretEnemy(gentity_t *self, gentity_t *enemy, qboolean validate );
- int NPC_FindNearestEnemy( gentity_t *ent )
- {
- gentity_t *radiusEnts[ MAX_RADIUS_ENTS ];
- gentity_t *nearest;
- vec3_t mins, maxs;
- int nearestEntID = -1;
- float nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
- float distance;
- int numEnts, numChecks = 0;
- //Setup the bbox to search in
- for ( int i = 0; i < 3; i++ )
- {
- mins[i] = ent->currentOrigin[i] - NPCInfo->stats.visrange;
- maxs[i] = ent->currentOrigin[i] + NPCInfo->stats.visrange;
- }
- //Get a number of entities in a given space
- numEnts = gi.EntitiesInBox( mins, maxs, radiusEnts, MAX_RADIUS_ENTS );
- for ( i = 0; i < numEnts; i++ )
- {
- nearest = G_CheckControlledTurretEnemy(ent, radiusEnts[i], qtrue);
- //Don't consider self
- if ( nearest == ent )
- continue;
- //Must be valid
- if ( NPC_ValidEnemy( nearest ) == qfalse )
- continue;
- numChecks++;
- //Must be visible
- if ( NPC_TargetVisible( nearest ) == qfalse )
- continue;
- distance = DistanceSquared( ent->currentOrigin, nearest->currentOrigin );
- //Found one closer to us
- if ( distance < nearestDist )
- {
- nearestEntID = nearest->s.number;
- nearestDist = distance;
- }
- }
- return nearestEntID;
- }
- /*
- -------------------------
- NPC_PickEnemyExt
- -------------------------
- */
- gentity_t *NPC_PickEnemyExt( qboolean checkAlerts = qfalse )
- {
- //Check for Hazard Team status and remove this check
- /*
- if ( NPC->client->playerTeam != TEAM_STARFLEET )
- {
- //If we've found the player, return it
- if ( NPC_FindPlayer() )
- return &g_entities[0];
- }
- */
- //If we've asked for the closest enemy
- int entID = NPC_FindNearestEnemy( NPC );
- //If we have a valid enemy, use it
- if ( entID >= 0 )
- return &g_entities[entID];
- if ( checkAlerts )
- {
- int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
- //There is an event to look at
- if ( alertEvent >= 0 )
- {
- alertEvent_t *event = &level.alertEvents[alertEvent];
- //Don't pay attention to our own alerts
- if ( event->owner == NPC )
- return NULL;
- if ( event->level >= AEL_DISCOVERED )
- {
- //If it's the player, attack him
- if ( event->owner == &g_entities[0] )
- return event->owner;
- //If it's on our team, then take its enemy as well
- if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPC->client->playerTeam ) )
- return event->owner->enemy;
- }
- }
- }
- return NULL;
- }
- /*
- -------------------------
- NPC_FindPlayer
- -------------------------
- */
- qboolean NPC_FindPlayer( void )
- {
- return NPC_TargetVisible( &g_entities[0] );
- }
- /*
- -------------------------
- NPC_CheckPlayerDistance
- -------------------------
- */
- static qboolean NPC_CheckPlayerDistance( void )
- {
- //Make sure we have an enemy
- if ( NPC->enemy == NULL )
- return qfalse;
- //Only do this for non-players
- if ( NPC->enemy->s.number == 0 )
- return qfalse;
- //must be set up to get mad at player
- if ( !NPC->client || NPC->client->enemyTeam != TEAM_PLAYER )
- return qfalse;
- //Must be within our FOV
- if ( InFOV( &g_entities[0], NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
- return qfalse;
- float distance = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
- if ( distance > DistanceSquared( NPC->currentOrigin, g_entities[0].currentOrigin ) )
- {
- G_SetEnemy( NPC, &g_entities[0] );
- return qtrue;
- }
- return qfalse;
- }
- /*
- -------------------------
- NPC_FindEnemy
- -------------------------
- */
- 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 )
- {
- G_ClearEnemy( NPC );
- return qfalse;
- }
- //Don't want a new enemy
- if ( ( NPC_ValidEnemy( NPC->enemy ) ) && ( NPC->svFlags & SVF_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;
- }
- //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;
- }
- G_ClearEnemy( NPC );
- return qfalse;
- }
- /*
- -------------------------
- NPC_CheckEnemyExt
- -------------------------
- */
- qboolean NPC_CheckEnemyExt( qboolean checkAlerts )
- {
- //Make sure we're ready to think again
- /*
- if ( NPCInfo->enemyCheckDebounceTime > level.time )
- return qfalse;
- //Get our next think time
- NPCInfo->enemyCheckDebounceTime = level.time + NPC_GetCheckDelta();
- //Attempt to find an enemy
- return NPC_FindEnemy();
- */
- return NPC_FindEnemy( checkAlerts );
- }
- /*
- -------------------------
- NPC_FacePosition
- -------------------------
- */
- qboolean NPC_FacePosition( vec3_t position, qboolean doPitch )
- {
- vec3_t muzzle;
- 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->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
- if ( NPC->client->NPC_class == CLASS_ROCKETTROOPER )
- {//*sigh*, look down more
- position[2] -= 32;
- }
- }
- //Find the desired angles
- vec3_t 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 += Q_flrand( -5, 5 ) + sin( level.time * 0.004f ) * 7;
- NPCInfo->desiredPitch += Q_flrand( -2, 2 );
- }
- //Face that yaw
- NPC_UpdateAngles( qtrue, qtrue );
- //Find the delta between our goal and our current facing
- float 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;
- }
- /*
- -------------------------
- NPC_FaceEntity
- -------------------------
- */
- 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_FaceEnemy
- -------------------------
- */
- qboolean NPC_FaceEnemy( qboolean doPitch )
- {
- if ( NPC == NULL )
- return qfalse;
- if ( NPC->enemy == NULL )
- return qfalse;
- return NPC_FaceEntity( NPC->enemy, doPitch );
- }
- /*
- -------------------------
- NPC_CheckCanAttackExt
- -------------------------
- */
- qboolean NPC_CheckCanAttackExt( void )
- {
- //We don't want them to shoot
- if( NPCInfo->scriptFlags & SCF_DONT_FIRE )
- return qfalse;
- //Turn to face
- if ( NPC_FaceEnemy( qtrue ) == qfalse )
- return qfalse;
- //Must have a clear line of sight to the target
- if ( NPC_ClearShot( NPC->enemy ) == qfalse )
- return qfalse;
- return qtrue;
- }
- /*
- -------------------------
- NPC_ClearLookTarget
- -------------------------
- */
- void NPC_ClearLookTarget( gentity_t *self )
- {
- if ( !self->client )
- {
- return;
- }
- self->client->renderInfo.lookTarget = ENTITYNUM_NONE;//ENTITYNUM_WORLD;
- self->client->renderInfo.lookTargetClearTime = 0;
- }
- /*
- -------------------------
- NPC_SetLookTarget
- -------------------------
- */
- void NPC_SetLookTarget( gentity_t *self, int entNum, int clearTime )
- {
- if ( !self->client )
- {
- return;
- }
- self->client->renderInfo.lookTarget = entNum;
- self->client->renderInfo.lookTargetClearTime = clearTime;
- }
- /*
- -------------------------
- NPC_CheckLookTarget
- -------------------------
- */
- qboolean NPC_CheckLookTarget( gentity_t *self )
- {
- if ( self->client )
- {
- if ( self->client->renderInfo.lookTarget >= 0 && self->client->renderInfo.lookTarget < ENTITYNUM_WORLD )
- {//within valid range
- if ( (&g_entities[self->client->renderInfo.lookTarget] == NULL) || !g_entities[self->client->renderInfo.lookTarget].inuse )
- {//lookTarget not inuse or not valid anymore
- NPC_ClearLookTarget( self );
- }
- else if ( self->client->renderInfo.lookTargetClearTime && self->client->renderInfo.lookTargetClearTime < level.time )
- {//Time to clear lookTarget
- NPC_ClearLookTarget( self );
- }
- else if ( g_entities[self->client->renderInfo.lookTarget].client && self->enemy && (&g_entities[self->client->renderInfo.lookTarget] != self->enemy) )
- {//should always look at current enemy if engaged in battle... FIXME: this could override certain scripted lookTargets...???
- NPC_ClearLookTarget( self );
- }
- else
- {
- return qtrue;
- }
- }
- }
- return qfalse;
- }
- /*
- -------------------------
- NPC_CheckCharmed
- -------------------------
- */
- extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
- void G_CheckCharmed( gentity_t *self )
- {
- if ( self
- && self->client
- && self->client->playerTeam == TEAM_PLAYER
- && self->NPC
- && self->NPC->charmedTime
- && (self->NPC->charmedTime < level.time ||self->health <= 0) )
- {//we were charmed, set us back!
- //NOTE: presumptions here...
- team_t savTeam = self->client->enemyTeam;
- self->client->enemyTeam = self->client->playerTeam;
- self->client->playerTeam = savTeam;
- self->client->leader = NULL;
- self->NPC->charmedTime = 0;
- if ( self->health > 0 )
- {
- if ( self->NPC->tempBehavior == BS_FOLLOW_LEADER )
- {
- self->NPC->tempBehavior = BS_DEFAULT;
- }
- G_ClearEnemy( self );
- //say something to let player know you've snapped out of it
- G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
- }
- }
- }
- void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex = 0 )
- {
- if ( !self || !self->ghoul2.size() )
- {
- return;
- }
- mdxaBone_t boltMatrix;
- vec3_t result, angles={0,self->currentAngles[YAW],0};
- gi.G2API_GetBoltMatrix( self->ghoul2, modelIndex,
- boltIndex,
- &boltMatrix, angles, self->currentOrigin, (cg.time?cg.time:level.time),
- NULL, self->s.modelScale );
- if ( pos )
- {
- gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, result );
- VectorCopy( result, pos );
- }
- }
- float NPC_EntRangeFromBolt( gentity_t *targEnt, int boltIndex )
- {
- vec3_t org;
- if ( !targEnt )
- {
- return Q3_INFINITE;
- }
- G_GetBoltPosition( NPC, boltIndex, org );
- return (Distance( targEnt->currentOrigin, org ));
- }
- float NPC_EnemyRangeFromBolt( int boltIndex )
- {
- return (NPC_EntRangeFromBolt( NPC->enemy, boltIndex ));
- }
- int G_GetEntsNearBolt( gentity_t *self, gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg )
- {
- vec3_t mins, maxs;
- int i;
- //get my handRBolt's position
- vec3_t org;
- G_GetBoltPosition( self, boltIndex, org );
- VectorCopy( org, boltOrg );
- //Setup the bbox to search in
- for ( i = 0; i < 3; i++ )
- {
- mins[i] = boltOrg[i] - radius;
- maxs[i] = boltOrg[i] + radius;
- }
- //Get the number of entities in a given space
- return (gi.EntitiesInBox( mins, maxs, radiusEnts, 128 ));
- }
- int NPC_GetEntsNearBolt( gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg )
- {
- return (G_GetEntsNearBolt( NPC, radiusEnts, radius, boltIndex, boltOrg ));
- }
- extern qboolean RT_Flying( gentity_t *self );
- extern void RT_FlyStart( gentity_t *self );
- extern void RT_FlyStop( gentity_t *self );
- extern qboolean Boba_Flying( gentity_t *self );
- extern void Boba_FlyStart( gentity_t *self );
- extern void Boba_FlyStop( gentity_t *self );
- qboolean JET_Flying( gentity_t *self )
- {
- if ( !self || !self->client )
- {
- return qfalse;
- }
- if ( self->client->NPC_class == CLASS_BOBAFETT )
- {
- return (Boba_Flying(self));
- }
- else if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
- {
- return (RT_Flying(self));
- }
- else
- {
- return qfalse;
- }
- }
- void JET_FlyStart( gentity_t *self )
- {
- if ( !self || !self->client )
- {
- return;
- }
- self->lastInAirTime = level.time;
- if ( self->client->NPC_class == CLASS_BOBAFETT )
- {
- Boba_FlyStart( self );
- }
- else if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
- {
- RT_FlyStart( self );
- }
- }
- void JET_FlyStop( gentity_t *self )
- {
- if ( !self || !self->client )
- {
- return;
- }
- if ( self->client->NPC_class == CLASS_BOBAFETT )
- {
- Boba_FlyStop( self );
- }
- else if ( self->client->NPC_class == CLASS_ROCKETTROOPER )
- {
- RT_FlyStop( self );
- }
- }
|