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->ps.eFlags & EF_DEAD) ||
01213 ent->client->ps.pm_type == PM_DEAD)
01214 {
01215 return;
01216 }
01217
01218 if (!ent->client->jetPackOn &&
01219 ent->client->ps.jetpackFuel < 5)
01220 {
01221 return;
01222 }
01223
01224 if (ent->client->jetPackOn)
01225 {
01226 Jetpack_Off(ent);
01227 }
01228 else
01229 {
01230 Jetpack_On(ent);
01231 }
01232
01233 ent->client->jetPackToggleTime = level.time + JETPACK_TOGGLE_TIME;
01234 }
01235
01236 #define CLOAK_TOGGLE_TIME 1000
01237 extern void Jedi_Cloak( gentity_t *self );
01238 extern void Jedi_Decloak( gentity_t *self );
01239 void ItemUse_UseCloak( gentity_t *ent )
01240 {
01241 assert(ent && ent->client);
01242
01243 if (ent->client->cloakToggleTime >= level.time)
01244 {
01245 return;
01246 }
01247
01248 if (ent->health <= 0 ||
01249 ent->client->ps.stats[STAT_HEALTH] <= 0 ||
01250 (ent->client->ps.eFlags & EF_DEAD) ||
01251 ent->client->ps.pm_type == PM_DEAD)
01252 {
01253 return;
01254 }
01255
01256 if (!ent->client->ps.powerups[PW_CLOAKED] &&
01257 ent->client->ps.cloakFuel < 5)
01258 {
01259 return;
01260 }
01261
01262 if ( ent->client->ps.powerups[PW_CLOAKED] )
01263 {
01264 Jedi_Decloak( ent );
01265 }
01266 else
01267 {
01268 Jedi_Cloak( ent );
01269 }
01270
01271 ent->client->cloakToggleTime = level.time + CLOAK_TOGGLE_TIME;
01272 }
01273
01274 #define TOSSED_ITEM_STAY_PERIOD 20000
01275 #define TOSSED_ITEM_OWNER_NOTOUCH_DUR 1000
01276
01277 void SpecialItemThink(gentity_t *ent)
01278 {
01279 float gravity = 3.0f;
01280 float mass = 0.09f;
01281 float bounce = 1.1f;
01282
01283 if (ent->genericValue5 < level.time)
01284 {
01285 ent->think = G_FreeEntity;
01286 ent->nextthink = level.time;
01287 return;
01288 }
01289
01290 G_RunExPhys(ent, gravity, mass, bounce, qfalse, NULL, 0);
01291 VectorCopy(ent->r.currentOrigin, ent->s.origin);
01292 ent->nextthink = level.time + 50;
01293 }
01294
01295 void G_SpecialSpawnItem(gentity_t *ent, gitem_t *item)
01296 {
01297 RegisterItem( item );
01298 ent->item = item;
01299
01300
01301 ent->genericValue5 = level.time + TOSSED_ITEM_STAY_PERIOD;
01302 ent->think = SpecialItemThink;
01303 ent->nextthink = level.time + 50;
01304 ent->clipmask = MASK_SOLID;
01305
01306 ent->physicsBounce = 0.50;
01307 VectorSet (ent->r.mins, -8, -8, -0);
01308 VectorSet (ent->r.maxs, 8, 8, 16);
01309
01310 ent->s.eType = ET_ITEM;
01311 ent->s.modelindex = ent->item - bg_itemlist;
01312
01313 ent->r.contents = CONTENTS_TRIGGER;
01314 ent->touch = Touch_Item;
01315
01316
01317 ent->genericValue11 = ent->r.ownerNum;
01318 ent->genericValue10 = level.time + TOSSED_ITEM_OWNER_NOTOUCH_DUR;
01319
01320
01321 ent->genericValue9 = 1;
01322
01323
01324
01325
01326
01327 ent->s.brokenLimbs = 1;
01328
01329
01330 ent->s.eFlags |= EF_CLIENTSMOOTH;
01331 }
01332
01333 #define DISP_HEALTH_ITEM "item_medpak_instant"
01334 #define DISP_AMMO_ITEM "ammo_all"
01335
01336 void G_PrecacheDispensers(void)
01337 {
01338 gitem_t *item;
01339
01340 item = BG_FindItem(DISP_HEALTH_ITEM);
01341 if (item)
01342 {
01343 RegisterItem(item);
01344 }
01345
01346 item = BG_FindItem(DISP_AMMO_ITEM);
01347 if (item)
01348 {
01349 RegisterItem(item);
01350 }
01351 }
01352
01353 void ItemUse_UseDisp(gentity_t *ent, int type)
01354 {
01355 gitem_t *item = NULL;
01356 gentity_t *eItem;
01357
01358 if (!ent->client ||
01359 ent->client->tossableItemDebounce > level.time)
01360 {
01361 return;
01362 }
01363
01364 if (ent->client->ps.weaponTime > 0 ||
01365 ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
01366 {
01367 return;
01368 }
01369
01370 ent->client->tossableItemDebounce = level.time + TOSS_DEBOUNCE_TIME;
01371
01372 if (type == HI_HEALTHDISP)
01373 {
01374 item = BG_FindItem(DISP_HEALTH_ITEM);
01375 }
01376 else
01377 {
01378 item = BG_FindItem(DISP_AMMO_ITEM);
01379 }
01380
01381 if (item)
01382 {
01383 vec3_t fwd, pos;
01384 gentity_t *te;
01385
01386 eItem = G_Spawn();
01387 eItem->r.ownerNum = ent->s.number;
01388 eItem->classname = item->classname;
01389
01390 VectorCopy(ent->client->ps.origin, pos);
01391 pos[2] += ent->client->ps.viewheight;
01392
01393 G_SetOrigin(eItem, pos);
01394 VectorCopy(eItem->r.currentOrigin, eItem->s.origin);
01395 trap_LinkEntity(eItem);
01396
01397 G_SpecialSpawnItem(eItem, item);
01398
01399 AngleVectors(ent->client->ps.viewangles, fwd, NULL, NULL);
01400 VectorScale(fwd, 128.0f, eItem->epVelocity);
01401 eItem->epVelocity[2] = 16.0f;
01402
01403
01404
01405 te = G_TempEntity( ent->client->ps.origin, EV_LOCALTIMER );
01406 te->s.time = level.time;
01407 te->s.time2 = TOSS_DEBOUNCE_TIME;
01408 te->s.owner = ent->client->ps.clientNum;
01409 }
01410 }
01411
01412
01413
01414
01415
01416
01417 void EWebDisattach(gentity_t *owner, gentity_t *eweb)
01418 {
01419 owner->client->ewebIndex = 0;
01420 owner->client->ps.emplacedIndex = 0;
01421 if (owner->health > 0)
01422 {
01423 owner->client->ps.stats[STAT_WEAPONS] = eweb->genericValue11;
01424 }
01425 else
01426 {
01427 owner->client->ps.stats[STAT_WEAPONS] = 0;
01428 }
01429 eweb->think = G_FreeEntity;
01430 eweb->nextthink = level.time;
01431 }
01432
01433
01434 void EWebPrecache(void)
01435 {
01436 RegisterItem( BG_FindItemForWeapon(WP_TURRET) );
01437 G_EffectIndex("detpack/explosion.efx");
01438 G_EffectIndex("turret/muzzle_flash.efx");
01439 }
01440
01441
01442 #define EWEB_DEATH_RADIUS 128
01443 #define EWEB_DEATH_DMG 90
01444
01445 #include "../namespace_begin.h"
01446 extern void BG_CycleInven(playerState_t *ps, int direction);
01447 #include "../namespace_end.h"
01448
01449 void EWebDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
01450 {
01451 vec3_t fxDir;
01452
01453 G_RadiusDamage(self->r.currentOrigin, self, EWEB_DEATH_DMG, EWEB_DEATH_RADIUS, self, self, MOD_SUICIDE);
01454
01455 VectorSet(fxDir, 1.0f, 0.0f, 0.0f);
01456 G_PlayEffect(EFFECT_EXPLOSION_DETPACK, self->r.currentOrigin, fxDir);
01457
01458 if (self->r.ownerNum != ENTITYNUM_NONE)
01459 {
01460 gentity_t *owner = &g_entities[self->r.ownerNum];
01461
01462 if (owner->inuse && owner->client)
01463 {
01464 EWebDisattach(owner, self);
01465
01466
01467 owner->client->ewebHealth = -1;
01468
01469
01470 owner->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1<<HI_EWEB);
01471
01472 if (owner->client->ps.stats[STAT_HOLDABLE_ITEM] > 0 &&
01473 bg_itemlist[owner->client->ps.stats[STAT_HOLDABLE_ITEM]].giType == IT_HOLDABLE &&
01474 bg_itemlist[owner->client->ps.stats[STAT_HOLDABLE_ITEM]].giTag == HI_EWEB)
01475 {
01476 owner->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
01477 BG_CycleInven(&owner->client->ps, 1);
01478 }
01479 }
01480 }
01481 }
01482
01483
01484 void EWebPain(gentity_t *self, gentity_t *attacker, int damage)
01485 {
01486
01487 if (self->r.ownerNum != ENTITYNUM_NONE)
01488 {
01489 gentity_t *owner = &g_entities[self->r.ownerNum];
01490
01491 if (owner->inuse && owner->client)
01492 {
01493 owner->client->ewebHealth = self->health;
01494 }
01495 }
01496 }
01497
01498
01499 void EWeb_SetBoneAngles(gentity_t *ent, char *bone, vec3_t angles)
01500 {
01501 #ifdef _XBOX
01502 byte *thebone = &ent->s.boneIndex1;
01503 byte *firstFree = NULL;
01504 #else
01505 int *thebone = &ent->s.boneIndex1;
01506 int *firstFree = NULL;
01507 #endif
01508 int i = 0;
01509 int boneIndex = G_BoneIndex(bone);
01510 int flags, up, right, forward;
01511 vec3_t *boneVector = &ent->s.boneAngles1;
01512 vec3_t *freeBoneVec = NULL;
01513
01514 while (thebone)
01515 {
01516 if (!*thebone && !firstFree)
01517 {
01518 firstFree = thebone;
01519 freeBoneVec = boneVector;
01520 }
01521 else if (*thebone)
01522 {
01523 if (*thebone == boneIndex)
01524 {
01525 break;
01526 }
01527 }
01528
01529 switch (i)
01530 {
01531 case 0:
01532 thebone = &ent->s.boneIndex2;
01533 boneVector = &ent->s.boneAngles2;
01534 break;
01535 case 1:
01536 thebone = &ent->s.boneIndex3;
01537 boneVector = &ent->s.boneAngles3;
01538 break;
01539 case 2:
01540 thebone = &ent->s.boneIndex4;
01541 boneVector = &ent->s.boneAngles4;
01542 break;
01543 default:
01544 thebone = NULL;
01545 boneVector = NULL;
01546 break;
01547 }
01548
01549 i++;
01550 }
01551
01552 if (!thebone)
01553 {
01554 if (!firstFree)
01555 {
01556 Com_Printf("WARNING: E-Web has no free bone indexes\n");
01557 return;
01558 }
01559
01560 thebone = firstFree;
01561
01562 *thebone = boneIndex;
01563 boneVector = freeBoneVec;
01564 }
01565
01566
01567
01568
01569
01570 VectorCopy(angles, *boneVector);
01571
01572
01573
01574 if (!ent->ghoul2)
01575 {
01576 return;
01577 }
01578
01579 flags = BONE_ANGLES_POSTMULT;
01580 up = POSITIVE_Y;
01581 right = NEGATIVE_Z;
01582 forward = NEGATIVE_X;
01583
01584
01585 ent->s.boneOrient = ((forward)|(right<<3)|(up<<6));
01586
01587 trap_G2API_SetBoneAngles( ent->ghoul2,
01588 0,
01589 bone,
01590 angles,
01591 flags,
01592 up,
01593 right,
01594 forward,
01595 NULL,
01596 100,
01597 level.time );
01598 }
01599
01600
01601 void EWeb_SetBoneAnim(gentity_t *eweb, int startFrame, int endFrame)
01602 {
01603
01604 eweb->s.eFlags |= EF_G2ANIMATING;
01605
01606 if (eweb->s.torsoAnim == startFrame && eweb->s.legsAnim == endFrame)
01607 {
01608 eweb->s.torsoFlip = !eweb->s.torsoFlip;
01609 }
01610 else
01611 {
01612 eweb->s.torsoAnim = startFrame;
01613 eweb->s.legsAnim = endFrame;
01614 }
01615
01616
01617 assert(eweb->ghoul2);
01618 trap_G2API_SetBoneAnim(eweb->ghoul2, 0, "model_root", startFrame, endFrame,
01619 (BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND), 1.0f, level.time, -1, 100);
01620 }
01621
01622
01623 #define EWEB_MISSILE_DAMAGE 20
01624 void EWebFire(gentity_t *owner, gentity_t *eweb)
01625 {
01626 mdxaBone_t boltMatrix;
01627 gentity_t *missile;
01628 vec3_t p, d, bPoint;
01629
01630 if (eweb->genericValue10 == -1)
01631 {
01632 assert(!"Bad e-web bolt");
01633 return;
01634 }
01635
01636
01637 trap_G2API_GetBoltMatrix(eweb->ghoul2, 0, eweb->genericValue10, &boltMatrix, eweb->s.apos.trBase, eweb->r.currentOrigin, level.time, NULL, eweb->modelScale);
01638 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, p);
01639 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, d);
01640
01641
01642 VectorMA(p, -16.0f, d, bPoint);
01643
01644
01645 missile = CreateMissile( bPoint, d, 1200.0f, 10000, owner, qfalse );
01646
01647 missile->classname = "generic_proj";
01648 missile->s.weapon = WP_TURRET;
01649
01650 missile->damage = EWEB_MISSILE_DAMAGE;
01651 missile->dflags = DAMAGE_DEATH_KNOCKBACK;
01652 missile->methodOfDeath = MOD_TURBLAST;
01653 missile->clipmask = (MASK_SHOT|CONTENTS_LIGHTSABER);
01654
01655
01656 missile->passThroughNum = eweb->s.number+1;
01657
01658
01659 missile->bounceCount = 8;
01660
01661
01662 vectoangles(d, d);
01663 G_PlayEffectID(G_EffectIndex("turret/muzzle_flash.efx"), p, d);
01664 }
01665
01666
01667 void EWebPositionUser(gentity_t *owner, gentity_t *eweb)
01668 {
01669 mdxaBone_t boltMatrix;
01670 vec3_t p, d;
01671 trace_t tr;
01672
01673 trap_G2API_GetBoltMatrix(eweb->ghoul2, 0, eweb->genericValue9, &boltMatrix, eweb->s.apos.trBase, eweb->r.currentOrigin, level.time, NULL, eweb->modelScale);
01674 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, p);
01675 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_X, d);
01676
01677 VectorMA(p, 32.0f, d, p);
01678 p[2] = eweb->r.currentOrigin[2];
01679
01680 p[2] += 4.0f;
01681
01682 trap_Trace(&tr, owner->client->ps.origin, owner->r.mins, owner->r.maxs, p, owner->s.number, MASK_PLAYERSOLID);
01683
01684 if (!tr.startsolid && !tr.allsolid && tr.fraction == 1.0f)
01685 {
01686 vec3_t pDown;
01687
01688 VectorCopy(p, pDown);
01689 pDown[2] -= 7.0f;
01690 trap_Trace(&tr, p, owner->r.mins, owner->r.maxs, pDown, owner->s.number, MASK_PLAYERSOLID);
01691
01692 if (!tr.startsolid && !tr.allsolid)
01693 {
01694 VectorSubtract(owner->client->ps.origin, tr.endpos, d);
01695 if (VectorLength(d) > 1.0f)
01696 {
01697 vec3_t dAng;
01698 int aFlags = SETANIM_FLAG_HOLD;
01699
01700 vectoangles(d, dAng);
01701 dAng[YAW] = AngleSubtract(owner->client->ps.viewangles[YAW], dAng[YAW]);
01702 if (dAng[YAW] > 0.0f)
01703 {
01704 if (owner->client->ps.legsAnim == BOTH_STRAFE_RIGHT1)
01705 {
01706 aFlags |= SETANIM_FLAG_OVERRIDE;
01707 }
01708 G_SetAnim(owner, &owner->client->pers.cmd, SETANIM_LEGS, BOTH_STRAFE_LEFT1, aFlags, 0);
01709 }
01710 else
01711 {
01712 if (owner->client->ps.legsAnim == BOTH_STRAFE_LEFT1)
01713 {
01714 aFlags |= SETANIM_FLAG_OVERRIDE;
01715 }
01716 G_SetAnim(owner, &owner->client->pers.cmd, SETANIM_LEGS, BOTH_STRAFE_RIGHT1, aFlags, 0);
01717 }
01718 }
01719 else if (owner->client->ps.legsAnim == BOTH_STRAFE_RIGHT1 || owner->client->ps.legsAnim == BOTH_STRAFE_LEFT1)
01720 {
01721 owner->client->ps.legsTimer = 0;
01722 }
01723
01724 G_SetOrigin(owner, tr.endpos);
01725 VectorCopy(tr.endpos, owner->client->ps.origin);
01726 }
01727 }
01728 else
01729 {
01730 EWebDisattach(owner, eweb);
01731 }
01732 }
01733
01734
01735 void EWebUpdateBoneAngles(gentity_t *owner, gentity_t *eweb)
01736 {
01737 vec3_t yAng;
01738 float ideal;
01739 float incr;
01740 const float turnCap = 4.0f;
01741
01742 VectorClear(yAng);
01743 ideal = AngleSubtract(owner->client->ps.viewangles[YAW], eweb->s.angles[YAW]);
01744 incr = AngleSubtract(ideal, eweb->angle);
01745
01746 if (incr > turnCap)
01747 {
01748 incr = turnCap;
01749 }
01750 else if (incr < -turnCap)
01751 {
01752 incr = -turnCap;
01753 }
01754
01755 eweb->angle += incr;
01756
01757 yAng[0] = eweb->angle;
01758 EWeb_SetBoneAngles(eweb, "cannon_Yrot", yAng);
01759
01760 EWebPositionUser(owner, eweb);
01761 if (!owner->client->ewebIndex)
01762 {
01763 return;
01764 }
01765
01766 VectorClear(yAng);
01767 yAng[2] = AngleSubtract(owner->client->ps.viewangles[PITCH], eweb->s.angles[PITCH])*0.8f;
01768 EWeb_SetBoneAngles(eweb, "cannon_Xrot", yAng);
01769 }
01770
01771
01772 #include "../namespace_begin.h"
01773 extern int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
01774 #include "../namespace_end.h"
01775
01776 void EWebThink(gentity_t *self)
01777 {
01778 qboolean killMe = qfalse;
01779 const float gravity = 3.0f;
01780 const float mass = 0.09f;
01781 const float bounce = 1.1f;
01782
01783 if (self->r.ownerNum == ENTITYNUM_NONE)
01784 {
01785 killMe = qtrue;
01786 }
01787 else
01788 {
01789 gentity_t *owner = &g_entities[self->r.ownerNum];
01790
01791 if (!owner->inuse || !owner->client || owner->client->pers.connected != CON_CONNECTED ||
01792 owner->client->ewebIndex != self->s.number || owner->health < 1)
01793 {
01794 killMe = qtrue;
01795 }
01796 else if (owner->client->ps.emplacedIndex != self->s.number)
01797 {
01798 EWebDisattach(owner, self);
01799 return;
01800 }
01801
01802 if (!killMe)
01803 {
01804 float yaw;
01805
01806 if (BG_EmplacedView(owner->client->ps.viewangles, self->s.angles, &yaw, self->s.origin2[0]))
01807 {
01808 owner->client->ps.viewangles[YAW] = yaw;
01809 }
01810 owner->client->ps.weapon = WP_EMPLACED_GUN;
01811 owner->client->ps.stats[STAT_WEAPONS] = WP_EMPLACED_GUN;
01812
01813 if (self->genericValue8 < level.time)
01814 {
01815 EWebUpdateBoneAngles(owner, self);
01816 if (!owner->client->ewebIndex)
01817 {
01818 return;
01819 }
01820
01821 if (owner->client->pers.cmd.buttons & BUTTON_ATTACK)
01822 {
01823 if (self->genericValue5 < level.time)
01824 {
01825 EWebFire(owner, self);
01826
01827
01828 EWeb_SetBoneAnim(self, 2, 4);
01829 self->genericValue3 = 1;
01830
01831
01832 self->genericValue5 = level.time + 100;
01833 }
01834 }
01835 else if (self->genericValue5 < level.time && self->genericValue3)
01836 {
01837 EWeb_SetBoneAnim(self, 0, 1);
01838 self->genericValue3 = 0;
01839 }
01840 }
01841 }
01842 }
01843
01844 if (killMe)
01845 {
01846 EWebDie(self, self, self, 999, MOD_SUICIDE);
01847 return;
01848 }
01849
01850
01851 G_RunExPhys(self, gravity, mass, bounce, qfalse, NULL, 0);
01852
01853 self->nextthink = level.time;
01854 }
01855
01856 #define EWEB_HEALTH 200
01857
01858
01859 gentity_t *EWeb_Create(gentity_t *spawner)
01860 {
01861 const char *modelName = "models/map_objects/hoth/eweb_model.glm";
01862 int failSound = G_SoundIndex("sound/interface/shieldcon_empty");
01863 gentity_t *ent;
01864 trace_t tr;
01865 vec3_t fAng, fwd, pos, downPos, s;
01866 vec3_t mins, maxs;
01867
01868 VectorSet(mins, -32, -32, -24);
01869 VectorSet(maxs, 32, 32, 24);
01870
01871 VectorSet(fAng, 0, spawner->client->ps.viewangles[1], 0);
01872 AngleVectors(fAng, fwd, 0, 0);
01873
01874 VectorCopy(spawner->client->ps.origin, s);
01875
01876 s[2] += 12.0f;
01877
01878 VectorMA(s, 48.0f, fwd, pos);
01879
01880 trap_Trace(&tr, s, mins, maxs, pos, spawner->s.number, MASK_PLAYERSOLID);
01881
01882 if (tr.allsolid || tr.startsolid || tr.fraction != 1.0f)
01883 {
01884 G_Sound(spawner, CHAN_AUTO, failSound);
01885 return NULL;
01886 }
01887
01888 ent = G_Spawn();
01889
01890 ent->clipmask = MASK_PLAYERSOLID;
01891 ent->r.contents = MASK_PLAYERSOLID;
01892
01893 ent->physicsObject = qtrue;
01894
01895
01896 ent->s.weapon = WP_NONE;
01897
01898 VectorCopy(pos, downPos);
01899 downPos[2] -= 18.0f;
01900 trap_Trace(&tr, pos, mins, maxs, downPos, spawner->s.number, MASK_PLAYERSOLID);
01901
01902 if (tr.startsolid || tr.allsolid || tr.fraction == 1.0f || tr.entityNum < ENTITYNUM_WORLD)
01903 {
01904 G_FreeEntity(ent);
01905 G_Sound(spawner, CHAN_AUTO, failSound);
01906 return NULL;
01907 }
01908
01909 VectorCopy(tr.endpos, pos);
01910
01911 G_SetOrigin(ent, pos);
01912
01913 VectorCopy(fAng, ent->s.apos.trBase);
01914 VectorCopy(fAng, ent->r.currentAngles);
01915
01916 ent->s.owner = spawner->s.number;
01917 ent->s.teamowner = spawner->client->sess.sessionTeam;
01918
01919 ent->takedamage = qtrue;
01920
01921 if (spawner->client->ewebHealth <= 0)
01922 {
01923 spawner->client->ewebHealth = EWEB_HEALTH;
01924 }
01925
01926
01927 ent->maxHealth = EWEB_HEALTH;
01928 ent->health = spawner->client->ewebHealth;
01929 G_ScaleNetHealth(ent);
01930
01931 ent->die = EWebDie;
01932 ent->pain = EWebPain;
01933
01934 ent->think = EWebThink;
01935 ent->nextthink = level.time;
01936
01937
01938 ent->s.modelGhoul2 = 1;
01939 ent->s.g2radius = 128;
01940 ent->s.modelindex = G_ModelIndex((char *)modelName);
01941
01942 trap_G2API_InitGhoul2Model(&ent->ghoul2, modelName, 0, 0, 0, 0, 0);
01943
01944 if (!ent->ghoul2)
01945 {
01946 G_FreeEntity(ent);
01947 return NULL;
01948 }
01949
01950
01951 EWeb_SetBoneAngles(ent, "cannon_Yrot", vec3_origin);
01952 EWeb_SetBoneAngles(ent, "cannon_Xrot", vec3_origin);
01953
01954 ent->genericValue10 = trap_G2API_AddBolt(ent->ghoul2, 0, "*cannonflash");
01955 ent->genericValue9 = trap_G2API_AddBolt(ent->ghoul2, 0, "cannon_Yrot");
01956
01957
01958 ent->s.origin2[0] = 360.0f;
01959
01960 VectorCopy(fAng, ent->s.angles);
01961
01962
01963 ent->angle = 0.0f;
01964
01965 ent->r.ownerNum = spawner->s.number;
01966 trap_LinkEntity(ent);
01967
01968
01969 ent->genericValue11 = spawner->client->ps.stats[STAT_WEAPONS];
01970
01971
01972 EWeb_SetBoneAnim(ent, 4, 20);
01973
01974 ent->genericValue8 = level.time + 500;
01975
01976 VectorCopy(mins, ent->r.mins);
01977 VectorCopy(maxs, ent->r.maxs);
01978
01979 return ent;
01980 }
01981
01982 #define EWEB_USE_DEBOUNCE 1000
01983
01984 void ItemUse_UseEWeb(gentity_t *ent)
01985 {
01986 if (ent->client->ewebTime > level.time)
01987 {
01988 return;
01989 }
01990
01991 if (ent->client->ps.weaponTime > 0 ||
01992 ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
01993 {
01994 return;
01995 }
01996
01997 if (ent->client->ps.emplacedIndex && !ent->client->ewebIndex)
01998 {
01999 return;
02000 }
02001
02002 if (ent->client->ewebIndex)
02003 {
02004 EWebDisattach(ent, &g_entities[ent->client->ewebIndex]);
02005 }
02006 else
02007 {
02008 gentity_t *eweb = EWeb_Create(ent);
02009
02010 if (eweb)
02011 {
02012 ent->client->ewebIndex = eweb->s.number;
02013 ent->client->ps.emplacedIndex = eweb->s.number;
02014 }
02015 }
02016
02017 ent->client->ewebTime = level.time + EWEB_USE_DEBOUNCE;
02018 }
02019
02020
02021
02022
02023
02024 int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
02025 int quantity;
02026 int i;
02027 gclient_t *client;
02028
02029 if ( !other->client->ps.powerups[ent->item->giTag] ) {
02030
02031
02032 other->client->ps.powerups[ent->item->giTag] =
02033 level.time - ( level.time % 1000 );
02034
02035 G_LogWeaponPowerup(other->s.number, ent->item->giTag);
02036 }
02037
02038 if ( ent->count ) {
02039 quantity = ent->count;
02040 } else {
02041 quantity = ent->item->quantity;
02042 }
02043
02044 other->client->ps.powerups[ent->item->giTag] += quantity * 1000;
02045
02046 if (ent->item->giTag == PW_YSALAMIRI)
02047 {
02048 other->client->ps.powerups[PW_FORCE_ENLIGHTENED_LIGHT] = 0;
02049 other->client->ps.powerups[PW_FORCE_ENLIGHTENED_DARK] = 0;
02050 other->client->ps.powerups[PW_FORCE_BOON] = 0;
02051 }
02052
02053
02054 for ( i = 0 ; i < level.maxclients ; i++ ) {
02055 vec3_t delta;
02056 float len;
02057 vec3_t forward;
02058 trace_t tr;
02059
02060 client = &level.clients[i];
02061 if ( client == other->client ) {
02062 continue;
02063 }
02064 if ( client->pers.connected == CON_DISCONNECTED ) {
02065 continue;
02066 }
02067 if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
02068 continue;
02069 }
02070
02071
02072
02073 if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam ) {
02074 continue;
02075 }
02076
02077
02078 VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
02079 len = VectorNormalize( delta );
02080 if ( len > 192 ) {
02081 continue;
02082 }
02083
02084
02085 AngleVectors( client->ps.viewangles, forward, NULL, NULL );
02086 if ( DotProduct( delta, forward ) < 0.4 ) {
02087 continue;
02088 }
02089
02090
02091 trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
02092 if ( tr.fraction != 1.0 ) {
02093 continue;
02094 }
02095
02096
02097 client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD;
02098 }
02099 return RESPAWN_POWERUP;
02100 }
02101
02102
02103
02104 int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
02105
02106 other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist;
02107
02108 other->client->ps.stats[STAT_HOLDABLE_ITEMS] |= (1 << ent->item->giTag);
02109
02110 G_LogWeaponItem(other->s.number, ent->item->giTag);
02111
02112 return adjustRespawnTime(RESPAWN_HOLDABLE, ent->item->giType, ent->item->giTag);
02113 }
02114
02115
02116
02117
02118 void Add_Ammo (gentity_t *ent, int weapon, int count)
02119 {
02120 if ( ent->client->ps.ammo[weapon] < ammoData[weapon].max )
02121 {
02122 ent->client->ps.ammo[weapon] += count;
02123 if ( ent->client->ps.ammo[weapon] > ammoData[weapon].max )
02124 {
02125 ent->client->ps.ammo[weapon] = ammoData[weapon].max;
02126 }
02127 }
02128 }
02129
02130 int Pickup_Ammo (gentity_t *ent, gentity_t *other)
02131 {
02132 int quantity;
02133
02134 if ( ent->count ) {
02135 quantity = ent->count;
02136 } else {
02137 quantity = ent->item->quantity;
02138 }
02139
02140 if (ent->item->giTag == -1)
02141 {
02142 if ( g_gametype.integer == GT_SIEGE )
02143 {
02144 Add_Ammo(other, AMMO_BLASTER, 100);
02145 Add_Ammo(other, AMMO_POWERCELL, 100);
02146 Add_Ammo(other, AMMO_METAL_BOLTS, 100);
02147 Add_Ammo(other, AMMO_ROCKETS, 5);
02148 if (other->client->ps.stats[STAT_WEAPONS] & (1<<WP_DET_PACK))
02149 {
02150 Add_Ammo(other, AMMO_DETPACK, 2);
02151 }
02152 if (other->client->ps.stats[STAT_WEAPONS] & (1<<WP_THERMAL))
02153 {
02154 Add_Ammo(other, AMMO_THERMAL, 2);
02155 }
02156 if (other->client->ps.stats[STAT_WEAPONS] & (1<<WP_TRIP_MINE))
02157 {
02158 Add_Ammo(other, AMMO_TRIPMINE, 2);
02159 }
02160 }
02161 else
02162 {
02163 Add_Ammo(other, AMMO_BLASTER, 50);
02164 Add_Ammo(other, AMMO_POWERCELL, 50);
02165 Add_Ammo(other, AMMO_METAL_BOLTS, 50);
02166 Add_Ammo(other, AMMO_ROCKETS, 2);
02167 }
02168 }
02169 else
02170 {
02171 Add_Ammo (other, ent->item->giTag, quantity);
02172 }
02173
02174 return adjustRespawnTime(RESPAWN_AMMO, ent->item->giType, ent->item->giTag);
02175 }
02176
02177
02178
02179
02180 int Pickup_Weapon (gentity_t *ent, gentity_t *other) {
02181 int quantity;
02182
02183 if ( ent->count < 0 ) {
02184 quantity = 0;
02185 } else {
02186 if ( ent->count ) {
02187 quantity = ent->count;
02188 } else {
02189 quantity = ent->item->quantity;
02190 }
02191
02192
02193 if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) {
02194
02195
02196
02197
02198
02199 if ( other->client->ps.ammo[ ent->item->giTag ] < quantity*0.5 ) {
02200 quantity = quantity - other->client->ps.ammo[ ent->item->giTag ];
02201 } else {
02202 quantity = quantity*0.5;
02203 }
02204
02205
02206
02207
02208
02209
02210
02211
02212
02213
02214 }
02215 }
02216
02217
02218 other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
02219
02220
02221 Add_Ammo( other, weaponData[ent->item->giTag].ammoIndex, quantity );
02222
02223 G_LogWeaponPickup(other->s.number, ent->item->giTag);
02224
02225
02226 if ( g_gametype.integer == GT_TEAM )
02227 {
02228 return adjustRespawnTime(RESPAWN_TEAM_WEAPON, ent->item->giType, ent->item->giTag);
02229 }
02230
02231 return adjustRespawnTime(g_weaponRespawn.integer, ent->item->giType, ent->item->giTag);
02232 }
02233
02234
02235
02236
02237 int Pickup_Health (gentity_t *ent, gentity_t *other) {
02238 int max;
02239 int quantity;
02240
02241
02242 if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) {
02243 max = other->client->ps.stats[STAT_MAX_HEALTH];
02244 } else {
02245 max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
02246 }
02247
02248 if ( ent->count ) {
02249 quantity = ent->count;
02250 } else {
02251 quantity = ent->item->quantity;
02252 }
02253
02254 other->health += quantity;
02255
02256 if (other->health > max ) {
02257 other->health = max;
02258 }
02259 other->client->ps.stats[STAT_HEALTH] = other->health;
02260
02261 if ( ent->item->quantity == 100 ) {
02262 return RESPAWN_MEGAHEALTH;
02263 }
02264
02265 return adjustRespawnTime(RESPAWN_HEALTH, ent->item->giType, ent->item->giTag);
02266 }
02267
02268
02269
02270 int Pickup_Armor( gentity_t *ent, gentity_t *other )
02271 {
02272 other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
02273 if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * ent->item->giTag )
02274 {
02275 other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * ent->item->giTag;
02276 }
02277
02278 return adjustRespawnTime(RESPAWN_ARMOR, ent->item->giType, ent->item->giTag);
02279 }
02280
02281
02282
02283
02284
02285
02286
02287
02288 void RespawnItem( gentity_t *ent ) {
02289
02290 if (ent->team) {
02291 gentity_t *master;
02292 int count;
02293 int choice;
02294
02295 if ( !ent->teammaster ) {
02296 G_Error( "RespawnItem: bad teammaster");
02297 }
02298 master = ent->teammaster;
02299
02300 for (count = 0, ent = master; ent; ent = ent->teamchain, count++)
02301 ;
02302
02303 choice = rand() % count;
02304
02305 for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++)
02306 ;
02307 }
02308
02309 ent->r.contents = CONTENTS_TRIGGER;
02310
02311 ent->s.eFlags &= ~(EF_NODRAW | EF_ITEMPLACEHOLDER);
02312 ent->r.svFlags &= ~SVF_NOCLIENT;
02313 trap_LinkEntity (ent);
02314
02315 if ( ent->item->giType == IT_POWERUP ) {
02316
02317 gentity_t *te;
02318
02319
02320 if (ent->speed) {
02321 te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
02322 }
02323 else {
02324 te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
02325 }
02326 te->s.eventParm = G_SoundIndex( "sound/items/respawn1" );
02327 te->r.svFlags |= SVF_BROADCAST;
02328 }
02329
02330
02331 G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );
02332
02333 ent->nextthink = 0;
02334 }
02335
02336 qboolean CheckItemCanBePickedUpByNPC( gentity_t *item, gentity_t *pickerupper )
02337 {
02338 if ( (item->flags&FL_DROPPED_ITEM)
02339 && item->activator != &g_entities[0]
02340 && pickerupper->s.number
02341 && pickerupper->s.weapon == WP_NONE
02342 && pickerupper->enemy
02343 && pickerupper->painDebounceTime < level.time
02344 && pickerupper->NPC && pickerupper->NPC->surrenderTime < level.time
02345 && !(pickerupper->NPC->scriptFlags&SCF_FORCED_MARCH)
02346 )
02347 {
02348 if ( level.time - item->s.time < 3000 )
02349 {
02350 return qfalse;
02351 }
02352 return qtrue;
02353 }
02354 return qfalse;
02355 }
02356
02357
02358
02359
02360
02361
02362 void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) {
02363 int respawn;
02364 qboolean predict;
02365
02366 if (ent->genericValue10 > level.time &&
02367 other &&
02368 other->s.number == ent->genericValue11)
02369 {
02370 return;
02371 }
02372
02373 if (ent->s.eFlags & EF_ITEMPLACEHOLDER)
02374 {
02375 return;
02376 }
02377
02378 if (ent->s.eFlags & EF_NODRAW)
02379 {
02380 return;
02381 }
02382
02383 if (ent->item->giType == IT_WEAPON &&
02384 ent->s.powerups &&
02385 ent->s.powerups < level.time)
02386 {
02387 ent->s.generic1 = 0;
02388 ent->s.powerups = 0;
02389 }
02390
02391 if (!other->client)
02392 return;
02393 if (other->health < 1)
02394 return;
02395
02396 if (ent->item->giType == IT_POWERUP &&
02397 (ent->item->giTag == PW_FORCE_ENLIGHTENED_LIGHT || ent->item->giTag == PW_FORCE_ENLIGHTENED_DARK))
02398 {
02399 if (ent->item->giTag == PW_FORCE_ENLIGHTENED_LIGHT)
02400 {
02401 if (other->client->ps.fd.forceSide != FORCE_LIGHTSIDE)
02402 {
02403 return;
02404 }
02405 }
02406 else
02407 {
02408 if (other->client->ps.fd.forceSide != FORCE_DARKSIDE)
02409 {
02410 return;
02411 }
02412 }
02413 }
02414
02415
02416 if ( !BG_CanItemBeGrabbed( g_gametype.integer, &ent->s, &other->client->ps ) ) {
02417 return;
02418 }
02419
02420
02421 if ( other->client->NPC_class == CLASS_ATST ||
02422 other->client->NPC_class == CLASS_GONK ||
02423 other->client->NPC_class == CLASS_MARK1 ||
02424 other->client->NPC_class == CLASS_MARK2 ||
02425 other->client->NPC_class == CLASS_MOUSE ||
02426 other->client->NPC_class == CLASS_PROBE ||
02427 other->client->NPC_class == CLASS_PROTOCOL ||
02428 other->client->NPC_class == CLASS_R2D2 ||
02429 other->client->NPC_class == CLASS_R5D2 ||
02430 other->client->NPC_class == CLASS_SEEKER ||
02431 other->client->NPC_class == CLASS_REMOTE ||
02432 other->client->NPC_class == CLASS_RANCOR ||
02433 other->client->NPC_class == CLASS_WAMPA ||
02434
02435 other->client->NPC_class == CLASS_UGNAUGHT ||
02436 other->client->NPC_class == CLASS_SENTRY )
02437 {
02438
02439 return;
02440 }
02441
02442 if ( CheckItemCanBePickedUpByNPC( ent, other ) )
02443 {
02444 if ( other->NPC && other->NPC->goalEntity && other->NPC->goalEntity->enemy == ent )
02445 {
02446 other->NPC->goalEntity = NULL;
02447 other->NPC->squadState = SQUAD_STAND_AND_SHOOT;
02448 }
02449 }
02450 else if ( !(ent->spawnflags & ITMSF_ALLOWNPC) )
02451 {
02452 if ( other->s.eType == ET_NPC )
02453 {
02454 qboolean dontGo = qfalse;
02455 if (ent->item->giType == IT_AMMO &&
02456 ent->item->giTag == -1 &&
02457 other->s.NPC_class == CLASS_VEHICLE &&
02458 other->m_pVehicle &&
02459 other->m_pVehicle->m_pVehicleInfo->type == VH_WALKER)
02460 {
02461 if (other->maxHealth &&
02462 other->health < other->maxHealth)
02463 {
02464 other->health += 80;
02465 if (other->health > other->maxHealth)
02466 {
02467 other->health = other->maxHealth;
02468 }
02469 G_ScaleNetHealth(other);
02470 dontGo = qtrue;
02471 }
02472 }
02473
02474 if (!dontGo)
02475 {
02476 return;
02477 }
02478 }
02479 }
02480
02481 G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname );
02482
02483 predict = other->client->pers.predictItemPickup;
02484
02485
02486 switch( ent->item->giType ) {
02487 case IT_WEAPON:
02488 respawn = Pickup_Weapon(ent, other);
02489
02490 predict = qtrue;
02491 break;
02492 case IT_AMMO:
02493 respawn = Pickup_Ammo(ent, other);
02494 if (ent->item->giTag == AMMO_THERMAL || ent->item->giTag == AMMO_TRIPMINE || ent->item->giTag == AMMO_DETPACK)
02495 {
02496 int weapForAmmo = 0;
02497
02498 if (ent->item->giTag == AMMO_THERMAL)
02499 {
02500 weapForAmmo = WP_THERMAL;
02501 }
02502 else if (ent->item->giTag == AMMO_TRIPMINE)
02503 {
02504 weapForAmmo = WP_TRIP_MINE;
02505 }
02506 else
02507 {
02508 weapForAmmo = WP_DET_PACK;
02509 }
02510
02511 if (other && other->client && other->client->ps.ammo[weaponData[weapForAmmo].ammoIndex] > 0 )
02512 {
02513 other->client->ps.stats[STAT_WEAPONS] |= (1 << weapForAmmo);
02514 }
02515 }
02516
02517 predict = qtrue;
02518 break;
02519 case IT_ARMOR:
02520 respawn = Pickup_Armor(ent, other);
02521
02522 predict = qtrue;
02523 break;
02524 case IT_HEALTH:
02525 respawn = Pickup_Health(ent, other);
02526
02527 predict = qtrue;
02528 break;
02529 case IT_POWERUP:
02530 respawn = Pickup_Powerup(ent, other);
02531 predict = qfalse;
02532
02533 break;
02534 case IT_TEAM:
02535 respawn = Pickup_Team(ent, other);
02536 break;
02537 case IT_HOLDABLE:
02538 respawn = Pickup_Holdable(ent, other);
02539 break;
02540 default:
02541 return;
02542 }
02543
02544 if ( !respawn ) {
02545 return;
02546 }
02547
02548
02549 if (predict) {
02550 if (other->client)
02551 {
02552 BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, ent->s.number, &other->client->ps);
02553 }
02554 else
02555 {
02556 G_AddPredictableEvent( other, EV_ITEM_PICKUP, ent->s.number );
02557 }
02558 } else {
02559 G_AddEvent( other, EV_ITEM_PICKUP, ent->s.number );
02560 }
02561
02562
02563 if ( ent->item->giType == IT_TEAM) {
02564
02565 if (!ent->speed) {
02566 gentity_t *te;
02567
02568 te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
02569 te->s.eventParm = ent->s.modelindex;
02570 te->r.svFlags |= SVF_BROADCAST;
02571 } else {
02572 gentity_t *te;
02573
02574 te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
02575 te->s.eventParm = ent->s.modelindex;
02576
02577 te->r.svFlags |= SVF_SINGLECLIENT;
02578 te->r.singleClient = other->s.number;
02579 }
02580 }
02581
02582
02583 G_UseTargets (ent, other);
02584
02585
02586 if ( ent->wait == -1 ) {
02587 ent->r.svFlags |= SVF_NOCLIENT;
02588 ent->s.eFlags |= EF_NODRAW;
02589 ent->r.contents = 0;
02590 ent->unlinkAfterEvent = qtrue;
02591 return;
02592 }
02593
02594
02595 if ( ent->wait ) {
02596 respawn = ent->wait;
02597 }
02598
02599
02600 if ( ent->random ) {
02601 respawn += crandom() * ent->random;
02602 if ( respawn < 1 ) {
02603 respawn = 1;
02604 }
02605 }
02606
02607
02608 if ( ent->flags & FL_DROPPED_ITEM ) {
02609 ent->freeAfterEvent = qtrue;
02610 }
02611
02612
02613
02614
02615 if (!(ent->flags & FL_DROPPED_ITEM) && (ent->item->giType==IT_WEAPON || ent->item->giType==IT_POWERUP))
02616 {
02617 ent->s.eFlags |= EF_ITEMPLACEHOLDER;
02618 ent->s.eFlags &= ~EF_NODRAW;
02619 }
02620 else
02621 {
02622 ent->s.eFlags |= EF_NODRAW;
02623 ent->r.svFlags |= SVF_NOCLIENT;
02624 }
02625 ent->r.contents = 0;
02626
02627 if (ent->genericValue9)
02628 {
02629 ent->think = G_FreeEntity;
02630 ent->nextthink = level.time;
02631 return;
02632 }
02633
02634
02635
02636
02637
02638 if ( respawn <= 0 ) {
02639 ent->nextthink = 0;
02640 ent->think = 0;
02641 } else {
02642 ent->nextthink = level.time + respawn * 1000;
02643 ent->think = RespawnItem;
02644 }
02645 trap_LinkEntity( ent );
02646 }
02647
02648
02649
02650
02651
02652
02653
02654
02655
02656
02657
02658 gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
02659 gentity_t *dropped;
02660
02661 dropped = G_Spawn();
02662
02663 dropped->s.eType = ET_ITEM;
02664 dropped->s.modelindex = item - bg_itemlist;
02665 if (dropped->s.modelindex < 0)
02666 {
02667 dropped->s.modelindex = 0;
02668 }
02669 dropped->s.modelindex2 = 1;
02670
02671 dropped->classname = item->classname;
02672 dropped->item = item;
02673 VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
02674 VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
02675
02676 dropped->r.contents = CONTENTS_TRIGGER;
02677
02678 dropped->touch = Touch_Item;
02679
02680 G_SetOrigin( dropped, origin );
02681 dropped->s.pos.trType = TR_GRAVITY;
02682 dropped->s.pos.trTime = level.time;
02683 VectorCopy( velocity, dropped->s.pos.trDelta );
02684
02685 dropped->flags |= FL_BOUNCE_HALF;
02686 if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) && item->giType == IT_TEAM) {
02687 dropped->think = Team_DroppedFlagThink;
02688 dropped->nextthink = level.time + 30000;
02689 Team_CheckDroppedItem( dropped );
02690
02691
02692 if (strcmp(dropped->classname, "team_CTF_redflag") == 0)
02693 {
02694 droppedRedFlag = dropped;
02695 }
02696 else if (strcmp(dropped->classname, "team_CTF_blueflag") == 0)
02697 {
02698 droppedBlueFlag = dropped;
02699 }
02700 } else {
02701 dropped->think = G_FreeEntity;
02702 dropped->nextthink = level.time + 30000;
02703 }
02704
02705 dropped->flags = FL_DROPPED_ITEM;
02706
02707 if (item->giType == IT_WEAPON || item->giType == IT_POWERUP)
02708 {
02709 dropped->s.eFlags |= EF_DROPPEDWEAPON;
02710 }
02711
02712 vectoangles(velocity, dropped->s.angles);
02713 dropped->s.angles[PITCH] = 0;
02714
02715 if (item->giTag == WP_TRIP_MINE ||
02716 item->giTag == WP_DET_PACK)
02717 {
02718 dropped->s.angles[PITCH] = -90;
02719 }
02720
02721 if (item->giTag != WP_BOWCASTER &&
02722 item->giTag != WP_DET_PACK &&
02723 item->giTag != WP_THERMAL)
02724 {
02725 dropped->s.angles[ROLL] = -90;
02726 }
02727
02728 dropped->physicsObject = qtrue;
02729
02730 trap_LinkEntity (dropped);
02731
02732 return dropped;
02733 }
02734
02735
02736
02737
02738
02739
02740
02741
02742 gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) {
02743 vec3_t velocity;
02744 vec3_t angles;
02745
02746 VectorCopy( ent->s.apos.trBase, angles );
02747 angles[YAW] += angle;
02748 angles[PITCH] = 0;
02749
02750 AngleVectors( angles, velocity, NULL, NULL );
02751 VectorScale( velocity, 150, velocity );
02752 velocity[2] += 200 + crandom() * 50;
02753
02754 return LaunchItem( item, ent->s.pos.trBase, velocity );
02755 }
02756
02757
02758
02759
02760
02761
02762
02763
02764
02765 void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
02766 RespawnItem( ent );
02767 }
02768
02769
02770
02771
02772
02773
02774
02775
02776
02777
02778
02779 void FinishSpawningItem( gentity_t *ent ) {
02780 trace_t tr;
02781 vec3_t dest;
02782
02783
02784
02785
02786
02787 if (g_gametype.integer == GT_SIEGE)
02788 {
02789 if (ent->item->giType == IT_POWERUP)
02790 {
02791 G_FreeEntity(ent);
02792 return;
02793 }
02794 }
02795
02796 if (g_gametype.integer != GT_JEDIMASTER)
02797 {
02798 if (HasSetSaberOnly())
02799 {
02800 if (ent->item->giType == IT_AMMO)
02801 {
02802 G_FreeEntity( ent );
02803 return;
02804 }
02805
02806 if (ent->item->giType == IT_HOLDABLE)
02807 {
02808 if (ent->item->giTag == HI_SEEKER ||
02809 ent->item->giTag == HI_SHIELD ||
02810 ent->item->giTag == HI_SENTRY_GUN)
02811 {
02812 G_FreeEntity( ent );
02813 return;
02814 }
02815 }
02816 }
02817 }
02818 else
02819 {
02820 if (ent->item->giType == IT_POWERUP)
02821 {
02822 G_FreeEntity(ent);
02823 return;
02824 }
02825 }
02826
02827 if (g_gametype.integer == GT_HOLOCRON)
02828 {
02829 if (ent->item->giType == IT_POWERUP)
02830 {
02831 if (ent->item->giTag == PW_FORCE_ENLIGHTENED_LIGHT ||
02832 ent->item->giTag == PW_FORCE_ENLIGHTENED_DARK)
02833 {
02834 G_FreeEntity(ent);
02835 return;
02836 }
02837 }
02838 }
02839
02840 if (g_forcePowerDisable.integer)
02841 {
02842 if (ent->item->giType == IT_POWERUP)
02843 {
02844 if (ent->item->giTag == PW_FORCE_ENLIGHTENED_LIGHT ||
02845 ent->item->giTag == PW_FORCE_ENLIGHTENED_DARK ||
02846 ent->item->giTag == PW_FORCE_BOON)
02847 {
02848 G_FreeEntity(ent);
02849 return;
02850 }
02851 }
02852 }
02853
02854 if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
02855 {
02856 if ( ent->item->giType == IT_ARMOR ||
02857 ent->item->giType == IT_HEALTH ||
02858 (ent->item->giType == IT_HOLDABLE && (ent->item->giTag == HI_MEDPAC || ent->item->giTag == HI_MEDPAC_BIG)) )
02859 {
02860 G_FreeEntity(ent);
02861 return;
02862 }
02863 }
02864
02865 if (g_gametype.integer != GT_CTF &&
02866 g_gametype.integer != GT_CTY &&
02867 ent->item->giType == IT_TEAM)
02868 {
02869 int killMe = 0;
02870
02871 switch (ent->item->giTag)
02872 {
02873 case PW_REDFLAG:
02874 killMe = 1;
02875 break;
02876 case PW_BLUEFLAG:
02877 killMe = 1;
02878 break;
02879 case PW_NEUTRALFLAG:
02880 killMe = 1;
02881 break;
02882 default:
02883 break;
02884 }
02885
02886 if (killMe)
02887 {
02888 G_FreeEntity( ent );
02889 return;
02890 }
02891 }
02892
02893 VectorSet (ent->r.mins, -8, -8, -0);
02894 VectorSet (ent->r.maxs, 8, 8, 16);
02895
02896 ent->s.eType = ET_ITEM;
02897 ent->s.modelindex = ent->item - bg_itemlist;
02898 ent->s.modelindex2 = 0;
02899
02900 ent->r.contents = CONTENTS_TRIGGER;
02901 ent->touch = Touch_Item;
02902
02903 ent->use = Use_Item;
02904
02905
02906
02907
02908
02909
02910
02911
02912
02913 if ( ent->spawnflags & ITMSF_SUSPEND ) {
02914
02915 G_SetOrigin( ent, ent->s.origin );
02916 } else {
02917
02918
02919
02920
02921 ent->s.origin[2] += 0.1;
02922 ent->r.maxs[2] -= 0.1;
02923
02924 VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
02925 trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
02926 if ( tr.startsolid ) {
02927 G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
02928 G_FreeEntity( ent );
02929 return;
02930 }
02931
02932
02933 ent->r.maxs[2] += 0.1;
02934
02935
02936 ent->s.groundEntityNum = tr.entityNum;
02937
02938 G_SetOrigin( ent, tr.endpos );
02939 }
02940
02941
02942 if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
02943 ent->s.eFlags |= EF_NODRAW;
02944 ent->r.contents = 0;
02945 return;
02946 }
02947
02948
02949
02950
02951
02952
02953
02954
02955
02956
02957
02958
02959
02960
02961
02962 trap_LinkEntity (ent);
02963 }
02964
02965
02966 qboolean itemRegistered[MAX_ITEMS];
02967
02968
02969
02970
02971
02972
02973 void G_CheckTeamItems( void ) {
02974
02975
02976 Team_InitGame();
02977
02978 if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY ) {
02979 gitem_t *item;
02980
02981
02982 item = BG_FindItem( "team_CTF_redflag" );
02983 if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
02984 G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
02985 }
02986 item = BG_FindItem( "team_CTF_blueflag" );
02987 if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
02988 G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
02989 }
02990 }
02991 }
02992
02993
02994
02995
02996
02997
02998 void ClearRegisteredItems( void ) {
02999 memset( itemRegistered, 0, sizeof( itemRegistered ) );
03000
03001
03002 RegisterItem( BG_FindItemForWeapon( WP_BRYAR_PISTOL ) );
03003 RegisterItem( BG_FindItemForWeapon( WP_STUN_BATON ) );
03004 RegisterItem( BG_FindItemForWeapon( WP_MELEE ) );
03005 RegisterItem( BG_FindItemForWeapon( WP_SABER ) );
03006
03007 if (g_gametype.integer == GT_SIEGE)
03008 {
03009 G_PrecacheDispensers();
03010 }
03011 }
03012
03013
03014
03015
03016
03017
03018
03019
03020 void RegisterItem( gitem_t *item ) {
03021 if ( !item ) {
03022 G_Error( "RegisterItem: NULL" );
03023 }
03024 itemRegistered[ item - bg_itemlist ] = qtrue;
03025 }
03026
03027
03028
03029
03030
03031
03032
03033
03034
03035
03036 void SaveRegisteredItems( void ) {
03037 char string[MAX_ITEMS+1];
03038 int i;
03039 int count;
03040
03041 count = 0;
03042 for ( i = 0 ; i < bg_numItems ; i++ ) {
03043 if ( itemRegistered[i] ) {
03044 count++;
03045 string[i] = '1';
03046 } else {
03047 string[i] = '0';
03048 }
03049 }
03050 string[ bg_numItems ] = 0;
03051
03052
03053 trap_SetConfigstring(CS_ITEMS, string);
03054 }
03055
03056
03057
03058
03059
03060
03061 int G_ItemDisabled( gitem_t *item ) {
03062
03063 char name[128];
03064
03065 Com_sprintf(name, sizeof(name), "disable_%s", item->classname);
03066 return trap_Cvar_VariableIntegerValue( name );
03067 }
03068
03069
03070
03071
03072
03073
03074
03075
03076
03077
03078
03079 void G_SpawnItem (gentity_t *ent, gitem_t *item) {
03080 int wDisable = 0;
03081
03082 G_SpawnFloat( "random", "0", &ent->random );
03083 G_SpawnFloat( "wait", "0", &ent->wait );
03084
03085 if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
03086 {
03087 wDisable = g_duelWeaponDisable.integer;
03088 }
03089 else
03090 {
03091 wDisable = g_weaponDisable.integer;
03092 }
03093
03094 if (item->giType == IT_WEAPON &&
03095 wDisable &&
03096 (wDisable & (1 << item->giTag)))
03097 {
03098 if (g_gametype.integer != GT_JEDIMASTER)
03099 {
03100 G_FreeEntity( ent );
03101 return;
03102 }
03103 }
03104
03105 RegisterItem( item );
03106 if ( G_ItemDisabled(item) )
03107 return;
03108
03109 ent->item = item;
03110
03111
03112 ent->nextthink = level.time + FRAMETIME * 2;
03113 ent->think = FinishSpawningItem;
03114
03115 ent->physicsBounce = 0.50;
03116
03117 if ( item->giType == IT_POWERUP ) {
03118 G_SoundIndex( "sound/items/respawn1" );
03119 G_SpawnFloat( "noglobalsound", "0", &ent->speed);
03120 }
03121 }
03122
03123
03124
03125
03126
03127
03128
03129
03130 void G_BounceItem( gentity_t *ent, trace_t *trace ) {
03131 vec3_t velocity;
03132 float dot;
03133 int hitTime;
03134
03135
03136 hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
03137 BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
03138 dot = DotProduct( velocity, trace->plane.normal );
03139 VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
03140
03141
03142 VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
03143
03144 if ((ent->s.weapon == WP_DET_PACK && ent->s.eType == ET_GENERAL && ent->physicsObject))
03145 {
03146 if (ent->touch)
03147 {
03148 ent->touch(ent, &g_entities[trace->entityNum], trace);
03149 return;
03150 }
03151 }
03152
03153
03154 if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
03155 trace->endpos[2] += 1.0;
03156 SnapVector( trace->endpos );
03157 G_SetOrigin( ent, trace->endpos );
03158 ent->s.groundEntityNum = trace->entityNum;
03159 return;
03160 }
03161
03162 VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
03163 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
03164 ent->s.pos.trTime = level.time;
03165
03166 if (ent->s.eType == ET_HOLOCRON ||
03167 (ent->s.shouldtarget && ent->s.eType == ET_GENERAL && ent->physicsObject))
03168 {
03169 if (ent->touch)
03170 {
03171 ent->touch(ent, &g_entities[trace->entityNum], trace);
03172 }
03173 }
03174 }
03175
03176
03177
03178
03179
03180
03181
03182
03183 void G_RunItem( gentity_t *ent ) {
03184 vec3_t origin;
03185 trace_t tr;
03186 int contents;
03187 int mask;
03188
03189
03190 if ( ent->s.groundEntityNum == -1 ) {
03191 if ( ent->s.pos.trType != TR_GRAVITY ) {
03192 ent->s.pos.trType = TR_GRAVITY;
03193 ent->s.pos.trTime = level.time;
03194 }
03195 }
03196
03197 if ( ent->s.pos.trType == TR_STATIONARY ) {
03198
03199 G_RunThink( ent );
03200 return;
03201 }
03202
03203
03204 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
03205
03206
03207 if ( ent->clipmask ) {
03208 mask = ent->clipmask;
03209 } else {
03210 mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
03211 }
03212 trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
03213 ent->r.ownerNum, mask );
03214
03215 VectorCopy( tr.endpos, ent->r.currentOrigin );
03216
03217 if ( tr.startsolid ) {
03218 tr.fraction = 0;
03219 }
03220
03221 trap_LinkEntity( ent );
03222
03223
03224 G_RunThink( ent );
03225
03226 if ( tr.fraction == 1 ) {
03227 return;
03228 }
03229
03230
03231 contents = trap_PointContents( ent->r.currentOrigin, -1 );
03232 if ( contents & CONTENTS_NODROP ) {
03233 if (ent->item && ent->item->giType == IT_TEAM) {
03234 Team_FreeEntity(ent);
03235 } else {
03236 G_FreeEntity( ent );
03237 }
03238 return;
03239 }
03240
03241 G_BounceItem( ent, &tr );
03242 }
03243