00001
00002
00003 #include "g_local.h"
00004 #include "../ghoul2/G2.h"
00005 #include "q_shared.h"
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #define RESPAWN_ARMOR 20
00022 #define RESPAWN_TEAM_WEAPON 30
00023 #define RESPAWN_HEALTH 30
00024 #define RESPAWN_AMMO 40
00025 #define RESPAWN_HOLDABLE 60
00026 #define RESPAWN_MEGAHEALTH 120
00027 #define RESPAWN_POWERUP 120
00028
00029
00030 #define ITMSF_SUSPEND 1
00031 #define ITMSF_NOPLAYER 2
00032 #define ITMSF_ALLOWNPC 4
00033 #define ITMSF_NOTSOLID 8
00034 #define ITMSF_VERTICAL 16
00035 #define ITMSF_INVISIBLE 32
00036
00037 extern gentity_t *droppedRedFlag;
00038 extern gentity_t *droppedBlueFlag;
00039
00040
00041
00042 #define MAX_MEDPACK_HEAL_AMOUNT 25
00043 #define MAX_MEDPACK_BIG_HEAL_AMOUNT 50
00044 #define MAX_SENTRY_DISTANCE 256
00045
00046
00047 int adjustRespawnTime(float preRespawnTime, int itemType, int itemTag)
00048 {
00049 float respawnTime = preRespawnTime;
00050
00051 if (itemType == IT_WEAPON)
00052 {
00053 if (itemTag == WP_THERMAL ||
00054 itemTag == WP_TRIP_MINE ||
00055 itemTag == WP_DET_PACK)
00056 {
00057 respawnTime = RESPAWN_AMMO;
00058 }
00059 }
00060
00061 if (!g_adaptRespawn.integer)
00062 {
00063 return((int)respawnTime);
00064 }
00065
00066 if (level.numPlayingClients > 4)
00067 {
00068 if (level.numPlayingClients > 32)
00069 {
00070 respawnTime *= 0.25;
00071 }
00072 else if (level.numPlayingClients > 12)
00073 {
00074 respawnTime *= 20.0 / (float)(level.numPlayingClients + 8);
00075 }
00076 else
00077 {
00078 respawnTime *= 8.0 / (float)(level.numPlayingClients + 4);
00079 }
00080 }
00081
00082 if (respawnTime < 1.0)
00083 {
00084 respawnTime = 1.0;
00085 }
00086
00087 return ((int)respawnTime);
00088 }
00089
00090
00091 #define SHIELD_HEALTH 250
00092 #define SHIELD_HEALTH_DEC 10 // 25 seconds
00093 #define MAX_SHIELD_HEIGHT 254
00094 #define MAX_SHIELD_HALFWIDTH 255
00095 #define SHIELD_HALFTHICKNESS 4
00096 #define SHIELD_PLACEDIST 64
00097
00098 #define SHIELD_SIEGE_HEALTH 2000
00099 #define SHIELD_SIEGE_HEALTH_DEC (SHIELD_SIEGE_HEALTH/25) // still 25 seconds.
00100
00101 static qhandle_t shieldLoopSound=0;
00102 static qhandle_t shieldAttachSound=0;
00103 static qhandle_t shieldActivateSound=0;
00104 static qhandle_t shieldDeactivateSound=0;
00105 static qhandle_t shieldDamageSound=0;
00106
00107
00108 void ShieldRemove(gentity_t *self)
00109 {
00110 self->think = G_FreeEntity;
00111 self->nextthink = level.time + 100;
00112
00113
00114 G_AddEvent(self, EV_GENERAL_SOUND, shieldDeactivateSound);
00115 self->s.loopSound = 0;
00116 self->s.loopIsSoundset = qfalse;
00117
00118 return;
00119 }
00120
00121
00122
00123 void ShieldThink(gentity_t *self)
00124 {
00125 self->s.trickedentindex = 0;
00126
00127 if ( g_gametype.integer == GT_SIEGE )
00128 {
00129 self->health -= SHIELD_SIEGE_HEALTH_DEC;
00130 }
00131 else
00132 {
00133 self->health -= SHIELD_HEALTH_DEC;
00134 }
00135 self->nextthink = level.time + 1000;
00136 if (self->health <= 0)
00137 {
00138 ShieldRemove(self);
00139 }
00140 return;
00141 }
00142
00143
00144
00145 void ShieldDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
00146 {
00147
00148 G_AddEvent(self, EV_GENERAL_SOUND, shieldDamageSound);
00149
00150 ShieldRemove(self);
00151 }
00152
00153
00154
00155 void ShieldPain(gentity_t *self, gentity_t *attacker, int damage)
00156 {
00157
00158 self->think = ShieldThink;
00159 self->nextthink = level.time + 400;
00160
00161
00162 G_AddEvent(self, EV_GENERAL_SOUND, shieldDamageSound);
00163
00164 self->s.trickedentindex = 1;
00165
00166 return;
00167 }
00168
00169
00170
00171 void ShieldGoSolid(gentity_t *self)
00172 {
00173 trace_t tr;
00174
00175
00176 self->health--;
00177 if (self->health <= 0)
00178 {
00179 ShieldRemove(self);
00180 return;
00181 }
00182
00183 trap_Trace (&tr, self->r.currentOrigin, self->r.mins, self->r.maxs, self->r.currentOrigin, self->s.number, CONTENTS_BODY );
00184 if(tr.startsolid)
00185 {
00186 self->nextthink = level.time + 200;
00187 self->think = ShieldGoSolid;
00188 trap_LinkEntity(self);
00189 }
00190 else
00191 {
00192 self->s.eFlags &= ~EF_NODRAW;
00193
00194 self->r.contents = CONTENTS_SOLID;
00195 self->nextthink = level.time + 1000;
00196 self->think = ShieldThink;
00197 self->takedamage = qtrue;
00198 trap_LinkEntity(self);
00199
00200
00201 G_AddEvent(self, EV_GENERAL_SOUND, shieldActivateSound);
00202 self->s.loopSound = shieldLoopSound;
00203 self->s.loopIsSoundset = qfalse;
00204 }
00205
00206 return;
00207 }
00208
00209
00210
00211 void ShieldGoNotSolid(gentity_t *self)
00212 {
00213
00214 self->r.contents = 0;
00215 self->s.eFlags |= EF_NODRAW;
00216
00217 self->nextthink = level.time + 200;
00218 self->think = ShieldGoSolid;
00219 self->takedamage = qfalse;
00220 trap_LinkEntity(self);
00221
00222
00223 G_AddEvent(self, EV_GENERAL_SOUND, shieldDeactivateSound);
00224 self->s.loopSound = 0;
00225 self->s.loopIsSoundset = qfalse;
00226 }
00227
00228
00229
00230 void ShieldTouch(gentity_t *self, gentity_t *other, trace_t *trace)
00231 {
00232 if (g_gametype.integer >= GT_TEAM)
00233 {
00234
00235 if (self->parent && ( self->parent->client) && (other->client))
00236 {
00237 if (OnSameTeam(self->parent, other))
00238 {
00239 ShieldGoNotSolid(self);
00240 }
00241 }
00242 }
00243 else
00244 {
00245 if (self->parent && self->parent->s.number == other->s.number)
00246 {
00247 ShieldGoNotSolid(self);
00248 }
00249 }
00250 }
00251
00252
00253
00254 void CreateShield(gentity_t *ent)
00255 {
00256 trace_t tr;
00257 vec3_t mins, maxs, end, posTraceEnd, negTraceEnd, start;
00258 int height, posWidth, negWidth, halfWidth = 0;
00259 qboolean xaxis;
00260 int paramData = 0;
00261 static int shieldID;
00262
00263
00264 VectorCopy(ent->r.currentOrigin, end);
00265 end[2] += MAX_SHIELD_HEIGHT;
00266 trap_Trace (&tr, ent->r.currentOrigin, NULL, NULL, end, ent->s.number, MASK_SHOT );
00267 height = (int)(MAX_SHIELD_HEIGHT * tr.fraction);
00268
00269
00270 VectorSet(mins, -SHIELD_HALFTHICKNESS, -SHIELD_HALFTHICKNESS, 0);
00271 VectorSet(maxs, SHIELD_HALFTHICKNESS, SHIELD_HALFTHICKNESS, height);
00272 VectorCopy(ent->r.currentOrigin, posTraceEnd);
00273 VectorCopy(ent->r.currentOrigin, negTraceEnd);
00274
00275 if ((int)(ent->s.angles[YAW]) == 0)
00276 {
00277 posTraceEnd[1]+=MAX_SHIELD_HALFWIDTH;
00278 negTraceEnd[1]-=MAX_SHIELD_HALFWIDTH;
00279 xaxis = qfalse;
00280 }
00281 else
00282 {
00283 posTraceEnd[0]+=MAX_SHIELD_HALFWIDTH;
00284 negTraceEnd[0]-=MAX_SHIELD_HALFWIDTH;
00285 xaxis = qtrue;
00286 }
00287
00288
00289
00290 VectorCopy(ent->r.currentOrigin, start);
00291 start[2] += (height>>1);
00292 trap_Trace (&tr, start, 0, 0, posTraceEnd, ent->s.number, MASK_SHOT );
00293 posWidth = MAX_SHIELD_HALFWIDTH * tr.fraction;
00294
00295 trap_Trace (&tr, start, 0, 0, negTraceEnd, ent->s.number, MASK_SHOT );
00296 negWidth = MAX_SHIELD_HALFWIDTH * tr.fraction;
00297
00298
00299 halfWidth = (posWidth + negWidth)>>1;
00300 if (xaxis)
00301 {
00302 ent->r.currentOrigin[0] = ent->r.currentOrigin[0] - negWidth + halfWidth;
00303 }
00304 else
00305 {
00306 ent->r.currentOrigin[1] = ent->r.currentOrigin[1] - negWidth + halfWidth;
00307 }
00308 ent->r.currentOrigin[2] += (height>>1);
00309
00310
00311 if (xaxis)
00312 {
00313 VectorSet(ent->r.mins, -halfWidth, -SHIELD_HALFTHICKNESS, -(height>>1));
00314 VectorSet(ent->r.maxs, halfWidth, SHIELD_HALFTHICKNESS, height>>1);
00315 }
00316 else
00317 {
00318 VectorSet(ent->r.mins, -SHIELD_HALFTHICKNESS, -halfWidth, -(height>>1));
00319 VectorSet(ent->r.maxs, SHIELD_HALFTHICKNESS, halfWidth, height);
00320 }
00321 ent->clipmask = MASK_SHOT;
00322
00323
00324
00325
00326
00327
00328
00329
00330 paramData = (xaxis << 24) | (height << 16) | (posWidth << 8) | (negWidth);
00331 ent->s.time2 = paramData;
00332
00333 if ( g_gametype.integer == GT_SIEGE )
00334 {
00335 ent->health = ceil((float)(SHIELD_SIEGE_HEALTH*1));
00336 }
00337 else
00338 {
00339 ent->health = ceil((float)(SHIELD_HEALTH*1));
00340 }
00341
00342 ent->s.time = ent->health;
00343 ent->pain = ShieldPain;
00344 ent->die = ShieldDie;
00345 ent->touch = ShieldTouch;
00346
00347
00348 trap_Trace (&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, CONTENTS_BODY );
00349
00350 if (tr.startsolid)
00351 {
00352
00353 ent->r.contents = 0;
00354 ent->s.eFlags |= EF_NODRAW;
00355
00356 ent->nextthink = level.time + 200;
00357 ent->think = ShieldGoSolid;
00358 ent->takedamage = qfalse;
00359 trap_LinkEntity(ent);
00360 }
00361 else
00362 {
00363 ent->r.contents = CONTENTS_PLAYERCLIP|CONTENTS_SHOTCLIP;
00364
00365 ent->nextthink = level.time;
00366 ent->think = ShieldThink;
00367
00368 ent->takedamage = qtrue;
00369 trap_LinkEntity(ent);
00370
00371
00372 G_AddEvent(ent, EV_GENERAL_SOUND, shieldActivateSound);
00373 ent->s.loopSound = shieldLoopSound;
00374 ent->s.loopIsSoundset = qfalse;
00375 }
00376
00377 ShieldGoSolid(ent);
00378
00379 return;
00380 }
00381
00382 qboolean PlaceShield(gentity_t *playerent)
00383 {
00384 static const gitem_t *shieldItem = NULL;
00385 gentity_t *shield = NULL;
00386 trace_t tr;
00387 vec3_t fwd, pos, dest, mins = {-4,-4, 0}, maxs = {4,4,4};
00388
00389 if (shieldAttachSound==0)
00390 {
00391 shieldLoopSound = G_SoundIndex("sound/movers/doors/forcefield_lp.wav");
00392 shieldAttachSound = G_SoundIndex("sound/weapons/detpack/stick.wav");
00393 shieldActivateSound = G_SoundIndex("sound/movers/doors/forcefield_on.wav");
00394 shieldDeactivateSound = G_SoundIndex("sound/movers/doors/forcefield_off.wav");
00395 shieldDamageSound = G_SoundIndex("sound/effects/bumpfield.wav");
00396 shieldItem = BG_FindItemForHoldable(HI_SHIELD);
00397 }
00398
00399
00400 AngleVectors (playerent->client->ps.viewangles, fwd, NULL, NULL);
00401 fwd[2] = 0;
00402 VectorMA(playerent->client->ps.origin, SHIELD_PLACEDIST, fwd, dest);
00403 trap_Trace (&tr, playerent->client->ps.origin, mins, maxs, dest, playerent->s.number, MASK_SHOT );
00404 if (tr.fraction > 0.9)
00405 {
00406 VectorCopy(tr.endpos, pos);
00407
00408 VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
00409 trap_Trace( &tr, pos, mins, maxs, dest, playerent->s.number, MASK_SOLID );
00410 if ( !tr.startsolid && !tr.allsolid )
00411 {
00412
00413 shield = G_Spawn();
00414
00415
00416 if (fabs(fwd[0]) > fabs(fwd[1]))
00417 {
00418 shield->s.angles[YAW] = 0;
00419 }
00420 else
00421 {
00422 shield->s.angles[YAW] = 90;
00423 }
00424 shield->think = CreateShield;
00425 shield->nextthink = level.time + 500;
00426 shield->parent = playerent;
00427
00428
00429 shield->s.otherEntityNum2 = playerent->client->sess.sessionTeam;
00430
00431 shield->s.eType = ET_SPECIAL;
00432 shield->s.modelindex = HI_SHIELD;
00433 shield->classname = shieldItem->classname;
00434
00435 shield->r.contents = CONTENTS_TRIGGER;
00436
00437 shield->touch = 0;
00438
00439 shield->use = 0;
00440
00441
00442 shield->s.groundEntityNum = tr.entityNum;
00443
00444 G_SetOrigin( shield, tr.endpos );
00445
00446 shield->s.eFlags &= ~EF_NODRAW;
00447 shield->r.svFlags &= ~SVF_NOCLIENT;
00448
00449 trap_LinkEntity (shield);
00450
00451 shield->s.owner = playerent->s.number;
00452 shield->s.shouldtarget = qtrue;
00453 if (g_gametype.integer >= GT_TEAM)
00454 {
00455 shield->s.teamowner = playerent->client->sess.sessionTeam;
00456 }
00457 else
00458 {
00459 shield->s.teamowner = 16;
00460 }
00461
00462
00463 G_AddEvent(shield, EV_GENERAL_SOUND, shieldAttachSound);
00464
00465 return qtrue;
00466 }
00467 }
00468
00469 return qfalse;
00470 }
00471
00472 void ItemUse_Binoculars(gentity_t *ent)
00473 {
00474 if (!ent || !ent->client)
00475 {
00476 return;
00477 }
00478
00479 if (ent->client->ps.weaponstate != WEAPON_READY)
00480 {
00481 return;
00482 }
00483
00484
00485
00486
00487
00488
00489
00490
00491 if (ent->client->ps.zoomMode == 0)
00492 {
00493 ent->client->ps.zoomMode = 2;
00494 ent->client->ps.zoomLocked = qfalse;
00495 ent->client->ps.zoomFov = 40.0f;
00496 }
00497 else if (ent->client->ps.zoomMode == 2)
00498 {
00499 ent->client->ps.zoomMode = 0;
00500 ent->client->ps.zoomTime = level.time;
00501 }
00502 }
00503
00504 void ItemUse_Shield(gentity_t *ent)
00505 {
00506 PlaceShield(ent);
00507 }
00508
00509
00510
00511
00512
00513 #define PAS_DAMAGE 2
00514
00515 void SentryTouch(gentity_t *ent, gentity_t *other, trace_t *trace)
00516 {
00517 return;
00518 }
00519
00520
00521 void pas_fire( gentity_t *ent )
00522
00523 {
00524 vec3_t fwd, myOrg, enOrg;
00525
00526 VectorCopy(ent->r.currentOrigin, myOrg);
00527 myOrg[2] += 24;
00528
00529 VectorCopy(ent->enemy->client->ps.origin, enOrg);
00530 enOrg[2] += 24;
00531
00532 VectorSubtract(enOrg, myOrg, fwd);
00533 VectorNormalize(fwd);
00534
00535 myOrg[0] += fwd[0]*16;
00536 myOrg[1] += fwd[1]*16;
00537 myOrg[2] += fwd[2]*16;
00538
00539 WP_FireTurretMissile(&g_entities[ent->genericValue3], myOrg, fwd, qfalse, 10, 2300, MOD_SENTRY, ent );
00540
00541 G_RunObject(ent);
00542 }
00543
00544 #define TURRET_RADIUS 800
00545
00546
00547 static qboolean pas_find_enemies( gentity_t *self )
00548
00549 {
00550 qboolean found = qfalse;
00551 int count, i;
00552 float bestDist = TURRET_RADIUS*TURRET_RADIUS;
00553 float enemyDist;
00554 vec3_t enemyDir, org, org2;
00555 gentity_t *entity_list[MAX_GENTITIES], *target;
00556 trace_t tr;
00557
00558 if ( self->aimDebounceTime > level.time )
00559 {
00560
00561 if ( self->painDebounceTime < level.time )
00562 {
00563 G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" ));
00564 self->painDebounceTime = level.time + 1000;
00565 }
00566 }
00567
00568 VectorCopy(self->s.pos.trBase, org2);
00569
00570 count = G_RadiusList( org2, TURRET_RADIUS, self, qtrue, entity_list );
00571
00572 for ( i = 0; i < count; i++ )
00573 {
00574 target = entity_list[i];
00575
00576 if ( !target->client )
00577 {
00578 continue;
00579 }
00580 if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET ))
00581 {
00582 continue;
00583 }
00584 if ( self->alliedTeam && target->client->sess.sessionTeam == self->alliedTeam )
00585 {
00586 continue;
00587 }
00588 if (self->genericValue3 == target->s.number)
00589 {
00590 continue;
00591 }
00592 if ( !trap_InPVS( org2, target->r.currentOrigin ))
00593 {
00594 continue;
00595 }
00596
00597 if (target->s.eType == ET_NPC &&
00598 target->s.NPC_class == CLASS_VEHICLE)
00599 {
00600 continue;
00601 }
00602
00603 if ( target->client )
00604 {
00605 VectorCopy( target->client->ps.origin, org );
00606 }
00607 else
00608 {
00609 VectorCopy( target->r.currentOrigin, org );
00610 }
00611
00612 trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT );
00613
00614 if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number ))
00615 {
00616
00617 VectorSubtract( target->r.currentOrigin, self->r.currentOrigin, enemyDir );
00618 enemyDist = VectorLengthSquared( enemyDir );
00619
00620 if ( enemyDist < bestDist )
00621 {
00622 if ( self->attackDebounceTime + 100 < level.time )
00623 {
00624
00625 G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" ));
00626
00627
00628 self->attackDebounceTime = level.time + 900 + random() * 200;
00629 }
00630
00631 G_SetEnemy( self, target );
00632 bestDist = enemyDist;
00633 found = qtrue;
00634 }
00635 }
00636 }
00637
00638 return found;
00639 }
00640
00641
00642 void pas_adjust_enemy( gentity_t *ent )
00643
00644 {
00645 trace_t tr;
00646 qboolean keep = qtrue;
00647
00648 if ( ent->enemy->health <= 0 )
00649 {
00650 keep = qfalse;
00651 }
00652 else
00653 {
00654 vec3_t org, org2;
00655
00656 VectorCopy(ent->s.pos.trBase, org2);
00657
00658 if ( ent->enemy->client )
00659 {
00660 VectorCopy( ent->enemy->client->ps.origin, org );
00661 org[2] -= 15;
00662 }
00663 else
00664 {
00665 VectorCopy( ent->enemy->r.currentOrigin, org );
00666 }
00667
00668 trap_Trace( &tr, org2, NULL, NULL, org, ent->s.number, MASK_SHOT );
00669
00670 if ( tr.allsolid || tr.startsolid || tr.fraction < 0.9f || tr.entityNum == ent->s.number )
00671 {
00672 if (tr.entityNum != ent->enemy->s.number)
00673 {
00674
00675 keep = qfalse;
00676 }
00677 }
00678 }
00679
00680 if ( keep )
00681 {
00682
00683 }
00684 else if ( ent->bounceCount < level.time && ent->enemy )
00685 {
00686 ent->enemy = NULL;
00687
00688 G_Sound( ent, CHAN_BODY, G_SoundIndex( "sound/chars/turret/shutdown.wav" ));
00689
00690 ent->bounceCount = level.time + 500 + random() * 150;
00691
00692
00693 ent->aimDebounceTime = level.time + 5000;
00694 }
00695 }
00696
00697 #define TURRET_DEATH_DELAY 2000
00698 #define TURRET_LIFETIME 60000
00699
00700 void turret_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);
00701
00702 void sentryExpire(gentity_t *self)
00703 {
00704 turret_die(self, self, self, 1000, MOD_UNKNOWN);
00705 }
00706
00707
00708 void pas_think( gentity_t *ent )
00709
00710 {
00711 qboolean moved;
00712 float diffYaw, diffPitch;
00713 vec3_t enemyDir, org;
00714 vec3_t frontAngles, backAngles;
00715 vec3_t desiredAngles;
00716 int iEntityList[MAX_GENTITIES];
00717 int numListedEntities;
00718 int i = 0;
00719 qboolean clTrapped = qfalse;
00720 vec3_t testMins, testMaxs;
00721
00722 testMins[0] = ent->r.currentOrigin[0] + ent->r.mins[0]+4;
00723 testMins[1] = ent->r.currentOrigin[1] + ent->r.mins[1]+4;
00724 testMins[2] = ent->r.currentOrigin[2] + ent->r.mins[2]+4;
00725
00726 testMaxs[0] = ent->r.currentOrigin[0] + ent->r.maxs[0]-4;
00727 testMaxs[1] = ent->r.currentOrigin[1] + ent->r.maxs[1]-4;
00728 testMaxs[2] = ent->r.currentOrigin[2] + ent->r.maxs[2]-4;
00729
00730 numListedEntities = trap_EntitiesInBox( testMins, testMaxs, iEntityList, MAX_GENTITIES );
00731
00732 while (i < numListedEntities)
00733 {
00734 if (iEntityList[i] < MAX_CLIENTS)
00735 {
00736 int clNum = iEntityList[i];
00737
00738 numListedEntities = trap_EntitiesInBox( g_entities[clNum].r.absmin, g_entities[clNum].r.absmax, iEntityList, MAX_GENTITIES );
00739
00740 i = 0;
00741 while (i < numListedEntities)
00742 {
00743 if (iEntityList[i] == ent->s.number)
00744 {
00745 clTrapped = qtrue;
00746 break;
00747 }
00748 i++;
00749 }
00750 break;
00751 }
00752
00753 i++;
00754 }
00755
00756 if (clTrapped)
00757 {
00758 ent->r.contents = 0;
00759 ent->s.fireflag = 0;
00760 ent->nextthink = level.time + FRAMETIME;
00761 return;
00762 }
00763 else
00764 {
00765 ent->r.contents = CONTENTS_SOLID;
00766 }
00767
00768 if (!g_entities[ent->genericValue3].inuse || !g_entities[ent->genericValue3].client ||
00769 g_entities[ent->genericValue3].client->sess.sessionTeam != ent->genericValue2)
00770 {
00771 ent->think = G_FreeEntity;
00772 ent->nextthink = level.time;
00773 return;
00774 }
00775
00776
00777
00778 if ( !ent->damage )
00779 {
00780 ent->damage = 1;
00781 ent->nextthink = level.time + FRAMETIME;
00782 return;
00783 }
00784
00785 if ((ent->genericValue8+TURRET_LIFETIME) < level.time)
00786 {
00787 G_Sound( ent, CHAN_BODY, G_SoundIndex( "sound/chars/turret/shutdown.wav" ));
00788 ent->s.bolt2 = ENTITYNUM_NONE;
00789 ent->s.fireflag = 2;
00790
00791 ent->think = sentryExpire;
00792 ent->nextthink = level.time + TURRET_DEATH_DELAY;
00793 return;
00794 }
00795
00796 ent->nextthink = level.time + FRAMETIME;
00797
00798 if ( ent->enemy )
00799 {
00800
00801 pas_adjust_enemy( ent );
00802 }
00803
00804 if (ent->enemy)
00805 {
00806 if (!ent->enemy->client)
00807 {
00808 ent->enemy = NULL;
00809 }
00810 else if (ent->enemy->s.number == ent->s.number)
00811 {
00812 ent->enemy = NULL;
00813 }
00814 else if (ent->enemy->health < 1)
00815 {
00816 ent->enemy = NULL;
00817 }
00818 }
00819
00820 if ( !ent->enemy )
00821 {
00822 pas_find_enemies( ent );
00823 }
00824
00825 if (ent->enemy)
00826 {
00827 ent->s.bolt2 = ent->enemy->s.number;
00828 }
00829 else
00830 {
00831 ent->s.bolt2 = ENTITYNUM_NONE;
00832 }
00833
00834 moved = qfalse;
00835 diffYaw = 0.0f; diffPitch = 0.0f;
00836
00837 ent->speed = AngleNormalize360( ent->speed );
00838 ent->random = AngleNormalize360( ent->random );
00839
00840 if ( ent->enemy )
00841 {
00842
00843
00844 if ( ent->enemy->client )
00845 {
00846 VectorCopy( ent->enemy->client->ps.origin, org );
00847 }
00848 else
00849 {
00850 VectorCopy( ent->enemy->r.currentOrigin, org );
00851 }
00852
00853 VectorSubtract( org, ent->r.currentOrigin, enemyDir );
00854 vectoangles( enemyDir, desiredAngles );
00855
00856 diffYaw = AngleSubtract( ent->speed, desiredAngles[YAW] );
00857 diffPitch = AngleSubtract( ent->random, desiredAngles[PITCH] );
00858 }
00859 else
00860 {
00861
00862 diffYaw = sin( level.time * 0.0001f + ent->count ) * 2.0f;
00863 }
00864
00865 if ( fabs(diffYaw) > 0.25f )
00866 {
00867 moved = qtrue;
00868
00869 if ( fabs(diffYaw) > 10.0f )
00870 {
00871
00872 ent->speed += (diffYaw > 0.0f) ? -10.0f : 10.0f;
00873 }
00874 else
00875 {
00876
00877 ent->speed -= diffYaw;
00878 }
00879 }
00880
00881
00882 if ( fabs(diffPitch) > 0.25f )
00883 {
00884 moved = qtrue;
00885
00886 if ( fabs(diffPitch) > 4.0f )
00887 {
00888
00889 ent->random += (diffPitch > 0.0f) ? -4.0f : 4.0f;
00890 }
00891 else
00892 {
00893
00894 ent->random -= diffPitch;
00895 }
00896 }
00897
00898
00899 VectorSet( frontAngles, -ent->random, 0.0f, 0.0f );
00900 VectorSet( backAngles, 0.0f, 0.0f, ent->speed );
00901
00902 if ( moved )
00903 {
00904
00905 }
00906 else
00907 {
00908 ent->s.loopSound = 0;
00909 ent->s.loopIsSoundset = qfalse;
00910 }
00911
00912 if ( ent->enemy && ent->attackDebounceTime < level.time )
00913 {
00914 ent->count--;
00915
00916 if ( ent->count )
00917 {
00918 pas_fire( ent );
00919 ent->s.fireflag = 1;
00920 ent->attackDebounceTime = level.time + 200;
00921 }
00922 else
00923 {
00924
00925 G_Sound( ent, CHAN_BODY, G_SoundIndex( "sound/chars/turret/shutdown.wav" ));
00926 ent->s.bolt2 = ENTITYNUM_NONE;
00927 ent->s.fireflag = 2;
00928
00929 ent->think = sentryExpire;
00930 ent->nextthink = level.time + TURRET_DEATH_DELAY;
00931 }
00932 }
00933 else
00934 {
00935 ent->s.fireflag = 0;
00936 }
00937 }
00938
00939
00940 void turret_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
00941
00942 {
00943
00944 self->think = 0;
00945 self->use = 0;
00946
00947 if ( self->target )
00948 {
00949 G_UseTargets( self, attacker );
00950 }
00951
00952 if (!g_entities[self->genericValue3].inuse || !g_entities[self->genericValue3].client)
00953 {
00954 G_FreeEntity(self);
00955 return;
00956 }
00957
00958
00959 self->die = 0;
00960 self->takedamage = qfalse;
00961 self->health = 0;
00962
00963
00964 VectorSet( self->s.angles, 0, 0, 1 );
00965
00966 G_PlayEffect(EFFECT_EXPLOSION_PAS, self->s.pos.trBase, self->s.angles);
00967 G_RadiusDamage(self->s.pos.trBase, &g_entities[self->genericValue3], 30, 256, self, self, MOD_UNKNOWN);
00968
00969 g_entities[self->genericValue3].client->ps.fd.sentryDeployed = qfalse;
00970
00971
00972 G_FreeEntity( self );
00973 }
00974
00975 #define TURRET_AMMO_COUNT 40
00976
00977
00978 void SP_PAS( gentity_t *base )
00979
00980 {
00981 if ( base->count == 0 )
00982 {
00983
00984 base->count = TURRET_AMMO_COUNT;
00985 }
00986
00987 base->s.bolt1 = 1;
00988 base->s.bolt2 = ENTITYNUM_NONE;
00989
00990 base->damage = 0;
00991
00992 VectorSet( base->r.mins, -8, -8, 0 );
00993 VectorSet( base->r.maxs, 8, 8, 24 );
00994
00995 G_RunObject(base);
00996
00997 base->think = pas_think;
00998 base->nextthink = level.time + FRAMETIME;
00999
01000 if ( !base->health )
01001 {
01002 base->health = 50;
01003 }
01004
01005 base->takedamage = qtrue;
01006 base->die = turret_die;
01007
01008 base->physicsObject = qtrue;
01009
01010 G_Sound( base, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" ));
01011 }
01012
01013
01014 void ItemUse_Sentry( gentity_t *ent )
01015
01016 {
01017 vec3_t fwd, fwdorg;
01018 vec3_t yawonly;
01019 vec3_t mins, maxs;
01020 gentity_t *sentry;
01021
01022 if (!ent || !ent->client)
01023 {
01024 return;
01025 }
01026
01027 VectorSet( mins, -8, -8, 0 );
01028 VectorSet( maxs, 8, 8, 24 );
01029
01030
01031 yawonly[ROLL] = 0;
01032 yawonly[PITCH] = 0;
01033 yawonly[YAW] = ent->client->ps.viewangles[YAW];
01034
01035 AngleVectors(yawonly, fwd, NULL, NULL);
01036
01037 fwdorg[0] = ent->client->ps.origin[0] + fwd[0]*64;
01038 fwdorg[1] = ent->client->ps.origin[1] + fwd[1]*64;
01039 fwdorg[2] = ent->client->ps.origin[2] + fwd[2]*64;
01040
01041 sentry = G_Spawn();
01042
01043 sentry->classname = "sentryGun";
01044 sentry->s.modelindex = G_ModelIndex("models/items/psgun.glm");
01045
01046 sentry->s.g2radius = 30.0f;
01047 sentry->s.modelGhoul2 = 1;
01048
01049 G_SetOrigin(sentry, fwdorg);
01050 sentry->parent = ent;
01051 sentry->r.contents = CONTENTS_SOLID;
01052 sentry->s.solid = 2;
01053 sentry->clipmask = MASK_SOLID;
01054 VectorCopy(mins, sentry->r.mins);
01055 VectorCopy(maxs, sentry->r.maxs);
01056 sentry->genericValue3 = ent->s.number;
01057 sentry->genericValue2 = ent->client->sess.sessionTeam;
01058 sentry->r.absmin[0] = sentry->s.pos.trBase[0] + sentry->r.mins[0];
01059 sentry->r.absmin[1] = sentry->s.pos.trBase[1] + sentry->r.mins[1];
01060 sentry->r.absmin[2] = sentry->s.pos.trBase[2] + sentry->r.mins[2];
01061 sentry->r.absmax[0] = sentry->s.pos.trBase[0] + sentry->r.maxs[0];
01062 sentry->r.absmax[1] = sentry->s.pos.trBase[1] + sentry->r.maxs[1];
01063 sentry->r.absmax[2] = sentry->s.pos.trBase[2] + sentry->r.maxs[2];
01064 sentry->s.eType = ET_GENERAL;
01065 sentry->s.pos.trType = TR_GRAVITY;
01066 sentry->s.pos.trTime = level.time;
01067 sentry->touch = SentryTouch;
01068 sentry->nextthink = level.time;
01069 sentry->genericValue4 = ENTITYNUM_NONE;
01070
01071 sentry->genericValue5 = 1000;
01072
01073 sentry->genericValue8 = level.time;
01074
01075 sentry->alliedTeam = ent->client->sess.sessionTeam;
01076
01077 ent->client->ps.fd.sentryDeployed = qtrue;
01078
01079 trap_LinkEntity(sentry);
01080
01081 sentry->s.owner = ent->s.number;
01082 sentry->s.shouldtarget = qtrue;
01083 if (g_gametype.integer >= GT_TEAM)
01084 {
01085 sentry->s.teamowner = ent->client->sess.sessionTeam;
01086 }
01087 else
01088 {
01089 sentry->s.teamowner = 16;
01090 }
01091
01092 SP_PAS( sentry );
01093 }
01094
01095 extern gentity_t *NPC_SpawnType( gentity_t *ent, char *npc_type, char *targetname, qboolean isVehicle );
01096 void ItemUse_Seeker(gentity_t *ent)
01097 {
01098 if ( g_gametype.integer == GT_SIEGE && d_siegeSeekerNPC.integer )
01099 {
01100 gentity_t *remote = NPC_SpawnType( ent, "remote", NULL, qfalse );
01101 if ( remote && remote->client )
01102 {
01103 remote->s.owner = remote->r.ownerNum = ent->s.number;
01104 remote->activator = ent;
01105 if ( ent->client->sess.sessionTeam == TEAM_BLUE )
01106 {
01107 remote->client->playerTeam = NPCTEAM_PLAYER;
01108 }
01109 else if ( ent->client->sess.sessionTeam == TEAM_RED )
01110 {
01111 remote->client->playerTeam = NPCTEAM_ENEMY;
01112 }
01113 else
01114 {
01115 remote->client->playerTeam = NPCTEAM_NEUTRAL;
01116 }
01117 }
01118 }
01119 else
01120 {
01121 ent->client->ps.eFlags |= EF_SEEKERDRONE;
01122 ent->client->ps.droneExistTime = level.time + 30000;
01123 ent->client->ps.droneFireTime = level.time + 1500;
01124 }
01125 }
01126
01127 static void MedPackGive(gentity_t *ent, int amount)
01128 {
01129 if (!ent || !ent->client)
01130 {
01131 return;
01132 }
01133
01134 if (ent->health <= 0 ||
01135 ent->client->ps.stats[STAT_HEALTH] <= 0 ||
01136 (ent->client->ps.eFlags & EF_DEAD))
01137 {
01138 return;
01139 }
01140
01141 if (ent->health >= ent->client->ps.stats[STAT_MAX_HEALTH])
01142 {
01143 return;
01144 }
01145
01146 ent->health += amount;
01147
01148 if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH])
01149 {
01150 ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
01151 }
01152 }
01153
01154 void ItemUse_MedPack_Big(gentity_t *ent)
01155 {
01156 MedPackGive(ent, MAX_MEDPACK_BIG_HEAL_AMOUNT);
01157 }
01158
01159 void ItemUse_MedPack(gentity_t *ent)
01160 {
01161 MedPackGive(ent, MAX_MEDPACK_HEAL_AMOUNT);
01162 }
01163
01164 #define JETPACK_TOGGLE_TIME 1000
01165 void Jetpack_Off(gentity_t *ent)
01166 {
01167 assert(ent && ent->client);
01168
01169 if (!ent->client->jetPackOn)
01170 {
01171 return;
01172 }
01173
01174 ent->client->jetPackOn = qfalse;
01175 }
01176
01177 void Jetpack_On(gentity_t *ent)
01178 {
01179 assert(ent && ent->client);
01180
01181 if (ent->client->jetPackOn)
01182 {
01183 return;
01184 }
01185
01186 if (ent->client->ps.fd.forceGripBeingGripped >= level.time)
01187 {
01188 return;
01189 }
01190
01191 if (ent->client->ps.fallingToDeath)
01192 {
01193 return;
01194 }
01195
01196 G_Sound(ent, CHAN_AUTO, G_SoundIndex("sound/boba/JETON"));
01197
01198 ent->client->jetPackOn = qtrue;
01199 }
01200
01201 void ItemUse_Jetpack( gentity_t *ent )
01202 {
01203 assert(ent && ent->client);
01204
01205 if (ent->client->jetPackToggleTime >= level.time)
01206 {
01207 return;
01208 }
01209
01210 if (ent->health <= 0 ||
01211 ent->client->ps.stats[STAT_HEALTH] <= 0 ||
01212 (ent->client->