codemp/game/NPC_spawn.c

Go to the documentation of this file.
00001 //b_spawn.cpp
00002 //added by MCG
00003 #include "b_local.h"
00004 #include "anims.h"
00005 #include "w_saber.h"
00006 #include "bg_saga.h"
00007 #include "bg_vehicles.h"
00008 #include "g_nav.h"
00009 
00010 extern void G_DebugPrint( int level, const char *format, ... );
00011 
00012 extern qboolean G_CheckInSolid (gentity_t *self, qboolean fix);
00013 extern void ClientUserinfoChanged( int clientNum );
00014 extern qboolean SpotWouldTelefrag2( gentity_t *mover, vec3_t dest );
00015 extern void Jedi_Cloak( gentity_t *self );
00016 
00017 //extern void FX_BorgTeleport( vec3_t org );
00018 
00019 extern void Q3_SetParm (int entID, int parmNum, const char *parmValue);
00020 extern team_t TranslateTeamName( const char *name );
00021 extern char     *TeamNames[TEAM_NUM_TEAMS];
00022 
00023 //extern void CG_ShimmeryThing_Spawner( vec3_t start, vec3_t end, float radius, qboolean taper, int duration );
00024 
00025 //extern void NPC_StasisSpawnEffect( gentity_t *ent );
00026 
00027 extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
00028 extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
00029 
00030 extern void ST_ClearTimers( gentity_t *ent );
00031 extern void Jedi_ClearTimers( gentity_t *ent );
00032 extern void NPC_ShadowTrooper_Precache( void );
00033 extern void NPC_Gonk_Precache( void );
00034 extern void NPC_Mouse_Precache( void );
00035 extern void NPC_Seeker_Precache( void );
00036 extern void NPC_Remote_Precache( void );
00037 extern void     NPC_R2D2_Precache(void);
00038 extern void     NPC_R5D2_Precache(void);
00039 extern void NPC_Probe_Precache(void);
00040 extern void NPC_Interrogator_Precache(gentity_t *self);
00041 extern void NPC_MineMonster_Precache( void );
00042 extern void NPC_Howler_Precache( void );
00043 extern void NPC_ATST_Precache(void);
00044 extern void NPC_Sentry_Precache(void);
00045 extern void NPC_Mark1_Precache(void);
00046 extern void NPC_Mark2_Precache(void);
00047 extern void NPC_GalakMech_Precache( void );
00048 extern void NPC_GalakMech_Init( gentity_t *ent );
00049 extern void NPC_Protocol_Precache( void );
00050 extern void Boba_Precache( void );
00051 extern void NPC_Wampa_Precache( void );
00052 gentity_t *NPC_SpawnType( gentity_t *ent, char *npc_type, char *targetname, qboolean isVehicle );
00053 
00054 extern void Rancor_SetBolts( gentity_t *self );
00055 extern void Wampa_SetBolts( gentity_t *self );
00056 
00057 #define NSF_DROP_TO_FLOOR       16
00058 
00059 // PAIN functions...
00060 //
00061 extern void funcBBrushPain                              (gentity_t *self, gentity_t *attacker, int damage);
00062 extern void misc_model_breakable_pain   (gentity_t *self, gentity_t *attacker, int damage);
00063 extern void NPC_Pain                                    (gentity_t *self, gentity_t *attacker, int damage);
00064 extern void station_pain                                (gentity_t *self, gentity_t *attacker, int damage);
00065 extern void func_usable_pain                    (gentity_t *self, gentity_t *attacker, int damage);
00066 extern void NPC_ATST_Pain                               (gentity_t *self, gentity_t *attacker, int damage);
00067 extern void NPC_ST_Pain                                 (gentity_t *self, gentity_t *attacker, int damage);
00068 extern void NPC_Jedi_Pain                               (gentity_t *self, gentity_t *attacker, int damage);
00069 extern void NPC_Droid_Pain                              (gentity_t *self, gentity_t *attacker, int damage);
00070 extern void NPC_Probe_Pain                              (gentity_t *self, gentity_t *attacker, int damage);
00071 extern void NPC_MineMonster_Pain                (gentity_t *self, gentity_t *attacker, int damage);
00072 extern void NPC_Howler_Pain                             (gentity_t *self, gentity_t *attacker, int damage);
00073 extern void NPC_Seeker_Pain                             (gentity_t *self, gentity_t *attacker, int damage);
00074 extern void NPC_Remote_Pain                             (gentity_t *self, gentity_t *attacker, int damage);
00075 extern void emplaced_gun_pain                   (gentity_t *self, gentity_t *attacker, int damage);
00076 extern void NPC_Mark1_Pain                              (gentity_t *self, gentity_t *attacker, int damage);
00077 extern void NPC_GM_Pain                         (gentity_t *self, gentity_t *attacker, int damage);
00078 extern void NPC_Sentry_Pain                             (gentity_t *self, gentity_t *attacker, int damage);
00079 extern void NPC_Mark2_Pain                              (gentity_t *self, gentity_t *attacker, int damage);
00080 extern void PlayerPain                                  (gentity_t *self, gentity_t *attacker, int damage);
00081 extern void GasBurst                                    (gentity_t *self, gentity_t *attacker, int damage);
00082 extern void CrystalCratePain                    (gentity_t *self, gentity_t *attacker, int damage);
00083 extern void TurretPain                                  (gentity_t *self, gentity_t *attacker, int damage);
00084 extern void NPC_Wampa_Pain                              (gentity_t *self, gentity_t *attacker, int damage);
00085 extern void NPC_Rancor_Pain                             (gentity_t *self, gentity_t *attacker, int damage);
00086 
00087 
00088 //void HirogenAlpha_Precache( void );
00089 
00090 int WP_SetSaberModel( gclient_t *client, class_t npcClass )
00091 {
00092         //rwwFIXMEFIXME: Do something here, need to let the client know.
00093         return 1;
00094 }
00095 
00096 /*
00097 -------------------------
00098 NPC_PainFunc
00099 -------------------------
00100 */
00101 typedef void (PAIN_FUNC) (gentity_t *self, gentity_t *attacker, int damage);
00102 
00103 PAIN_FUNC *NPC_PainFunc( gentity_t *ent )
00104 {
00105         void (*func)(gentity_t *self, gentity_t *attacker, int damage);
00106 
00107         if ( ent->client->ps.weapon == WP_SABER )
00108         {
00109                 func = NPC_Jedi_Pain;
00110         }
00111         else
00112         {
00113                 // team no longer indicates race/species, use NPC_class to determine different npc types
00114                 /*
00115                 switch ( ent->client->playerTeam )
00116                 {
00117                 default:
00118                         func = painF_NPC_Pain;
00119                         break;
00120                 }
00121                 */
00122                 switch( ent->client->NPC_class )
00123                 {
00124                 // troopers get special pain
00125                 case CLASS_STORMTROOPER:
00126                 case CLASS_SWAMPTROOPER:
00127                         func = NPC_ST_Pain;
00128                         break;
00129 
00130                 case CLASS_SEEKER:
00131                         func = NPC_Seeker_Pain;
00132                         break;
00133 
00134                 case CLASS_REMOTE:
00135                         func = NPC_Remote_Pain;
00136                         break;
00137 
00138                 case CLASS_MINEMONSTER:
00139                         func = NPC_MineMonster_Pain;
00140                         break;
00141 
00142                 case CLASS_HOWLER:
00143                         func = NPC_Howler_Pain;
00144                         break;
00145 
00146                 // all other droids, did I miss any?
00147                 case CLASS_GONK:
00148                 case CLASS_R2D2:
00149                 case CLASS_R5D2:
00150                 case CLASS_MOUSE:
00151                 case CLASS_PROTOCOL:
00152                 case CLASS_INTERROGATOR:
00153                         func = NPC_Droid_Pain;
00154                         break;
00155                 case CLASS_PROBE:
00156                         func = NPC_Probe_Pain;
00157                         break;
00158 
00159                 case CLASS_SENTRY:
00160                         func = NPC_Sentry_Pain;
00161                         break;
00162                 case CLASS_MARK1:
00163                         func = NPC_Mark1_Pain;
00164                         break;
00165                 case CLASS_MARK2:
00166                         func = NPC_Mark2_Pain;
00167                         break;
00168                 case CLASS_ATST:  
00169                         func = NPC_ATST_Pain;
00170                         break;
00171                 case CLASS_GALAKMECH:
00172                         func = NPC_GM_Pain;
00173                         break;
00174                 case CLASS_RANCOR:
00175                         func = NPC_Rancor_Pain;
00176                         break;
00177                 case CLASS_WAMPA:
00178                         func = NPC_Wampa_Pain;
00179                         break;
00180                 // everyone else gets the normal pain func
00181                 default:
00182                         func = NPC_Pain;
00183                         break;
00184                 }
00185 
00186         }
00187 
00188         return func;
00189 }
00190 
00191 
00192 /*
00193 -------------------------
00194 NPC_TouchFunc
00195 -------------------------
00196 */
00197 typedef void (TOUCH_FUNC) (gentity_t *self, gentity_t *other, trace_t *trace);
00198 
00199 TOUCH_FUNC *NPC_TouchFunc( gentity_t *ent )
00200 {
00201         void (*func)(gentity_t *self, gentity_t *other, trace_t *trace);
00202 
00203         func = NPC_Touch;
00204 
00205         return func;
00206 }
00207 
00208 /*
00209 -------------------------
00210 NPC_SetMiscDefaultData
00211 -------------------------
00212 */
00213 
00214 extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
00215 void NPC_SetMiscDefaultData( gentity_t *ent )
00216 {
00217         if ( ent->spawnflags & SFB_CINEMATIC )
00218         {//if a cinematic guy, default us to wait bState
00219                 ent->NPC->behaviorState = BS_CINEMATIC;
00220         }
00221         if ( ent->client->NPC_class == CLASS_BOBAFETT )
00222         {//set some stuff, precache
00223                 Boba_Precache();
00224                 ent->client->ps.fd.forcePowersKnown |= ( 1 << FP_LEVITATION );
00225                 ent->client->ps.fd.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_3;
00226                 ent->client->ps.fd.forcePower = 100;
00227                 ent->NPC->scriptFlags |= (SCF_ALT_FIRE|SCF_NO_GROUPS);
00228         }
00229         //if ( !Q_stricmp( "atst_vehicle", ent->NPC_type ) )//FIXME: has to be a better, easier way to tell this, no?
00230         if (ent->s.NPC_class == CLASS_VEHICLE && ent->m_pVehicle)
00231         {
00232                 ent->s.g2radius = 255;//MAX for this value, was (ent->r.maxs[2]-ent->r.mins[2]), which is 272 or something
00233 
00234                 if (ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER)
00235                 {
00236                         ent->mass = 2000;//???
00237                         ent->flags |= (FL_SHIELDED|FL_NO_KNOCKBACK);
00238                         ent->pain = NPC_ATST_Pain;
00239                 }
00240                 //turn the damn hatch cover on and LEAVE it on
00241                 trap_G2API_SetSurfaceOnOff( ent->ghoul2, "head_hatchcover", 0/*TURN_ON*/ );
00242         }
00243         if ( !Q_stricmp( "wampa", ent->NPC_type ) )
00244         {//FIXME: extern this into NPC.cfg?
00245                 Wampa_SetBolts( ent );
00246                 ent->s.g2radius = 80;//???
00247                 ent->mass = 300;//???
00248                 ent->flags |= FL_NO_KNOCKBACK;
00249                 ent->pain = NPC_Wampa_Pain;
00250         }
00251         if ( ent->client->NPC_class == CLASS_RANCOR )
00252         {
00253                 Rancor_SetBolts( ent );
00254                 ent->s.g2radius = 255;//MAX for this value, was (ent->r.maxs[2]-ent->r.mins[2]), which is 272 or something
00255                 ent->mass = 1000;//???
00256                 ent->flags |= FL_NO_KNOCKBACK;
00257                 ent->pain = NPC_Rancor_Pain;
00258                 ent->health *= 4;
00259         }
00260         if ( !Q_stricmp( "Yoda", ent->NPC_type ) )
00261         {//FIXME: extern this into NPC.cfg?
00262                 ent->NPC->scriptFlags |= SCF_NO_FORCE;//force powers don't work on him
00263         }
00264         if ( !Q_stricmp( "emperor", ent->NPC_type ) )
00265         {//FIXME: extern this into NPC.cfg?
00266                 ent->NPC->scriptFlags |= SCF_DONT_FIRE;//so he uses only force powers
00267         }
00268         //==================
00269 //      if ( ent->client->ps.saber[0].type != SABER_NONE )
00270         if (ent->client->ps.weapon == WP_SABER) //rwwFIXMEFIXME: is this going to work?
00271         {//if I'm equipped with a saber, initialize it (them)
00272         //      ent->client->ps.SaberDeactivate();
00273         //      ent->client->ps.SetSaberLength( 0 );
00274                 WP_SaberInitBladeData( ent );
00275                 ent->client->ps.saberHolstered = 2;
00276         //      G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saber[0].model, ent->handRBolt, 0 );
00277         //      if ( ent->client->ps.dualSabers )
00278         //      {
00279         //              G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saber[1].model, ent->handLBolt, 1 );
00280         //      }
00281                 Jedi_ClearTimers( ent );
00282         }
00283         if ( ent->client->ps.fd.forcePowersKnown != 0 )
00284         {
00285                 WP_InitForcePowers( ent );
00286                 WP_SpawnInitForcePowers(ent); //rww
00287         }
00288         if ( ent->client->NPC_class == CLASS_SEEKER )
00289         {               
00290                 ent->NPC->defaultBehavior = BS_DEFAULT;
00291                 ent->client->ps.gravity = 0;
00292                 ent->NPC->aiFlags |= NPCAI_CUSTOM_GRAVITY;
00293                 ent->client->ps.eFlags2 |= EF2_FLYING;
00294                 ent->count = 30; // SEEKER shot ammo count
00295         }
00296         //***I'm not sure whether I should leave this as a TEAM_ switch, I think NPC_class may be more appropriate - dmv
00297         switch(ent->client->playerTeam)
00298         {
00299         case NPCTEAM_PLAYER:
00300                 //ent->flags |= FL_NO_KNOCKBACK;
00301                 if ( ent->client->NPC_class == CLASS_JEDI || ent->client->NPC_class == CLASS_LUKE )
00302                 {//good jedi
00303                         ent->client->enemyTeam = NPCTEAM_ENEMY;
00304                         if ( ent->spawnflags & JSF_AMBUSH )
00305                         {//ambusher
00306                                 ent->NPC->scriptFlags |= SCF_IGNORE_ALERTS;
00307                                 ent->client->noclip = qtrue;//hang
00308                         }
00309                 }
00310                 else
00311                 {
00312                         /*
00313                         if (ent->client->ps.weapon != WP_NONE)
00314                         {
00315                                 G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
00316                         }
00317                         */
00318                         switch ( ent->client->ps.weapon )
00319                         {
00320                         case WP_BRYAR_PISTOL://FIXME: new weapon: imp blaster pistol
00321         //              case WP_BLASTER_PISTOL:
00322                         case WP_DISRUPTOR:
00323                         case WP_BOWCASTER:
00324                         case WP_REPEATER:
00325                         case WP_DEMP2:
00326                         case WP_FLECHETTE:
00327                         case WP_ROCKET_LAUNCHER:
00328                         default:
00329                                 break;
00330                         case WP_THERMAL:
00331                         case WP_BLASTER:
00332                                 //FIXME: health in NPCs.cfg, and not all blaster users are stormtroopers
00333                                 //ent->health = 25;
00334                                 //FIXME: not necc. a ST
00335                                 ST_ClearTimers( ent );
00336                                 if ( ent->NPC->rank >= RANK_LT || ent->client->ps.weapon == WP_THERMAL )
00337                                 {//officers, grenade-throwers use alt-fire
00338                                         //ent->health = 50;
00339                                         //ent->NPC->scriptFlags |= SCF_ALT_FIRE;
00340                                 }
00341                                 break;
00342                         }
00343                 }
00344                 if ( ent->client->NPC_class == CLASS_KYLE || ent->client->NPC_class == CLASS_VEHICLE || (ent->spawnflags & SFB_CINEMATIC) )
00345                 {
00346                         ent->NPC->defaultBehavior = BS_CINEMATIC;
00347                 }
00348                 else
00349                 {
00350                         /*
00351                         ent->NPC->defaultBehavior = BS_FOLLOW_LEADER;
00352                         ent->client->leader = &g_entities[0];
00353                         */
00354                 }
00355                 break;
00356 
00357         case NPCTEAM_NEUTRAL: 
00358 
00359                 if ( Q_stricmp( ent->NPC_type, "gonk" ) == 0 ) 
00360                 {
00361                         // I guess we generically make them player usable
00362                         ent->r.svFlags |= SVF_PLAYER_USABLE;
00363 
00364                         // Not even sure if we want to give different levels of batteries?  ...Or even that these are the values we'd want to use.
00365                         /*
00366                         switch ( g_spskill.integer )
00367                         {
00368                         case 0: //      EASY
00369                                 ent->client->ps.batteryCharge = MAX_BATTERIES * 0.8f; 
00370                                 break;
00371                         case 1: //      MEDIUM
00372                                 ent->client->ps.batteryCharge = MAX_BATTERIES * 0.75f; 
00373                                 break;
00374                         default :
00375                         case 2: //      HARD
00376                                 ent->client->ps.batteryCharge = MAX_BATTERIES * 0.5f; 
00377                                 break;
00378                         }*/
00379                         //rwwFIXMEFIXME: Make use of this.
00380                 }
00381                 break;
00382 
00383         case NPCTEAM_ENEMY:
00384                 {
00385                         ent->NPC->defaultBehavior = BS_DEFAULT;
00386                         if ( ent->client->NPC_class == CLASS_SHADOWTROOPER )
00387                         {//FIXME: a spawnflag?
00388                                 Jedi_Cloak( ent );
00389                         }
00390                         if( ent->client->NPC_class == CLASS_TAVION ||
00391                                 ent->client->NPC_class == CLASS_REBORN ||
00392                                 ent->client->NPC_class == CLASS_DESANN ||
00393                                 ent->client->NPC_class == CLASS_SHADOWTROOPER )
00394                         {
00395                                 ent->client->enemyTeam = NPCTEAM_PLAYER;
00396                                 if ( ent->spawnflags & JSF_AMBUSH )
00397                                 {//ambusher
00398                                         ent->NPC->scriptFlags |= SCF_IGNORE_ALERTS;
00399                                         ent->client->noclip = qtrue;//hang
00400                                 }
00401                         }
00402                         else if( ent->client->NPC_class == CLASS_PROBE || ent->client->NPC_class == CLASS_REMOTE ||
00403                                                 ent->client->NPC_class == CLASS_INTERROGATOR || ent->client->NPC_class == CLASS_SENTRY)
00404                         {               
00405                                 ent->NPC->defaultBehavior = BS_DEFAULT;
00406                                 ent->client->ps.gravity = 0;
00407                                 ent->NPC->aiFlags |= NPCAI_CUSTOM_GRAVITY;
00408                                 ent->client->ps.eFlags2 |= EF2_FLYING;
00409                         }
00410                         else 
00411                         {
00412                 //              G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
00413                                 switch ( ent->client->ps.weapon )
00414                                 {
00415                                 case WP_BRYAR_PISTOL:
00416                                         break;
00417                 //              case WP_BLASTER_PISTOL:
00418                 //                      break;
00419                                 case WP_DISRUPTOR:
00420                                         //Sniper
00421                                         //ent->NPC->scriptFlags |= SCF_ALT_FIRE;//FIXME: use primary fire sometimes?  Up close?  Different class of NPC?
00422                                         break;
00423                                 case WP_BOWCASTER:
00424                                         break;
00425                                 case WP_REPEATER:
00426                                         //machine-gunner
00427                                         break;
00428                                 case WP_DEMP2:
00429                                         break;
00430                                 case WP_FLECHETTE:
00431                                         //shotgunner
00432                                         if ( !Q_stricmp( "stofficeralt", ent->NPC_type ) )
00433                                         {
00434                                                 //ent->NPC->scriptFlags |= SCF_ALT_FIRE;
00435                                         }
00436                                         break;
00437                                 case WP_ROCKET_LAUNCHER:
00438                                         break;
00439                                 case WP_THERMAL:
00440                                         //Gran, use main, bouncy fire
00441 //                                      ent->NPC->scriptFlags |= SCF_ALT_FIRE;
00442                                         break;
00443                                 case WP_STUN_BATON:
00444                                         break;
00445                                 default:
00446                                 case WP_BLASTER:
00447                                         //FIXME: health in NPCs.cfg, and not all blaster users are stormtroopers
00448                                         //FIXME: not necc. a ST
00449                                         ST_ClearTimers( ent );
00450                                         if ( ent->NPC->rank >= RANK_COMMANDER )
00451                                         {//commanders use alt-fire
00452                                                 //ent->NPC->scriptFlags |= SCF_ALT_FIRE;
00453                                         }
00454                                         if ( !Q_stricmp( "rodian2", ent->NPC_type ) )
00455                                         {
00456                                                 //ent->NPC->scriptFlags |= SCF_ALT_FIRE;
00457                                         }
00458                                         break;
00459                                 }
00460                                 if ( !Q_stricmp( "galak_mech", ent->NPC_type ) )
00461                                 {//starts with armor
00462                                         NPC_GalakMech_Init( ent );
00463                                 }
00464                         }
00465                 }
00466                 break;
00467 
00468         default:
00469                 break;
00470         }
00471 
00472 
00473         if ( ent->client->NPC_class == CLASS_SEEKER
00474                 && ent->activator )
00475         {//assume my teams are already set correctly
00476         }
00477         else
00478         {
00479                 //for siege, want "bad" npc's allied with the "bad" team
00480                 if (g_gametype.integer == GT_SIEGE && ent->s.NPC_class != CLASS_VEHICLE)
00481                 {
00482                         if (ent->client->enemyTeam == NPCTEAM_PLAYER)
00483                         {
00484                                 ent->client->sess.sessionTeam = SIEGETEAM_TEAM1;
00485                         }
00486                         else if (ent->client->enemyTeam == NPCTEAM_ENEMY)
00487                         {
00488                                 ent->client->sess.sessionTeam = SIEGETEAM_TEAM2;
00489                         }
00490                         else
00491                         {
00492                                 ent->client->sess.sessionTeam = TEAM_FREE;
00493                         }
00494                 }
00495         }
00496 
00497         if ( ent->client->NPC_class == CLASS_ATST || ent->client->NPC_class == CLASS_MARK1 ) // chris/steve/kevin requested that the mark1 be shielded also
00498         {
00499                 ent->flags |= (FL_SHIELDED|FL_NO_KNOCKBACK);
00500         }
00501 }
00502 
00503 /*
00504 -------------------------
00505 NPC_WeaponsForTeam
00506 -------------------------
00507 */
00508 
00509 int NPC_WeaponsForTeam( team_t team, int spawnflags, const char *NPC_type )
00510 {
00511         //*** not sure how to handle this, should I pass in class instead of team and go from there? - dmv
00512         switch(team)
00513         {
00514         // no longer exists
00515 //      case TEAM_BORG:
00516 //              break;
00517 
00518 //      case TEAM_HIROGEN:
00519 //              if( Q_stricmp( "hirogenalpha", NPC_type ) == 0 )
00520 //                      return ( 1 << WP_BLASTER);
00521                 //Falls through
00522 
00523 //      case TEAM_KLINGON:
00524 
00525                 //NOTENOTE: Falls through
00526 
00527 //      case TEAM_IMPERIAL:
00528         case NPCTEAM_ENEMY:
00529                 if ( Q_stricmp( "tavion", NPC_type ) == 0 || 
00530                         Q_strncmp( "reborn", NPC_type, 6 ) == 0 || 
00531                         Q_stricmp( "desann", NPC_type ) == 0 || 
00532                         Q_strncmp( "shadowtrooper", NPC_type, 13 ) == 0 )
00533                         return ( 1 << WP_SABER);
00534 //                      return ( 1 << WP_IMPERIAL_BLADE);
00535                 //NOTENOTE: Falls through if not a knife user
00536 
00537 //      case TEAM_SCAVENGERS:
00538 //      case TEAM_MALON:
00539                 //FIXME: default weapon in npc config?
00540                 if ( Q_strncmp( "stofficer", NPC_type, 9 ) == 0 )
00541                 {
00542                         return ( 1 << WP_FLECHETTE);
00543                 }
00544                 if ( Q_stricmp( "stcommander", NPC_type ) == 0 )
00545                 {
00546                         return ( 1 << WP_REPEATER);
00547                 }
00548                 if ( Q_stricmp( "swamptrooper", NPC_type ) == 0 )
00549                 {
00550                         return ( 1 << WP_FLECHETTE);
00551                 }
00552                 if ( Q_stricmp( "swamptrooper2", NPC_type ) == 0 )
00553                 {
00554                         return ( 1 << WP_REPEATER);
00555                 }
00556                 if ( Q_stricmp( "rockettrooper", NPC_type ) == 0 )
00557                 {
00558                         return ( 1 << WP_ROCKET_LAUNCHER);
00559                 }
00560                 if ( Q_strncmp( "shadowtrooper", NPC_type, 13 ) == 0 )
00561                 {
00562                         return ( 1 << WP_SABER);//|( 1 << WP_RAPID_CONCUSSION)?
00563                 }
00564                 if ( Q_stricmp( "imperial", NPC_type ) == 0 )
00565                 {
00566                         //return ( 1 << WP_BLASTER_PISTOL);
00567                         return ( 1 << WP_BLASTER);
00568                 }
00569                 if ( Q_strncmp( "impworker", NPC_type, 9 ) == 0 )
00570                 {
00571                         //return ( 1 << WP_BLASTER_PISTOL);
00572                         return ( 1 << WP_BLASTER);
00573                 }
00574                 if ( Q_stricmp( "stormpilot", NPC_type ) == 0 )
00575                 {
00576                         //return ( 1 << WP_BLASTER_PISTOL);
00577                         return ( 1 << WP_BLASTER);
00578                 }
00579                 if ( Q_stricmp( "galak", NPC_type ) == 0 )
00580                 {
00581                         return ( 1 << WP_BLASTER);
00582                 }
00583                 if ( Q_stricmp( "galak_mech", NPC_type ) == 0 )
00584                 {
00585                         return ( 1 << WP_REPEATER);
00586                 }
00587                 if ( Q_strncmp( "ugnaught", NPC_type, 8 ) == 0 )
00588                 {
00589                         return WP_NONE;
00590                 }
00591                 if ( Q_stricmp( "granshooter", NPC_type ) == 0 )
00592                 {
00593                         return ( 1 << WP_BLASTER);
00594                 }
00595                 if ( Q_stricmp( "granboxer", NPC_type ) == 0 )
00596                 {
00597                         return ( 1 << WP_STUN_BATON);
00598                 }
00599                 if ( Q_strncmp( "gran", NPC_type, 4 ) == 0 )
00600                 {
00601                         return (( 1 << WP_THERMAL)|( 1 << WP_STUN_BATON));
00602                 }
00603                 if ( Q_stricmp( "rodian", NPC_type ) == 0 )
00604                 {
00605                         return ( 1 << WP_DISRUPTOR);
00606                 }
00607                 if ( Q_stricmp( "rodian2", NPC_type ) == 0 )
00608                 {
00609                         return ( 1 << WP_BLASTER);
00610                 }
00611 
00612                 if (( Q_stricmp( "interrogator",NPC_type) == 0) || ( Q_stricmp( "sentry",NPC_type) == 0) || (Q_strncmp( "protocol",NPC_type,8) == 0) )
00613                 {
00614                         return WP_NONE;
00615                 }
00616 
00617                 if ( Q_strncmp( "weequay", NPC_type, 7 ) == 0 )
00618                 {
00619                         return ( 1 << WP_BOWCASTER);//|( 1 << WP_STAFF )(FIXME: new weap?)
00620                 }
00621                 if ( Q_stricmp( "impofficer", NPC_type ) == 0 )
00622                 {
00623                         return ( 1 << WP_BLASTER);
00624                 }
00625                 if ( Q_stricmp( "impcommander", NPC_type ) == 0 )
00626                 {
00627                         return ( 1 << WP_BLASTER);
00628                 }
00629                 if (( Q_stricmp( "probe", NPC_type ) == 0 ) || ( Q_stricmp( "seeker", NPC_type ) == 0 ))
00630                 {
00631                         //return ( 1 << WP_BOT_LASER);
00632                         return 0;
00633                 }       
00634                 if ( Q_stricmp( "remote", NPC_type ) == 0 )
00635                 {
00636                         //return ( 1 << WP_BOT_LASER );
00637                         return 0;
00638                 }       
00639                 if ( Q_stricmp( "trandoshan", NPC_type ) == 0 )
00640                 {
00641                         return (1<<WP_REPEATER);
00642                 }
00643                 if ( Q_stricmp( "atst", NPC_type ) == 0 )
00644                 {
00645                         //return (( 1 << WP_ATST_MAIN)|( 1 << WP_ATST_SIDE));
00646                         return 0;
00647                 }
00648                 if ( Q_stricmp( "mark1", NPC_type ) == 0 )
00649                 {
00650                         //return ( 1 << WP_BOT_LASER);
00651                         return 0;
00652                 }
00653                 if ( Q_stricmp( "mark2", NPC_type ) == 0 )
00654                 {
00655                         //return ( 1 << WP_BOT_LASER);
00656                         return 0;
00657                 }
00658                 if ( Q_stricmp( "minemonster", NPC_type ) == 0 )
00659                 {
00660                         return (( 1 << WP_STUN_BATON));
00661                 }
00662                 if ( Q_stricmp( "howler", NPC_type ) == 0 )
00663                 {
00664                         return (( 1 << WP_STUN_BATON));
00665                 }
00666                 //Stormtroopers, etc.
00667                 return ( 1 << WP_BLASTER);
00668                 break;
00669 
00670         case NPCTEAM_PLAYER:
00671                 
00672 //              if(spawnflags & SFB_TRICORDER)
00673 //                      return ( 1 << WP_TRICORDER);
00674                 
00675                 if(spawnflags & SFB_RIFLEMAN)
00676                         return ( 1 << WP_REPEATER);
00677                 
00678                 if(spawnflags & SFB_PHASER)
00679                         //return ( 1 << WP_BLASTER_PISTOL);
00680                         return ( 1 << WP_BLASTER);
00681 
00682                 if ( Q_strncmp( "jedi", NPC_type, 4 ) == 0 || Q_stricmp( "luke", NPC_type ) == 0 )
00683                         return ( 1 << WP_SABER);
00684 
00685                 if ( Q_strncmp( "prisoner", NPC_type, 8 ) == 0 )
00686                 {
00687                         return WP_NONE;
00688                 }
00689                 if ( Q_strncmp( "bespincop", NPC_type, 9 ) == 0 )
00690                 {
00691                         //return ( 1 << WP_BLASTER_PISTOL);
00692                         return ( 1 << WP_BLASTER);
00693                 }
00694 
00695                 if ( Q_stricmp( "MonMothma", NPC_type ) == 0 )
00696                 {
00697                         return WP_NONE;
00698                 }
00699 
00700                 //rebel
00701                 return ( 1 << WP_BLASTER);
00702                 break;
00703 
00704         case NPCTEAM_NEUTRAL:
00705 
00706                 if ( Q_stricmp( "mark1", NPC_type ) == 0 )
00707                 {
00708                         return WP_NONE;
00709                 }       
00710                 if ( Q_stricmp( "mark2", NPC_type ) == 0 )
00711                 {
00712                         return WP_NONE;
00713                 }       
00714                 if ( Q_strncmp( "ugnaught", NPC_type, 8 ) == 0 )
00715                 {
00716                         return WP_NONE;
00717                 }       
00718                 if ( Q_stricmp( "bartender", NPC_type ) == 0 )
00719                 {
00720                         return WP_NONE;
00721                 }
00722                 if ( Q_stricmp( "morgankatarn", NPC_type ) == 0 )
00723                 {
00724                         return WP_NONE;
00725                 }
00726         
00727                 break;
00728 
00729         // these no longer exist
00730 //      case TEAM_FORGE:
00731 //              return ( 1 << WP_STUN_BATON);
00732 //              break;
00733 
00734 //      case TEAM_STASIS:
00735 //              return ( 1 << WP_STUN_BATON);
00736 //              break;
00737 
00738 //      case TEAM_PARASITE:
00739 //              break;
00740 
00741 //      case TEAM_8472:
00742 //              break;
00743 
00744         default:
00745                 break;
00746         }
00747 
00748         return WP_NONE;
00749 }
00750 
00751 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
00752 
00753 /*
00754 -------------------------
00755 NPC_SetWeapons
00756 -------------------------
00757 */
00758 
00759 void NPC_SetWeapons( gentity_t *ent )
00760 {
00761         int                     bestWeap = WP_NONE;
00762         int                     curWeap;
00763         int                     weapons = NPC_WeaponsForTeam( ent->client->playerTeam, ent->spawnflags, ent->NPC_type );
00764 
00765         ent->client->ps.stats[STAT_WEAPONS] = 0;
00766         for ( curWeap = WP_SABER; curWeap < WP_NUM_WEAPONS; curWeap++ )
00767         {
00768                 if ( (weapons & ( 1 << curWeap )) )
00769                 {
00770                         ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << curWeap );
00771 //                      RegisterItem( FindItemForWeapon( (weapon_t)(curWeap) ) );       //precache the weapon
00772                         //rwwFIXMEFIXME: Precache
00773                         ent->NPC->currentAmmo = ent->client->ps.ammo[weaponData[curWeap].ammoIndex] = 100;//FIXME: max ammo
00774 
00775                         if ( bestWeap == WP_SABER )
00776                         {
00777                                 // still want to register other weapons -- force saber to be best weap
00778                                 continue;
00779                         }
00780 
00781                         if ( curWeap == WP_STUN_BATON )
00782                         {
00783                                 if ( bestWeap == WP_NONE )
00784                                 {// We'll only consider giving Melee since we haven't found anything better yet.
00785                                         bestWeap = curWeap;
00786                                 }
00787                         }
00788                         else if ( curWeap > bestWeap || bestWeap == WP_STUN_BATON )
00789                         {
00790                                 // This will never override saber as best weap.  Also will override WP_STUN_BATON if something better comes later in the list
00791                                 bestWeap = curWeap;
00792                         }
00793                 }
00794         }
00795 
00796         ent->client->ps.weapon = bestWeap;
00797 }
00798 
00799 /*
00800 -------------------------
00801 NPC_SpawnEffect
00802 
00803   NOTE:  Make sure any effects called here have their models, tga's and sounds precached in
00804                         CG_RegisterNPCEffects in cg_player.cpp
00805 -------------------------
00806 */
00807 
00808 void NPC_SpawnEffect (gentity_t *ent)
00809 {
00810 }
00811 
00812 //--------------------------------------------------------------
00813 // NPC_SetFX_SpawnStates
00814 //
00815 // Set up any special parms for spawn effects
00816 //--------------------------------------------------------------
00817 void NPC_SetFX_SpawnStates( gentity_t *ent )
00818 {
00819         if ( !(ent->NPC->aiFlags&NPCAI_CUSTOM_GRAVITY) )
00820         {
00821                 ent->client->ps.gravity = g_gravity.value;
00822         }
00823 }
00824 
00825 /*
00826 ================
00827 NPC_SpotWouldTelefrag
00828 
00829 ================
00830 */
00831 qboolean NPC_SpotWouldTelefrag( gentity_t *npc )
00832 {
00833         int                     i, num;
00834         int                     touch[MAX_GENTITIES];
00835         gentity_t       *hit;
00836         vec3_t          mins, maxs;
00837 
00838         VectorAdd( npc->r.currentOrigin, npc->r.mins, mins );
00839         VectorAdd( npc->r.currentOrigin, npc->r.maxs, maxs );
00840         num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
00841 
00842         for (i=0 ; i<num ; i++)
00843         {
00844                 hit = &g_entities[touch[i]];
00845                 //if ( hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 ) {
00846                 if (hit->inuse 
00847                         && hit->client 
00848                         && hit->s.number != npc->s.number 
00849                         && (hit->r.contents&MASK_NPCSOLID)
00850                         && hit->s.number != npc->r.ownerNum
00851                         && hit->r.ownerNum != npc->s.number)
00852                 {
00853                         return qtrue;
00854                 }
00855 
00856         }
00857 
00858         return qfalse;
00859 }
00860 
00861 //--------------------------------------------------------------
00862 void NPC_Begin (gentity_t *ent)
00863 {
00864         vec3_t  spawn_origin, spawn_angles;
00865         gclient_t       *client;
00866         usercmd_t       ucmd;
00867         gentity_t       *spawnPoint = NULL;
00868 
00869         memset( &ucmd, 0, sizeof( ucmd ) );
00870 
00871         if ( !(ent->spawnflags & SFB_NOTSOLID) )
00872         {//No NPCs should telefrag
00873                 if (NPC_SpotWouldTelefrag(ent))
00874                 {
00875                         if ( ent->wait < 0 )
00876                         {//remove yourself
00877                                 G_DebugPrint( WL_DEBUG, "NPC %s could not spawn, firing target3 (%s) and removing self\n", ent->targetname, ent->target3 );
00878                                 //Fire off our target3
00879                                 G_UseTargets2( ent, ent, ent->target3 );
00880 
00881                                 //Kill us
00882                                 ent->think = G_FreeEntity;
00883                                 ent->nextthink = level.time + 100;
00884                         }
00885                         else
00886                         {
00887                                 G_DebugPrint( WL_DEBUG, "NPC %s could not spawn, waiting %4.2 secs to try again\n", ent->targetname, ent->wait/1000.0f );
00888                                 ent->think = NPC_Begin;
00889                                 ent->nextthink = level.time + ent->wait;//try again in half a second
00890                         }
00891                         return;
00892                 }
00893         }
00894         //Spawn effect
00895         NPC_SpawnEffect( ent );
00896 
00897         VectorCopy( ent->client->ps.origin, spawn_origin);
00898         VectorCopy( ent->s.angles, spawn_angles);
00899         spawn_angles[YAW] = ent->NPC->desiredYaw;
00900 
00901         client = ent->client;
00902 
00903         // increment the spawncount so the client will detect the respawn
00904         client->ps.persistant[PERS_SPAWN_COUNT]++;
00905 
00906         client->airOutTime = level.time + 12000;
00907 
00908         client->ps.clientNum = ent->s.number;
00909         // clear entity values
00910 
00911         if ( ent->health )      // Was health supplied in map
00912         {
00913                 client->pers.maxHealth = client->ps.stats[STAT_MAX_HEALTH] = ent->health;
00914         }
00915         else if ( ent->NPC->stats.health )      // Was health supplied in NPC.cfg?
00916         {
00917                 
00918                 if ( ent->client->NPC_class != CLASS_REBORN
00919                         && ent->client->NPC_class != CLASS_SHADOWTROOPER 
00920                         //&& ent->client->NPC_class != CLASS_TAVION
00921                         //&& ent->client->NPC_class != CLASS_DESANN 
00922                         && ent->client->NPC_class != CLASS_JEDI )
00923                 {// up everyone except jedi
00924                         ent->NPC->stats.health += ent->NPC->stats.health/4 * g_spskill.integer; // 100% on easy, 125% on medium, 150% on hard
00925                 }
00926                 
00927                 client->pers.maxHealth = client->ps.stats[STAT_MAX_HEALTH] = ent->NPC->stats.health;
00928         }
00929         else
00930         {
00931                 client->pers.maxHealth = client->ps.stats[STAT_MAX_HEALTH] = 100;
00932         }
00933 
00934         if ( !Q_stricmp( "rodian", ent->NPC_type ) )
00935         {//sniper
00936                 //NOTE: this will get overridden by any aim settings in their spawnscripts
00937                 switch ( g_spskill.integer )
00938                 {
00939                 case 0:
00940                         ent->NPC->stats.aim = 1;
00941                         break;
00942                 case 1:
00943                         ent->NPC->stats.aim = Q_irand( 2, 3 );
00944                         break;
00945                 case 2:
00946                         ent->NPC->stats.aim = Q_irand( 3, 4 );
00947                         break;
00948                 }
00949         }
00950         else if ( ent->client->NPC_class == CLASS_STORMTROOPER
00951                 || ent->client->NPC_class == CLASS_SWAMPTROOPER
00952                 || ent->client->NPC_class == CLASS_IMPWORKER
00953                 || !Q_stricmp( "rodian2", ent->NPC_type ) )
00954         {//tweak yawspeed for these NPCs based on difficulty
00955                 switch ( g_spskill.integer )
00956                 {
00957                 case 0:
00958                         ent->NPC->stats.yawSpeed *= 0.75f;
00959                         if ( ent->client->NPC_class == CLASS_IMPWORKER )
00960                         {
00961                                 ent->NPC->stats.aim -= Q_irand( 3, 6 );
00962                         }
00963                         break;
00964                 case 1:
00965                         if ( ent->client->NPC_class == CLASS_IMPWORKER )
00966                         {
00967                                 ent->NPC->stats.aim -= Q_irand( 2, 4 );
00968                         }
00969                         break;
00970                 case 2:
00971                         ent->NPC->stats.yawSpeed *= 1.5f;
00972                         if ( ent->client->NPC_class == CLASS_IMPWORKER )
00973                         {
00974                                 ent->NPC->stats.aim -= Q_irand( 0, 2 );
00975                         }
00976                         break;
00977                 }
00978         }
00979         else if ( ent->client->NPC_class == CLASS_REBORN
00980                 || ent->client->NPC_class == CLASS_SHADOWTROOPER )
00981         {
00982                 switch ( g_spskill.integer )
00983                 {
00984                 case 1:
00985                         ent->NPC->stats.yawSpeed *= 1.25f;
00986                         break;
00987                 case 2:
00988                         ent->NPC->stats.yawSpeed *= 1.5f;
00989                         break;
00990                 }
00991         }
00992 
00993 
00994         ent->s.groundEntityNum = ENTITYNUM_NONE;
00995         ent->mass = 10;
00996         ent->takedamage = qtrue;
00997         ent->inuse = qtrue;
00998         ent->classname = "NPC";
00999 //      if ( ent->client->race == RACE_HOLOGRAM )
01000 //      {//can shoot through holograms, but not walk through them
01001 //              ent->contents = CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_ITEM;//contents_corspe to make them show up in ID and use traces
01002 //              ent->clipmask = MASK_NPCSOLID;
01003 //      } else
01004         if(!(ent->spawnflags & SFB_NOTSOLID))
01005         {
01006                 ent->r.contents = CONTENTS_BODY;
01007                 ent->clipmask = MASK_NPCSOLID;
01008         }
01009         else
01010         {
01011                 ent->r.contents = 0;
01012                 ent->clipmask = MASK_NPCSOLID&~CONTENTS_BODY;
01013         }
01014         /*
01015         if(!ent->client->moveType)//Static?
01016         {
01017                 ent->client->moveType = MT_RUNJUMP;
01018         }
01019         */
01020         //rwwFIXMEFIXME: movetype support
01021 
01022         ent->die = player_die;
01023         ent->waterlevel = 0;
01024         ent->watertype = 0;
01025         ent->client->ps.rocketLockIndex = ENTITYNUM_NONE;
01026         ent->client->ps.rocketLockTime = 0;
01027         
01028         //visible to player and NPCs
01029         if ( ent->client->NPC_class != CLASS_R2D2 &&
01030                 ent->client->NPC_class != CLASS_R5D2 &&
01031                 ent->client->NPC_class != CLASS_MOUSE &&
01032                 ent->client->NPC_class != CLASS_GONK &&
01033                 ent->client->NPC_class != CLASS_PROTOCOL )
01034         {
01035                 ent->flags &= ~FL_NOTARGET;
01036         }
01037         ent->s.eFlags &= ~EF_NODRAW;
01038 
01039         NPC_SetFX_SpawnStates( ent );
01040         
01041         //client->ps.friction = 6;
01042         //rwwFIXMEFIXME: per ent friction?
01043 
01044         if ( ent->client->ps.weapon == WP_NONE )
01045         {//not set by the NPCs.cfg
01046                 NPC_SetWeapons(ent);
01047         }
01048         //select the weapon
01049         ent->NPC->currentAmmo = ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex];
01050         ent->client->ps.weaponstate = WEAPON_IDLE;
01051         ChangeWeapon( ent, ent->client->ps.weapon );
01052 
01053         VectorCopy( spawn_origin, client->ps.origin );
01054 
01055         // the respawned flag will be cleared after the attack and jump keys come up
01056         client->ps.pm_flags |= PMF_RESPAWNED;
01057 
01058         // clear entity state values
01059         //ent->s.eType = ET_PLAYER;
01060         ent->s.eType = ET_NPC;
01061 //      ent->s.skinNum = ent - g_entities - 1;  // used as index to get custom models
01062 
01063         VectorCopy (spawn_origin, ent->s.origin);
01064 //      ent->s.origin[2] += 1;  // make sure off ground
01065 
01066         SetClientViewAngle( ent, spawn_angles );
01067         client->renderInfo.lookTarget = ENTITYNUM_NONE;
01068 
01069         if(!(ent->spawnflags & 64))
01070         {
01071                 G_KillBox( ent );
01072                 trap_LinkEntity (ent);
01073         }
01074 
01075         // don't allow full run speed for a bit
01076         client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
01077         client->ps.pm_time = 100;
01078 
01079         client->respawnTime = level.time;
01080         client->inactivityTime = level.time + g_inactivity.value * 1000;
01081         client->latched_buttons = 0;
01082         if ( ent->s.m_iVehicleNum )
01083         {//I'm an NPC in a vehicle (or a vehicle), I already have owner set
01084         }
01085         else if ( client->NPC_class == CLASS_SEEKER && ent->activator != NULL )
01086         {//somebody else "owns" me
01087                 ent->s.owner = ent->r.ownerNum = ent->activator->s.number;
01088         }
01089         else
01090         {
01091                 ent->s.owner = ENTITYNUM_NONE;
01092         }
01093 
01094         // set default animations
01095         if ( ent->client->NPC_class != CLASS_VEHICLE )
01096         {
01097                 NPC_SetAnim( ent, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_NORMAL );
01098         }
01099 
01100         if( spawnPoint )
01101         {
01102                 // fire the targets of the spawn point
01103                 G_UseTargets( spawnPoint, ent );
01104         }
01105 
01106         //ICARUS include
01107         trap_ICARUS_InitEnt( ent );
01108 
01109 //==NPC initialization
01110         SetNPCGlobals( ent );
01111 
01112         ent->enemy = NULL;
01113         NPCInfo->timeOfDeath = 0;
01114         NPCInfo-