00001
00002
00003 #include "b_local.h"
00004 #include "../icarus/Q3_Interface.h"
00005 #include "../ghoul2/G2.h"
00006
00007 int teamNumbers[TEAM_NUM_TEAMS];
00008 int teamStrength[TEAM_NUM_TEAMS];
00009 int teamCounter[TEAM_NUM_TEAMS];
00010
00011 #define VALID_ATTACK_CONE 2.0f //Degrees
00012 extern void G_DebugPrint( int level, const char *format, ... );
00013
00014
00015
00016
00017
00018
00019
00020 void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point )
00021 {
00022 vec3_t forward, up, right;
00023 vec3_t start, end;
00024 trace_t tr;
00025
00026 if ( !ent )
00027 {
00028 return;
00029 }
00030 switch ( spot )
00031 {
00032 case SPOT_ORIGIN:
00033 if(VectorCompare(ent->r.currentOrigin, vec3_origin))
00034 {
00035 VectorSubtract(ent->r.absmax, ent->r.absmin, point);
00036 VectorMA(ent->r.absmin, 0.5, point, point);
00037 }
00038 else
00039 {
00040 VectorCopy ( ent->r.currentOrigin, point );
00041 }
00042 break;
00043
00044 case SPOT_CHEST:
00045 case SPOT_HEAD:
00046 if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) )
00047 {
00048
00049 VectorCopy( ent->client->renderInfo.eyePoint, point );
00050 if ( ent->client->NPC_class == CLASS_ATST )
00051 {
00052 point[2] += 28;
00053 }
00054 if ( ent->NPC )
00055 {
00056 point[0] = ent->r.currentOrigin[0];
00057 point[1] = ent->r.currentOrigin[1];
00058 }
00059
00060
00061
00062
00063
00064
00065 }
00066 else
00067 {
00068 VectorCopy ( ent->r.currentOrigin, point );
00069 if ( ent->client )
00070 {
00071 point[2] += ent->client->ps.viewheight;
00072 }
00073 }
00074 if ( spot == SPOT_CHEST && ent->client )
00075 {
00076 if ( ent->client->NPC_class != CLASS_ATST )
00077 {
00078 point[2] -= ent->r.maxs[2]*0.2f;
00079 }
00080 }
00081 break;
00082
00083 case SPOT_HEAD_LEAN:
00084 if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) )
00085 {
00086
00087 VectorCopy( ent->client->renderInfo.eyePoint, point );
00088 if ( ent->client->NPC_class == CLASS_ATST )
00089 {
00090 point[2] += 28;
00091 }
00092 if ( ent->NPC )
00093 {
00094 point[0] = ent->r.currentOrigin[0];
00095 point[1] = ent->r.currentOrigin[1];
00096 }
00097
00098
00099
00100
00101
00102
00103
00104 }
00105 else
00106 {
00107 VectorCopy ( ent->r.currentOrigin, point );
00108 if ( ent->client )
00109 {
00110 point[2] += ent->client->ps.viewheight;
00111 }
00112
00113 }
00114 break;
00115
00116
00117
00118
00119
00120
00121 case SPOT_LEGS:
00122 VectorCopy ( ent->r.currentOrigin, point );
00123 point[2] += (ent->r.mins[2] * 0.5);
00124 break;
00125
00126 case SPOT_WEAPON:
00127 if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
00128 {
00129 AngleVectors( ent->NPC->shootAngles, forward, right, up );
00130 }
00131 else
00132 {
00133 AngleVectors( ent->client->ps.viewangles, forward, right, up );
00134 }
00135 CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point );
00136
00137 break;
00138
00139 case SPOT_GROUND:
00140
00141 if ( ent->s.groundEntityNum != -1 )
00142 {
00143 VectorCopy( ent->r.currentOrigin, point );
00144 point[2] = ent->r.absmin[2];
00145 break;
00146 }
00147
00148
00149 VectorCopy( ent->r.currentOrigin, start );
00150 start[2] = ent->r.absmin[2];
00151 VectorCopy( start, end );
00152 end[2] -= 64;
00153 trap_Trace( &tr, start, ent->r.mins, ent->r.maxs, end, ent->s.number, MASK_PLAYERSOLID );
00154 if ( tr.fraction < 1.0 )
00155 {
00156 VectorCopy( tr.endpos, point);
00157 break;
00158 }
00159
00160
00161 VectorCopy( ent->r.currentOrigin, point );
00162 break;
00163
00164 default:
00165 VectorCopy ( ent->r.currentOrigin, point );
00166 break;
00167 }
00168 }
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182 qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw )
00183 {
00184 #if 1
00185
00186 float error;
00187 float decay;
00188 float targetPitch = 0;
00189 float targetYaw = 0;
00190 float yawSpeed;
00191 qboolean exact = qtrue;
00192
00193
00194
00195 if ( !NPC->enemy && ( (level.time < NPCInfo->aimTime) ) )
00196 {
00197 if(doPitch)
00198 targetPitch = NPCInfo->lockedDesiredPitch;
00199
00200 if(doYaw)
00201 targetYaw = NPCInfo->lockedDesiredYaw;
00202 }
00203 else
00204 {
00205
00206
00207
00208 if(doPitch)
00209 {
00210 targetPitch = NPCInfo->desiredPitch;
00211 NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00212 }
00213
00214 if(doYaw)
00215 {
00216 targetYaw = NPCInfo->desiredYaw;
00217 NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00218 }
00219 }
00220
00221 if ( NPC->s.weapon == WP_EMPLACED_GUN )
00222 {
00223
00224 yawSpeed = 20;
00225 }
00226 else
00227 {
00228 yawSpeed = NPCInfo->stats.yawSpeed;
00229 }
00230
00231 if ( NPC->s.weapon == WP_SABER && NPC->client->ps.fd.forcePowersActive&(1<<FP_SPEED) )
00232 {
00233 char buf[128];
00234 float tFVal = 0;
00235
00236 trap_Cvar_VariableStringBuffer("timescale", buf, sizeof(buf));
00237
00238 tFVal = atof(buf);
00239
00240 yawSpeed *= 1.0f/tFVal;
00241 }
00242
00243 if( doYaw )
00244 {
00245
00246 error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00247 if( fabs(error) > MIN_ANGLE_ERROR )
00248 {
00249 if ( error )
00250 {
00251 exact = qfalse;
00252
00253 decay = 60.0 + yawSpeed * 3;
00254 decay *= 50.0f / 1000.0f;
00255
00256 if ( error < 0.0 )
00257 {
00258 error += decay;
00259 if ( error > 0.0 )
00260 {
00261 error = 0.0;
00262 }
00263 }
00264 else
00265 {
00266 error -= decay;
00267 if ( error < 0.0 )
00268 {
00269 error = 0.0;
00270 }
00271 }
00272 }
00273 }
00274
00275 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
00276 }
00277
00278
00279 if( doPitch )
00280 {
00281
00282 error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00283 if ( fabs(error) > MIN_ANGLE_ERROR )
00284 {
00285 if ( error )
00286 {
00287 exact = qfalse;
00288
00289 decay = 60.0 + yawSpeed * 3;
00290 decay *= 50.0f / 1000.0f;
00291
00292 if ( error < 0.0 )
00293 {
00294 error += decay;
00295 if ( error > 0.0 )
00296 {
00297 error = 0.0;
00298 }
00299 }
00300 else
00301 {
00302 error -= decay;
00303 if ( error < 0.0 )
00304 {
00305 error = 0.0;
00306 }
00307 }
00308 }
00309 }
00310
00311 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
00312 }
00313
00314 ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00315
00316 if ( exact && trap_ICARUS_TaskIDPending( NPC, TID_ANGLE_FACE ) )
00317 {
00318 trap_ICARUS_TaskIDComplete( NPC, TID_ANGLE_FACE );
00319 }
00320 return exact;
00321
00322 #else
00323
00324 float error;
00325 float decay;
00326 float targetPitch = 0;
00327 float targetYaw = 0;
00328 float yawSpeed;
00329
00330 qboolean exact = qtrue;
00331 qboolean doSound = qfalse;
00332
00333
00334 if ( level.time < NPCInfo->aimTime )
00335 {
00336 if(doPitch)
00337 targetPitch = NPCInfo->lockedDesiredPitch;
00338 if(doYaw)
00339 targetYaw = NPCInfo->lockedDesiredYaw;
00340 }
00341 else
00342 {
00343 if(doPitch)
00344 targetPitch = NPCInfo->desiredPitch;
00345 if(doYaw)
00346 targetYaw = NPCInfo->desiredYaw;
00347
00348
00349 if(doPitch)
00350 NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00351 if(doYaw)
00352 NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00353 }
00354
00355 yawSpeed = NPCInfo->stats.yawSpeed;
00356
00357 if(doYaw)
00358 {
00359
00360 error = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00361 if( fabs(error) > MIN_ANGLE_ERROR )
00362 {
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402 if ( error )
00403 {
00404 exact = qfalse;
00405
00406 decay = 60.0 + yawSpeed * 3;
00407 decay *= 50.0 / 1000.0;
00408
00409 if ( error < 0.0 )
00410 {
00411 error += decay;
00412 if ( error > 0.0 )
00413 {
00414 error = 0.0;
00415 }
00416 }
00417 else
00418 {
00419 error -= decay;
00420 if ( error < 0.0 )
00421 {
00422 error = 0.0;
00423 }
00424 }
00425 }
00426 }
00427 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + error ) - client->ps.delta_angles[YAW];
00428 }
00429
00430
00431 if(doPitch)
00432 {
00433
00434 error = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00435 if ( fabs(error) > MIN_ANGLE_ERROR )
00436 {
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 if ( error )
00477 {
00478 exact = qfalse;
00479
00480 decay = 60.0 + yawSpeed * 3;
00481 decay *= 50.0 / 1000.0;
00482
00483 if ( error < 0.0 )
00484 {
00485 error += decay;
00486 if ( error > 0.0 )
00487 {
00488 error = 0.0;
00489 }
00490 }
00491 else
00492 {
00493 error -= decay;
00494 if ( error < 0.0 )
00495 {
00496 error = 0.0;
00497 }
00498 }
00499 }
00500 }
00501 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + error ) - client->ps.delta_angles[PITCH];
00502 }
00503
00504 ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00505
00506
00507
00508
00509
00510
00511
00512
00513 return exact;
00514
00515 #endif
00516
00517 }
00518
00519 void NPC_AimWiggle( vec3_t enemy_org )
00520 {
00521
00522
00523 if ( NPCInfo->aimErrorDebounceTime < level.time )
00524 {
00525 NPCInfo->aimOfs[0] = 0.3*flrand(NPC->enemy->r.mins[0], NPC->enemy->r.maxs[0]);
00526 NPCInfo->aimOfs[1] = 0.3*flrand(NPC->enemy->r.mins[1], NPC->enemy->r.maxs[1]);
00527 if ( NPC->enemy->r.maxs[2] > 0 )
00528 {
00529 NPCInfo->aimOfs[2] = NPC->enemy->r.maxs[2]*flrand(0.0f, -1.0f);
00530 }
00531 }
00532 VectorAdd( enemy_org, NPCInfo->aimOfs, enemy_org );
00533 }
00534
00535
00536
00537
00538
00539
00540 qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw )
00541 {
00542
00543 #if 0
00544
00545 float diff;
00546 float error;
00547 float targetPitch = 0;
00548 float targetYaw = 0;
00549 qboolean exact = qtrue;
00550
00551 if ( level.time < NPCInfo->aimTime )
00552 {
00553 if( doPitch )
00554 targetPitch = NPCInfo->lockedDesiredPitch;
00555
00556 if( doYaw )
00557 targetYaw = NPCInfo->lockedDesiredYaw;
00558 }
00559 else
00560 {
00561 if( doPitch )
00562 {
00563 targetPitch = NPCInfo->desiredPitch;
00564 NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00565 }
00566
00567 if( doYaw )
00568 {
00569 targetYaw = NPCInfo->desiredYaw;
00570 NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00571 }
00572 }
00573
00574 if( doYaw )
00575 {
00576
00577 error = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00578
00579 if(Q_irand(0, 1))
00580 error *= -1;
00581
00582 diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00583
00584 if ( diff )
00585 exact = qfalse;
00586
00587 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
00588 }
00589
00590 if( doPitch )
00591 {
00592
00593 error = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00594
00595 diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00596
00597 if ( diff )
00598 exact = qfalse;
00599
00600 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
00601 }
00602
00603 ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00604
00605 return exact;
00606
00607 #else
00608
00609 float error, diff;
00610 float decay;
00611 float targetPitch = 0;
00612 float targetYaw = 0;
00613 qboolean exact = qtrue;
00614
00615
00616 if ( level.time < NPCInfo->aimTime )
00617 {
00618 if(doPitch)
00619 targetPitch = NPCInfo->lockedDesiredPitch;
00620 if(doYaw)
00621 targetYaw = NPCInfo->lockedDesiredYaw;
00622 }
00623 else
00624 {
00625 if(doPitch)
00626 targetPitch = NPCInfo->desiredPitch;
00627 if(doYaw)
00628 targetYaw = NPCInfo->desiredYaw;
00629
00630
00631 if(doPitch)
00632 NPCInfo->lockedDesiredPitch = NPCInfo->desiredPitch;
00633 if(doYaw)
00634 NPCInfo->lockedDesiredYaw = NPCInfo->desiredYaw;
00635 }
00636
00637 if ( NPCInfo->aimErrorDebounceTime < level.time )
00638 {
00639 if ( Q_irand(0, 1 ) )
00640 {
00641 NPCInfo->lastAimErrorYaw = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00642 }
00643 if ( Q_irand(0, 1 ) )
00644 {
00645 NPCInfo->lastAimErrorPitch = ((float)(6 - NPCInfo->stats.aim)) * flrand(-1, 1);
00646 }
00647 NPCInfo->aimErrorDebounceTime = level.time + Q_irand(250, 2000);
00648 }
00649
00650 if(doYaw)
00651 {
00652
00653 diff = AngleDelta ( NPC->client->ps.viewangles[YAW], targetYaw );
00654
00655 if ( diff)
00656 {
00657 exact = qfalse;
00658
00659 decay = 60.0 + 80.0;
00660 decay *= 50.0f / 1000.0f;
00661 if ( diff < 0.0 )
00662 {
00663 diff += decay;
00664 if ( diff > 0.0 )
00665 {
00666 diff = 0.0;
00667 }
00668 }
00669 else
00670 {
00671 diff -= decay;
00672 if ( diff < 0.0 )
00673 {
00674 diff = 0.0;
00675 }
00676 }
00677 }
00678
00679
00680 error = NPCInfo->lastAimErrorYaw;
00681
00682
00683
00684
00685
00686
00687
00688
00689 ucmd.angles[YAW] = ANGLE2SHORT( targetYaw + diff + error ) - client->ps.delta_angles[YAW];
00690 }
00691
00692 if(doPitch)
00693 {
00694
00695 diff = AngleDelta ( NPC->client->ps.viewangles[PITCH], targetPitch );
00696 if ( diff)
00697 {
00698 exact = qfalse;
00699
00700 decay = 60.0 + 80.0;
00701 decay *= 50.0f / 1000.0f;
00702 if ( diff < 0.0 )
00703 {
00704 diff += decay;
00705 if ( diff > 0.0 )
00706 {
00707 diff = 0.0;
00708 }
00709 }
00710 else
00711 {
00712 diff -= decay;
00713 if ( diff < 0.0 )
00714 {
00715 diff = 0.0;
00716 }
00717 }
00718 }
00719
00720 error = NPCInfo->lastAimErrorPitch;
00721
00722 ucmd.angles[PITCH] = ANGLE2SHORT( targetPitch + diff + error ) - client->ps.delta_angles[PITCH];
00723 }
00724
00725 ucmd.angles[ROLL] = ANGLE2SHORT ( NPC->client->ps.viewangles[ROLL] ) - client->ps.delta_angles[ROLL];
00726
00727 return exact;
00728
00729 #endif
00730
00731 }
00732
00733
00734
00735
00736
00737
00738
00739
00740 void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw )
00741 {
00742 float error;
00743 float decay;
00744 float targetPitch = 0;
00745 float targetYaw = 0;
00746
00747 if(doPitch)
00748 targetPitch = angles[PITCH];
00749 if(doYaw)
00750 targetYaw = angles[YAW];
00751
00752
00753 if(doYaw)
00754 {
00755
00756 error = AngleDelta ( NPCInfo->shootAngles[YAW], targetYaw );
00757 if ( error )
00758 {
00759 decay = 60.0 + 80.0 * NPCInfo->stats.aim;
00760 decay *= 100.0f / 1000.0f;
00761 if ( error < 0.0 )
00762 {
00763 error += decay;
00764 if ( error > 0.0 )
00765 {
00766 error = 0.0;
00767 }
00768 }
00769 else
00770 {
00771 error -= decay;
00772 if ( error < 0.0 )
00773 {
00774 error = 0.0;
00775 }
00776 }
00777 }
00778 NPCInfo->shootAngles[YAW] = targetYaw + error;
00779 }
00780
00781 if(doPitch)
00782 {
00783
00784 error = AngleDelta ( NPCInfo->shootAngles[PITCH], targetPitch );
00785 if ( error )
00786 {
00787 decay = 60.0 + 80.0 * NPCInfo->stats.aim;
00788 decay *= 100.0f / 1000.0f;
00789 if ( error < 0.0 )
00790 {
00791 error += decay;
00792 if ( error > 0.0 )
00793 {
00794 error = 0.0;
00795 }
00796 }
00797 else
00798 {
00799 error -= decay;
00800 if ( error < 0.0 )
00801 {
00802 error = 0.0;
00803 }
00804 }
00805 }
00806 NPCInfo->shootAngles[PITCH] = targetPitch + error;
00807 }
00808 }
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818 void SetTeamNumbers (void)
00819 {
00820 gentity_t *found;
00821 int i;
00822
00823 for( i = 0; i < TEAM_NUM_TEAMS; i++ )
00824 {
00825 teamNumbers[i] = 0;
00826 teamStrength[i] = 0;
00827 }
00828
00829 for( i = 0; i < 1 ; i++ )
00830 {
00831 found = &g_entities[i];
00832
00833 if( found->client )
00834 {
00835 if( found->health > 0 )
00836 {
00837 teamNumbers[found->client->playerTeam]++;
00838 teamStrength[found->client->playerTeam] += found->health;
00839 }
00840 }
00841 }
00842
00843 for( i = 0; i < TEAM_NUM_TEAMS; i++ )
00844 {
00845 teamStrength[i] = floor( ((float)(teamStrength[i])) / ((float)(teamNumbers[i])) );
00846 }
00847 }
00848
00849 extern stringID_table_t BSTable[];
00850 extern stringID_table_t BSETTable[];
00851 qboolean G_ActivateBehavior (gentity_t *self, int bset )
00852 {
00853 bState_t bSID = (bState_t)-1;
00854 char *bs_name = NULL;
00855
00856 if ( !self )
00857 {
00858 return qfalse;
00859 }
00860
00861 bs_name = self->behaviorSet[bset];
00862
00863 if( !(VALIDSTRING( bs_name )) )
00864 {
00865 return qfalse;
00866 }
00867
00868 if ( self->NPC )
00869 {
00870 bSID = (bState_t)(GetIDForString( BSTable, bs_name ));
00871 }
00872
00873 if(bSID > -1)
00874 {
00875 self->NPC->tempBehavior = BS_DEFAULT;
00876 self->NPC->behaviorState = bSID;
00877 }
00878 else
00879 {
00880
00881
00882
00883
00884
00885
00886
00887 if (0)
00888 {
00889 G_DebugPrint( WL_VERBOSE, "%s attempting to run bSet %s (%s)\n", self->targetname, GetStringForID( BSETTable, bset ), bs_name );
00890 }
00891 trap_ICARUS_RunScript( self, va( "%s/%s", Q3_SCRIPT_DIR, bs_name ) );
00892 }
00893 return qtrue;
00894 }
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906 void NPC_SetBoneAngles(gentity_t *ent, char *bone, vec3_t angles)
00907 {
00908 #ifdef _XBOX
00909 byte *thebone = &ent->s.boneIndex1;
00910 byte *firstFree = NULL;
00911 #else
00912 int *thebone = &ent->s.boneIndex1;
00913 int *firstFree = NULL;
00914 #endif
00915 int i = 0;
00916 int boneIndex = G_BoneIndex(bone);
00917 int flags, up, right, forward;
00918 vec3_t *boneVector = &ent->s.boneAngles1;
00919 vec3_t *freeBoneVec = NULL;
00920
00921 while (thebone)
00922 {
00923 if (!*thebone && !firstFree)
00924 {
00925 firstFree = thebone;
00926 freeBoneVec = boneVector;
00927 }
00928 else if (*thebone)
00929 {
00930 if (*thebone == boneIndex)
00931 {
00932 break;
00933 }
00934 }
00935
00936 switch (i)
00937 {
00938 case 0:
00939 thebone = &ent->s.boneIndex2;
00940 boneVector = &ent->s.boneAngles2;
00941 break;
00942 case 1:
00943 thebone = &ent->s.boneIndex3;
00944 boneVector = &ent->s.boneAngles3;
00945 break;
00946 case 2:
00947 thebone = &ent->s.boneIndex4;
00948 boneVector = &ent->s.boneAngles4;
00949 break;
00950 default:
00951 thebone = NULL;
00952 boneVector = NULL;
00953 break;
00954 }
00955
00956 i++;
00957 }
00958
00959 if (!thebone)
00960 {
00961 if (!firstFree)
00962 {
00963 Com_Printf("WARNING: NPC has no free bone indexes\n");
00964 return;
00965 }
00966
00967 thebone = firstFree;
00968
00969 *thebone = boneIndex;
00970 boneVector = freeBoneVec;
00971 }
00972
00973
00974
00975
00976
00977 VectorCopy(angles, *boneVector);
00978
00979
00980
00981 if (!ent->ghoul2)
00982 {
00983 return;
00984 }
00985
00986 flags = BONE_ANGLES_POSTMULT;
00987 up = POSITIVE_X;
00988 right = NEGATIVE_Y;
00989 forward = NEGATIVE_Z;
00990
00991
00992 ent->s.boneOrient = ((forward)|(right<<3)|(up<<6));
00993
00994 trap_G2API_SetBoneAngles(ent->ghoul2, 0, bone, angles, flags, up, right, forward, NULL, 100, level.time);
00995 }
00996
00997
00998 #define TURN_ON 0x00000000
00999 #define TURN_OFF 0x00000100
01000
01001 void NPC_SetSurfaceOnOff(gentity_t *ent, const char *surfaceName, int surfaceFlags)
01002 {
01003 int i = 0;
01004 qboolean foundIt = qfalse;
01005
01006 while (i < BG_NUM_TOGGLEABLE_SURFACES && bgToggleableSurfaces[i])
01007 {
01008 if (!Q_stricmp(surfaceName, bgToggleableSurfaces[i]))
01009 {
01010 foundIt = qtrue;
01011 break;
01012 }
01013 i++;
01014 }
01015
01016 if (!foundIt)
01017 {
01018 Com_Printf("WARNING: Tried to toggle NPC surface that isn't in toggleable surface list (%s)\n", surfaceName);
01019 return;
01020 }
01021
01022 if (surfaceFlags == TURN_ON)
01023 {
01024 ent->s.surfacesOn |= (1 << i);
01025 ent->s.surfacesOff &= ~(1 << i);
01026 }
01027 else
01028 {
01029 ent->s.surfacesOn &= ~(1 << i);
01030 ent->s.surfacesOff |= (1 << i);
01031 }
01032
01033 if (!ent->ghoul2)
01034 {
01035 return;
01036 }
01037
01038 trap_G2API_SetSurfaceOnOff(ent->ghoul2, surfaceName, surfaceFlags);
01039 }
01040
01041
01042 qboolean NPC_SomeoneLookingAtMe(gentity_t *ent)
01043 {
01044 int i = 0;
01045 gentity_t *pEnt;
01046
01047 while (i < MAX_CLIENTS)
01048 {
01049 pEnt = &g_entities[i];
01050
01051 if (pEnt && pEnt->inuse && pEnt->client && pEnt->client->sess.sessionTeam != TEAM_SPECTATOR &&
01052 !(pEnt->client->ps.pm_flags & PMF_FOLLOW) && pEnt->s.weapon != WP_NONE)
01053 {
01054 if (trap_InPVS(ent->r.currentOrigin, pEnt->r.currentOrigin))
01055 {
01056 if (InFOV( ent, pEnt, 30, 30 ))
01057 {
01058 return qtrue;
01059 }
01060 }
01061 }
01062
01063 i++;
01064 }
01065
01066 return qfalse;
01067 }
01068
01069 qboolean NPC_ClearLOS( const vec3_t start, const vec3_t end )
01070 {
01071 return G_ClearLOS( NPC, start, end );
01072 }
01073 qboolean NPC_ClearLOS5( const vec3_t end )
01074 {
01075 return G_ClearLOS5( NPC, end );
01076 }
01077 qboolean NPC_ClearLOS4( gentity_t *ent )
01078 {
01079 return G_ClearLOS4( NPC, ent );
01080 }
01081 qboolean NPC_ClearLOS3( const vec3_t start, gentity_t *ent )
01082 {
01083 return G_ClearLOS3( NPC, start, ent );
01084 }
01085 qboolean NPC_ClearLOS2( gentity_t *ent, const vec3_t end )
01086 {
01087 return G_ClearLOS2( NPC, ent, end );
01088 }
01089
01090
01091
01092
01093
01094
01095
01096 qboolean NPC_ValidEnemy( gentity_t *ent )
01097 {
01098 int entTeam = TEAM_FREE;
01099
01100 if ( ent == NULL )
01101 return qfalse;
01102
01103
01104 if ( ent == NPC )
01105 return qfalse;
01106
01107
01108 if ( ent->inuse == qfalse )
01109 return qfalse;
01110
01111
01112 if ( ent->health <= 0 )
01113 return qfalse;
01114
01115
01116 if ( ent->flags & FL_NOTARGET )
01117 return qfalse;
01118
01119
01120 if ( ent->client == NULL )
01121 {
01122
01123 if (ent->s.eType != ET_NPC)
01124 {
01125 if ( ent->alliedTeam == NPC->client->playerTeam )
01126 {
01127 return qfalse;
01128 }
01129 else
01130 {
01131 return qtrue;
01132 }
01133 }
01134 else
01135 {
01136 return qfalse;
01137 }
01138 }
01139 else if ( ent->client && ent->client->sess.sessionTeam == TEAM_SPECTATOR )
01140 {
01141 return qfalse;
01142 }
01143 if ( ent->NPC && ent->client )
01144 {
01145 entTeam = ent->client->playerTeam;
01146 }
01147 else if ( ent->client )
01148 {
01149 if (g_gametype.integer < GT_TEAM)
01150 {
01151 entTeam = NPCTEAM_PLAYER;
01152 }
01153 else
01154 {
01155 if ( ent->client->sess.sessionTeam == TEAM_BLUE )
01156 {
01157 entTeam = NPCTEAM_PLAYER;
01158 }
01159 else if ( ent->client->sess.sessionTeam == TEAM_RED )
01160 {
01161 entTeam = NPCTEAM_ENEMY;
01162 }
01163 else
01164 {
01165 entTeam = NPCTEAM_NEUTRAL;
01166 }
01167 }
01168 }
01169
01170 if ( ent->client->playerTeam == NPC->client->playerTeam )
01171 return qfalse;
01172
01173
01174
01175
01176 if ( entTeam == NPC->client->enemyTeam
01177 || (NPC->client->enemyTeam == NPCTEAM_FREE && ent->client->NPC_class != NPC->client->NPC_class )
01178 || (ent->client->NPC_class == CLASS_WAMPA && ent->enemy )
01179 || (ent->client->NPC_class == CLASS_RANCOR && ent->enemy )
01180 || (entTeam == NPCTEAM_FREE && ent->client->enemyTeam == NPCTEAM_FREE && ent->enemy && ent->enemy->client && (ent->enemy->client->playerTeam == NPC->client->playerTeam||(ent->enemy->client->playerTeam != NPCTEAM_ENEMY&&NPC->client->playerTeam==NPCTEAM_PLAYER)))
01181 )
01182 {
01183 return qtrue;
01184 }
01185
01186 return qfalse;
01187 }
01188
01189
01190
01191
01192
01193
01194
01195 qboolean NPC_TargetVisible( gentity_t *ent )
01196 {
01197
01198 if ( DistanceSquared( ent->r.currentOrigin, NPC->r.currentOrigin ) > ( NPCInfo->stats.visrange * NPCInfo->stats.visrange ) )
01199 return qfalse;
01200
01201
01202 if ( InFOV( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse )
01203 return qfalse;
01204
01205
01206 if ( NPC_ClearLOS4( ent ) == qfalse )
01207 return qfalse;
01208
01209 return qtrue;
01210 }
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243 #define MAX_RADIUS_ENTS 256 //NOTE: This can cause entities to be lost
01244 #define NEAR_DEFAULT_RADIUS 256
01245
01246 int NPC_FindNearestEnemy( gentity_t *ent )
01247 {
01248 int iradiusEnts[ MAX_RADIUS_ENTS ];
01249 gentity_t *radEnt;
01250 vec3_t mins, maxs;
01251 int nearestEntID = -1;
01252 float nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
01253 float distance;
01254 int numEnts, numChecks = 0;
01255 int i;
01256
01257
01258 for ( i = 0; i < 3; i++ )
01259 {
01260 mins[i] = ent->r.currentOrigin[i] - NPCInfo->stats.visrange;
01261 maxs[i] = ent->r.currentOrigin[i] + NPCInfo->stats.visrange;
01262 }
01263
01264
01265 numEnts = trap_EntitiesInBox( mins, maxs, iradiusEnts, MAX_RADIUS_ENTS );
01266
01267 for ( i = 0; i < numEnts; i++ )
01268 {
01269 radEnt = &g_entities[iradiusEnts[i]];
01270
01271 if ( radEnt == ent )
01272 continue;
01273
01274
01275 if ( NPC_ValidEnemy( radEnt ) == qfalse )
01276 continue;
01277
01278 numChecks++;
01279
01280 if ( NPC_TargetVisible( radEnt ) == qfalse )
01281 continue;
01282
01283 distance = DistanceSquared( ent->r.currentOrigin, radEnt->r.currentOrigin );
01284
01285
01286 if ( distance < nearestDist )
01287 {
01288 nearestEntID = radEnt->s.number;
01289 nearestDist = distance;
01290 }
01291 }
01292
01293 return nearestEntID;
01294 }
01295
01296
01297
01298
01299
01300
01301
01302 gentity_t *NPC_PickEnemyExt( qboolean checkAlerts )
01303 {
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314
01315 int entID = NPC_FindNearestEnemy( NPC );
01316
01317
01318 if ( entID >= 0 )
01319 return &g_entities[entID];
01320
01321 if ( checkAlerts )
01322 {
01323 int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
01324
01325
01326 if ( alertEvent >= 0 )
01327 {
01328 alertEvent_t *event = &level.alertEvents[alertEvent];
01329
01330
01331 if ( event->owner == NPC )
01332 return NULL;
01333
01334 if ( event->level >= AEL_DISCOVERED )
01335 {
01336
01337 if ( event->owner == &g_entities[0] )
01338 return event->owner;
01339
01340
01341 if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPC->client->playerTeam ) )
01342 return event->owner->enemy;
01343 }
01344 }
01345 }
01346
01347 return NULL;
01348 }
01349
01350
01351
01352
01353
01354
01355
01356 qboolean NPC_FindPlayer( void )
01357 {
01358 return NPC_TargetVisible( &g_entities[0] );
01359 }
01360
01361
01362
01363
01364
01365
01366
01367 static qboolean NPC_CheckPlayerDistance( void )
01368 {
01369 return qfalse;
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391