00001
00002
00003
00004
00005 #include "g_local.h"
00006 #include "bg_saga.h"
00007 #include "q_shared.h"
00008
00009 typedef struct {
00010 char oldShader[MAX_QPATH];
00011 char newShader[MAX_QPATH];
00012 float timeOffset;
00013 } shaderRemap_t;
00014
00015 #define MAX_SHADER_REMAPS 128
00016
00017 int remapCount = 0;
00018 shaderRemap_t remappedShaders[MAX_SHADER_REMAPS];
00019
00020 void AddRemap(const char *oldShader, const char *newShader, float timeOffset) {
00021 int i;
00022
00023 for (i = 0; i < remapCount; i++) {
00024 if (Q_stricmp(oldShader, remappedShaders[i].oldShader) == 0) {
00025
00026 strcpy(remappedShaders[i].newShader,newShader);
00027 remappedShaders[i].timeOffset = timeOffset;
00028 return;
00029 }
00030 }
00031 if (remapCount < MAX_SHADER_REMAPS) {
00032 strcpy(remappedShaders[remapCount].newShader,newShader);
00033 strcpy(remappedShaders[remapCount].oldShader,oldShader);
00034 remappedShaders[remapCount].timeOffset = timeOffset;
00035 remapCount++;
00036 }
00037 }
00038
00039 const char *BuildShaderStateConfig(void) {
00040 static char buff[MAX_STRING_CHARS*4];
00041 char out[(MAX_QPATH * 2) + 5];
00042 int i;
00043
00044 memset(buff, 0, MAX_STRING_CHARS);
00045 for (i = 0; i < remapCount; i++) {
00046 Com_sprintf(out, (MAX_QPATH * 2) + 5, "%s=%s:%5.2f@", remappedShaders[i].oldShader, remappedShaders[i].newShader, remappedShaders[i].timeOffset);
00047 Q_strcat( buff, sizeof( buff ), out);
00048 }
00049 return buff;
00050 }
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 static int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create ) {
00067 int i;
00068 char s[MAX_STRING_CHARS];
00069
00070 if ( !name || !name[0] ) {
00071 return 0;
00072 }
00073
00074 for ( i=1 ; i<max ; i++ ) {
00075 trap_GetConfigstring( start + i, s, sizeof( s ) );
00076 if ( !s[0] ) {
00077 break;
00078 }
00079 if ( !strcmp( s, name ) ) {
00080 return i;
00081 }
00082 }
00083
00084 if ( !create ) {
00085 return 0;
00086 }
00087
00088 if ( i == max ) {
00089 G_Error( "G_FindConfigstringIndex: overflow" );
00090 }
00091
00092 trap_SetConfigstring( start + i, name );
00093
00094 return i;
00095 }
00096
00097
00098
00099
00100
00101 int G_BoneIndex( const char *name ) {
00102 return G_FindConfigstringIndex (name, CS_G2BONES, MAX_G2BONES, qtrue);
00103 }
00104
00105
00106
00107
00108 int G_ModelIndex( const char *name ) {
00109 #ifdef _DEBUG_MODEL_PATH_ON_SERVER
00110
00111
00112 fileHandle_t fh;
00113
00114 trap_FS_FOpenFile(name, &fh, FS_READ);
00115 if (!fh)
00116 {
00117 trap_FS_FOpenFile(va("models/%s", name), &fh, FS_READ);
00118 if (!fh)
00119 {
00120 Com_Printf("ERROR: Server tried to modelindex %s but it doesn't exist.\n", name);
00121 }
00122 }
00123
00124 if (fh)
00125 {
00126 trap_FS_FCloseFile(fh);
00127 }
00128 #endif
00129 return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
00130 }
00131
00132 int G_IconIndex( const char* name )
00133 {
00134 assert(name && name[0]);
00135 return G_FindConfigstringIndex (name, CS_ICONS, MAX_ICONS, qtrue);
00136 }
00137
00138 int G_SoundIndex( const char *name ) {
00139 assert(name && name[0]);
00140 return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue);
00141 }
00142
00143 int G_SoundSetIndex(const char *name)
00144 {
00145 return G_FindConfigstringIndex (name, CS_AMBIENT_SET, MAX_AMBIENT_SETS, qtrue);
00146 }
00147
00148 int G_EffectIndex( const char *name )
00149 {
00150 return G_FindConfigstringIndex (name, CS_EFFECTS, MAX_FX, qtrue);
00151 }
00152
00153 int G_BSPIndex( const char *name )
00154 {
00155 return G_FindConfigstringIndex (name, CS_BSP_MODELS, MAX_SUB_BSP, qtrue);
00156 }
00157
00158
00159
00160
00161
00162 qboolean G_PlayerHasCustomSkeleton(gentity_t *ent)
00163 {
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187 return qfalse;
00188 }
00189
00190
00191
00192
00193
00194
00195
00196
00197 void G_TeamCommand( team_t team, char *cmd ) {
00198 int i;
00199
00200 for ( i = 0 ; i < level.maxclients ; i++ ) {
00201 if ( level.clients[i].pers.connected == CON_CONNECTED ) {
00202 if ( level.clients[i].sess.sessionTeam == team ) {
00203 trap_SendServerCommand( i, va("%s", cmd ));
00204 }
00205 }
00206 }
00207 }
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222 gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
00223 {
00224 char *s;
00225
00226 if (!from)
00227 from = g_entities;
00228 else
00229 from++;
00230
00231 for ( ; from < &g_entities[level.num_entities] ; from++)
00232 {
00233 if (!from->inuse)
00234 continue;
00235 s = *(char **) ((byte *)from + fieldofs);
00236 if (!s)
00237 continue;
00238 if (!Q_stricmp (s, match))
00239 return from;
00240 }
00241
00242 return NULL;
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252 int G_RadiusList ( vec3_t origin, float radius, gentity_t *ignore, qboolean takeDamage, gentity_t *ent_list[MAX_GENTITIES])
00253 {
00254 float dist;
00255 gentity_t *ent;
00256 int entityList[MAX_GENTITIES];
00257 int numListedEntities;
00258 vec3_t mins, maxs;
00259 vec3_t v;
00260 int i, e;
00261 int ent_count = 0;
00262
00263 if ( radius < 1 )
00264 {
00265 radius = 1;
00266 }
00267
00268 for ( i = 0 ; i < 3 ; i++ )
00269 {
00270 mins[i] = origin[i] - radius;
00271 maxs[i] = origin[i] + radius;
00272 }
00273
00274 numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
00275
00276 for ( e = 0 ; e < numListedEntities ; e++ )
00277 {
00278 ent = &g_entities[entityList[ e ]];
00279
00280 if ((ent == ignore) || !(ent->inuse) || ent->takedamage != takeDamage)
00281 continue;
00282
00283
00284 for ( i = 0 ; i < 3 ; i++ )
00285 {
00286 if ( origin[i] < ent->r.absmin[i] )
00287 {
00288 v[i] = ent->r.absmin[i] - origin[i];
00289 } else if ( origin[i] > ent->r.absmax[i] )
00290 {
00291 v[i] = origin[i] - ent->r.absmax[i];
00292 } else
00293 {
00294 v[i] = 0;
00295 }
00296 }
00297
00298 dist = VectorLength( v );
00299 if ( dist >= radius )
00300 {
00301 continue;
00302 }
00303
00304
00305 ent_list[ent_count] = ent;
00306 ent_count++;
00307
00308 }
00309
00310 return(ent_count);
00311 }
00312
00313
00314
00315 void G_Throw( gentity_t *targ, vec3_t newDir, float push )
00316
00317 {
00318 vec3_t kvel;
00319 float mass;
00320
00321 if ( targ->physicsBounce > 0 )
00322 {
00323 mass = targ->physicsBounce;
00324 }
00325 else
00326 {
00327 mass = 200;
00328 }
00329
00330 if ( g_gravity.value > 0 )
00331 {
00332 VectorScale( newDir, g_knockback.value * (float)push / mass * 0.8, kvel );
00333 kvel[2] = newDir[2] * g_knockback.value * (float)push / mass * 1.5;
00334 }
00335 else
00336 {
00337 VectorScale( newDir, g_knockback.value * (float)push / mass, kvel );
00338 }
00339
00340 if ( targ->client )
00341 {
00342 VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );
00343 }
00344 else if ( targ->s.pos.trType != TR_STATIONARY && targ->s.pos.trType != TR_LINEAR_STOP && targ->s.pos.trType != TR_NONLINEAR_STOP )
00345 {
00346 VectorAdd( targ->s.pos.trDelta, kvel, targ->s.pos.trDelta );
00347 VectorCopy( targ->r.currentOrigin, targ->s.pos.trBase );
00348 targ->s.pos.trTime = level.time;
00349 }
00350
00351
00352
00353 if ( targ->client && !targ->client->ps.pm_time )
00354 {
00355 int t;
00356
00357 t = push * 2;
00358
00359 if ( t < 50 )
00360 {
00361 t = 50;
00362 }
00363 if ( t > 200 )
00364 {
00365 t = 200;
00366 }
00367 targ->client->ps.pm_time = t;
00368 targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
00369 }
00370 }
00371
00372
00373
00374
00375
00376 void G_FreeFakeClient(gclient_t **cl)
00377 {
00378
00379
00380
00381 }
00382
00383
00384 #define MAX_VEHICLES_AT_A_TIME 128
00385 static Vehicle_t g_vehiclePool[MAX_VEHICLES_AT_A_TIME];
00386 static qboolean g_vehiclePoolOccupied[MAX_VEHICLES_AT_A_TIME];
00387 static qboolean g_vehiclePoolInit = qfalse;
00388 void G_AllocateVehicleObject(Vehicle_t **pVeh)
00389 {
00390 int i = 0;
00391
00392 if (!g_vehiclePoolInit)
00393 {
00394 g_vehiclePoolInit = qtrue;
00395 memset(g_vehiclePoolOccupied, 0, sizeof(g_vehiclePoolOccupied));
00396 }
00397
00398 while (i < MAX_VEHICLES_AT_A_TIME)
00399 {
00400 if (!g_vehiclePoolOccupied[i])
00401 {
00402 g_vehiclePoolOccupied[i] = qtrue;
00403 memset(&g_vehiclePool[i], 0, sizeof(Vehicle_t));
00404 *pVeh = &g_vehiclePool[i];
00405 return;
00406 }
00407 i++;
00408 }
00409 Com_Error(ERR_DROP, "Ran out of vehicle pool slots.");
00410 }
00411
00412
00413 void G_FreeVehicleObject(Vehicle_t *pVeh)
00414 {
00415 int i = 0;
00416 while (i < MAX_VEHICLES_AT_A_TIME)
00417 {
00418 if (g_vehiclePoolOccupied[i] &&
00419 &g_vehiclePool[i] == pVeh)
00420 {
00421 g_vehiclePoolOccupied[i] = qfalse;
00422 break;
00423 }
00424 i++;
00425 }
00426 }
00427
00428 gclient_t *gClPtrs[MAX_GENTITIES];
00429
00430 void G_CreateFakeClient(int entNum, gclient_t **cl)
00431 {
00432
00433 if (!gClPtrs[entNum])
00434 {
00435 gClPtrs[entNum] = (gclient_t *) BG_Alloc(sizeof(gclient_t));
00436 }
00437 *cl = gClPtrs[entNum];
00438 }
00439
00440 #ifdef _XBOX
00441 void G_ClPtrClear(void)
00442 {
00443 for(int i=0; i<MAX_GENTITIES; i++) {
00444 gClPtrs[i] = NULL;
00445 }
00446 }
00447 #endif
00448
00449
00450 void G_CleanAllFakeClients(void)
00451 {
00452 int i = MAX_CLIENTS;
00453 gentity_t *ent;
00454
00455 while (i < MAX_GENTITIES)
00456 {
00457 ent = &g_entities[i];
00458
00459 if (ent->inuse && ent->s.eType == ET_NPC && ent->client)
00460 {
00461 G_FreeFakeClient(&ent->client);
00462 }
00463 i++;
00464 }
00465 }
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475 #include "../namespace_begin.h"
00476 void BG_SetAnim(playerState_t *ps, animation_t *animations, int setAnimParts,int anim,int setAnimFlags, int blendTime);
00477 #include "../namespace_end.h"
00478
00479 void G_SetAnim(gentity_t *ent, usercmd_t *ucmd, int setAnimParts, int anim, int setAnimFlags, int blendTime)
00480 {
00481 #if 0 //old hackish way
00482 pmove_t pmv;
00483
00484 assert(ent && ent->inuse && ent->client);
00485
00486 memset (&pmv, 0, sizeof(pmv));
00487 pmv.ps = &ent->client->ps;
00488 pmv.animations = bgAllAnims[ent->localAnimIndex].anims;
00489 if (!ucmd)
00490 {
00491 pmv.cmd = ent->client->pers.cmd;
00492 }
00493 else
00494 {
00495 pmv.cmd = *ucmd;
00496 }
00497 pmv.trace = trap_Trace;
00498 pmv.pointcontents = trap_PointContents;
00499 pmv.gametype = g_gametype.integer;
00500
00501
00502 pm = &pmv;
00503 PM_SetAnim(setAnimParts, anim, setAnimFlags, blendTime);
00504 #else //new clean and shining way!
00505 assert(ent->client);
00506 BG_SetAnim(&ent->client->ps, bgAllAnims[ent->localAnimIndex].anims, setAnimParts,
00507 anim, setAnimFlags, blendTime);
00508 #endif
00509 }
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519 #define MAXCHOICES 32
00520
00521 gentity_t *G_PickTarget (char *targetname)
00522 {
00523 gentity_t *ent = NULL;
00524 int num_choices = 0;
00525 gentity_t *choice[MAXCHOICES];
00526
00527 if (!targetname)
00528 {
00529 G_Printf("G_PickTarget called with NULL targetname\n");
00530 return NULL;
00531 }
00532
00533 while(1)
00534 {
00535 ent = G_Find (ent, FOFS(targetname), targetname);
00536 if (!ent)
00537 break;
00538 choice[num_choices++] = ent;
00539 if (num_choices == MAXCHOICES)
00540 break;
00541 }
00542
00543 if (!num_choices)
00544 {
00545 G_Printf("G_PickTarget: target %s not found\n", targetname);
00546 return NULL;
00547 }
00548
00549 return choice[rand() % num_choices];
00550 }
00551
00552 void GlobalUse(gentity_t *self, gentity_t *other, gentity_t *activator)
00553 {
00554 if (!self || (self->flags & FL_INACTIVE))
00555 {
00556 return;
00557 }
00558
00559 if (!self->use)
00560 {
00561 return;
00562 }
00563 self->use(self, other, activator);
00564 }
00565
00566 void G_UseTargets2( gentity_t *ent, gentity_t *activator, const char *string ) {
00567 gentity_t *t;
00568
00569 if ( !ent ) {
00570 return;
00571 }
00572
00573 if (ent->targetShaderName && ent->targetShaderNewName) {
00574 float f = level.time * 0.001;
00575 AddRemap(ent->targetShaderName, ent->targetShaderNewName, f);
00576 trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
00577 }
00578
00579 if ( !string || !string[0] ) {
00580 return;
00581 }
00582
00583 t = NULL;
00584 while ( (t = G_Find (t, FOFS(targetname), string)) != NULL ) {
00585 if ( t == ent ) {
00586 G_Printf ("WARNING: Entity used itself.\n");
00587 } else {
00588 if ( t->use ) {
00589 GlobalUse(t, ent, activator);
00590 }
00591 }
00592 if ( !ent->inuse ) {
00593 G_Printf("entity was removed while using targets\n");
00594 return;
00595 }
00596 }
00597 }
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609 void G_UseTargets( gentity_t *ent, gentity_t *activator )
00610 {
00611 if (!ent)
00612 {
00613 return;
00614 }
00615 G_UseTargets2(ent, activator, ent->target);
00616 }
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 float *tv( float x, float y, float z ) {
00628 static int index;
00629 static vec3_t vecs[8];
00630 float *v;
00631
00632
00633
00634 v = vecs[index];
00635 index = (index + 1)&7;
00636
00637 v[0] = x;
00638 v[1] = y;
00639 v[2] = z;
00640
00641 return v;
00642 }
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653 char *vtos( const vec3_t v ) {
00654 static int index;
00655 static char str[8][32];
00656 char *s;
00657
00658
00659 s = str[index];
00660 index = (index + 1)&7;
00661
00662 Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
00663
00664 return s;
00665 }
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678 void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
00679 static vec3_t VEC_UP = {0, -1, 0};
00680 static vec3_t MOVEDIR_UP = {0, 0, 1};
00681 static vec3_t VEC_DOWN = {0, -2, 0};
00682 static vec3_t MOVEDIR_DOWN = {0, 0, -1};
00683
00684 if ( VectorCompare (angles, VEC_UP) ) {
00685 VectorCopy (MOVEDIR_UP, movedir);
00686 } else if ( VectorCompare (angles, VEC_DOWN) ) {
00687 VectorCopy (MOVEDIR_DOWN, movedir);
00688 } else {
00689 AngleVectors (angles, movedir, NULL, NULL);
00690 }
00691 VectorClear( angles );
00692 }
00693
00694 void G_InitGentity( gentity_t *e ) {
00695 e->inuse = qtrue;
00696 e->classname = "noclass";
00697 e->s.number = e - g_entities;
00698 e->r.ownerNum = ENTITYNUM_NONE;
00699 e->s.modelGhoul2 = 0;
00700
00701 trap_ICARUS_FreeEnt( e );
00702 }
00703
00704
00705 static void G_SpewEntList(void)
00706 {
00707 int i = 0;
00708 int numNPC = 0;
00709 int numProjectile = 0;
00710 int numTempEnt = 0;
00711 int numTempEntST = 0;
00712 char className[MAX_STRING_CHARS];
00713 gentity_t *ent;
00714 char *str;
00715 #ifdef FINAL_BUILD
00716 #define VM_OR_FINAL_BUILD
00717 #elif defined Q3_VM
00718 #define VM_OR_FINAL_BUILD
00719 #endif
00720
00721 #ifndef VM_OR_FINAL_BUILD
00722 fileHandle_t fh;
00723 trap_FS_FOpenFile("entspew.txt", &fh, FS_WRITE);
00724 #endif
00725
00726 while (i < ENTITYNUM_MAX_NORMAL)
00727 {
00728 ent = &g_entities[i];
00729 if (ent->inuse)
00730 {
00731 if (ent->s.eType == ET_NPC)
00732 {
00733 numNPC++;
00734 }
00735 else if (ent->s.eType == ET_MISSILE)
00736 {
00737 numProjectile++;
00738 }
00739 else if (ent->freeAfterEvent)
00740 {
00741 numTempEnt++;
00742 if (ent->s.eFlags & EF_SOUNDTRACKER)
00743 {
00744 numTempEntST++;
00745 }
00746
00747 str = va("TEMPENT %4i: EV %i\n", ent->s.number, ent->s.eType-ET_EVENTS);
00748 Com_Printf(str);
00749 #ifndef VM_OR_FINAL_BUILD
00750 if (fh)
00751 {
00752 trap_FS_Write(str, strlen(str), fh);
00753 }
00754 #endif
00755 }
00756
00757 if (ent->classname && ent->classname[0])
00758 {
00759 strcpy(className, ent->classname);
00760 }
00761 else
00762 {
00763 strcpy(className, "Unknown");
00764 }
00765 str = va("ENT %4i: Classname %s\n", ent->s.number, className);
00766 Com_Printf(str);
00767 #ifndef VM_OR_FINAL_BUILD
00768 if (fh)
00769 {
00770 trap_FS_Write(str, strlen(str), fh);
00771 }
00772 #endif
00773 }
00774
00775 i++;
00776 }
00777
00778 str = va("TempEnt count: %i\nTempEnt ST: %i\nNPC count: %i\nProjectile count: %i\n", numTempEnt, numTempEntST, numNPC, numProjectile);
00779 Com_Printf(str);
00780 #ifndef VM_OR_FINAL_BUILD
00781 if (fh)
00782 {
00783 trap_FS_Write(str, strlen(str), fh);
00784 trap_FS_FCloseFile(fh);
00785 }
00786 #endif
00787 }
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804 gentity_t *G_Spawn( void ) {
00805 int i, force;
00806 gentity_t *e;
00807
00808 e = NULL;
00809 i = 0;
00810 for ( force = 0 ; force < 2 ; force++ ) {
00811
00812
00813 e = &g_entities[MAX_CLIENTS];
00814 for ( i = MAX_CLIENTS ; i<level.num_entities ; i++, e++) {
00815 if ( e->inuse ) {
00816 continue;
00817 }
00818
00819
00820
00821 if ( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 )
00822 {
00823 continue;
00824 }
00825
00826
00827 G_InitGentity( e );
00828 return e;
00829 }
00830 if ( i != MAX_GENTITIES ) {
00831 break;
00832 }
00833 }
00834 if ( i == ENTITYNUM_MAX_NORMAL ) {
00835
00836
00837
00838
00839
00840 G_SpewEntList();
00841 G_Error( "G_Spawn: no free entities" );
00842 }
00843
00844
00845 level.num_entities++;
00846
00847
00848 trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ),
00849 &level.clients[0].ps, sizeof( level.clients[0] ) );
00850
00851 G_InitGentity( e );
00852 return e;
00853 }
00854
00855
00856
00857
00858
00859
00860 qboolean G_EntitiesFree( void ) {
00861 int i;
00862 gentity_t *e;
00863
00864 e = &g_entities[MAX_CLIENTS];
00865 for ( i = MAX_CLIENTS; i < level.num_entities; i++, e++) {
00866 if ( e->inuse ) {
00867 continue;
00868 }
00869
00870 return qtrue;
00871 }
00872 return qfalse;
00873 }
00874
00875 #define MAX_G2_KILL_QUEUE 256
00876
00877 int gG2KillIndex[MAX_G2_KILL_QUEUE];
00878 int gG2KillNum = 0;
00879
00880 void G_SendG2KillQueue(void)
00881 {
00882 char g2KillString[1024];
00883 int i = 0;
00884
00885 if (!gG2KillNum)
00886 {
00887 return;
00888 }
00889
00890 Com_sprintf(g2KillString, 1024, "kg2");
00891
00892 while (i < gG2KillNum && i < 64)
00893 {
00894 Q_strcat(g2KillString, 1024, va(" %i", gG2KillIndex[i]));
00895 i++;
00896 }
00897
00898 trap_SendServerCommand(-1, g2KillString);
00899
00900
00901 gG2KillNum -= i;
00902 if (gG2KillNum < 0)
00903 {
00904 assert(0);
00905 gG2KillNum = 0;
00906 }
00907 }
00908
00909 void G_KillG2Queue(int entNum)
00910 {
00911 if (gG2KillNum >= MAX_G2_KILL_QUEUE)
00912 {
00913 #ifdef _DEBUG
00914 Com_Printf("WARNING: Exceeded the MAX_G2_KILL_QUEUE count for this frame!\n");
00915 #endif
00916
00917 trap_SendServerCommand(-1, va("kg2 %i", entNum));
00918 return;
00919 }
00920
00921 gG2KillIndex[gG2KillNum] = entNum;
00922 gG2KillNum++;
00923 }
00924
00925
00926
00927
00928
00929
00930
00931
00932 void G_FreeEntity( gentity_t *ed ) {
00933
00934
00935 if (ed->isSaberEntity)
00936 {
00937 #ifdef _DEBUG
00938 Com_Printf("Tried to remove JM saber!\n");
00939 #endif
00940 return;
00941 }
00942
00943 trap_UnlinkEntity (ed);
00944
00945 trap_ICARUS_FreeEnt( ed );
00946
00947 if ( ed->neverFree ) {
00948 return;
00949 }
00950
00951
00952
00953
00954
00955 if (ed->s.modelGhoul2)
00956 {
00957
00958
00959
00960
00961
00962
00963 G_KillG2Queue(ed->s.number);
00964 }
00965
00966
00967 if (ed->ghoul2)
00968 {
00969 trap_G2API_CleanGhoul2Models(&(ed->ghoul2));
00970 }
00971
00972 if (ed->s.eType == ET_NPC && ed->m_pVehicle)
00973 {
00974 G_FreeVehicleObject(ed->m_pVehicle);
00975 }
00976
00977 if (ed->s.eType == ET_NPC && ed->client)
00978 {
00979 int saberEntNum = -1;
00980 int i = 0;
00981 if (ed->client->ps.saberEntityNum)
00982 {
00983 saberEntNum = ed->client->ps.saberEntityNum;
00984 }
00985 else if (ed->client->saberStoredIndex)
00986 {
00987 saberEntNum = ed->client->saberStoredIndex;
00988 }
00989
00990 if (saberEntNum > 0 && g_entities[saberEntNum].inuse)
00991 {
00992 g_entities[saberEntNum].neverFree = qfalse;
00993 G_FreeEntity(&g_entities[saberEntNum]);
00994 }
00995
00996 while (i < MAX_SABERS)
00997 {
00998 if (ed->client->weaponGhoul2[i] && trap_G2_HaveWeGhoul2Models(ed->client->weaponGhoul2[i]))
00999 {
01000 trap_G2API_CleanGhoul2Models(&ed->client->weaponGhoul2[i]);
01001 }
01002 i++;
01003 }
01004
01005 G_FreeFakeClient(&ed->client);
01006 }
01007
01008 if (ed->s.eFlags & EF_SOUNDTRACKER)
01009 {
01010 int i = 0;
01011 gentity_t *ent;
01012
01013 while (i < MAX_CLIENTS)
01014 {
01015 ent = &g_entities[i];
01016
01017 if (ent && ent->inuse && ent->client)
01018 {
01019 int ch = TRACK_CHANNEL_NONE-50;
01020
01021 while (ch < NUM_TRACK_CHANNELS-50)
01022 {
01023 if (ent->client->ps.fd.killSoundEntIndex[ch] == ed->s.number)
01024 {
01025 ent->client->ps.fd.killSoundEntIndex[ch] = 0;
01026 }
01027
01028 ch++;
01029 }
01030 }
01031
01032 i++;
01033 }
01034
01035
01036 trap_SendServerCommand(-1, va("kls %i %i", ed->s.trickedentindex, ed->s.number));
01037 }
01038
01039 memset (ed, 0, sizeof(*ed));
01040 ed->classname = "freed";
01041 ed->freetime = level.time;
01042 ed->inuse = qfalse;
01043 }
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054 gentity_t *G_TempEntity( vec3_t origin, int event ) {
01055 gentity_t *e;
01056 vec3_t snapped;
01057
01058 e = G_Spawn();
01059 e->s.eType = ET_EVENTS + event;
01060
01061 e->classname = "tempEntity";
01062 e->eventTime = level.time;
01063 e->freeAfterEvent = qtrue;
01064
01065 VectorCopy( origin, snapped );
01066 SnapVector( snapped );
01067 G_SetOrigin( e, snapped );
01068
01069
01070
01071
01072
01073
01074 trap_LinkEntity( e );
01075
01076 return e;
01077 }
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087 gentity_t *G_SoundTempEntity( vec3_t origin, int event, int channel ) {
01088 gentity_t *e;
01089 vec3_t snapped;
01090
01091 e = G_Spawn();
01092
01093 e->s.eType = ET_EVENTS + event;
01094 e->inuse = qtrue;
01095
01096 e->classname = "tempEntity";
01097 e->eventTime = level.time;
01098 e->freeAfterEvent = qtrue;
01099
01100 VectorCopy( origin, snapped );
01101 SnapVector( snapped );
01102 G_SetOrigin( e, snapped );
01103
01104
01105 trap_LinkEntity( e );
01106
01107 return e;
01108 }
01109
01110
01111
01112 void G_ScaleNetHealth(gentity_t *self)
01113 {
01114 int maxHealth = self->maxHealth;
01115
01116 if (maxHealth < 1000)
01117 {
01118 self->s.maxhealth = maxHealth;
01119 self->s.health = self->health;
01120
01121 if (self->s.health < 0)
01122 {
01123 self->s.health = 0;
01124 }
01125 return;
01126 }
01127
01128
01129 self->s.maxhealth = (maxHealth/100);
01130 self->s.health = (self->health/100);
01131
01132 if (self->s.health < 0)
01133 {
01134 self->s.health = 0;
01135 }
01136
01137 if (self->health > 0 &&
01138 self->s.health <= 0)
01139 {
01140 self->s.health = 1;
01141 }
01142 }
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162 void G_KillBox (gentity_t *ent) {
01163 int i, num;
01164 int touch[MAX_GENTITIES];
01165 gentity_t *hit;
01166 vec3_t mins, maxs;
01167
01168 VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
01169 VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
01170 num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
01171
01172 for (i=0 ; i<num ; i++) {
01173 hit = &g_entities[touch[i]];
01174 if ( !hit->client ) {
01175 continue;
01176 }
01177
01178 if (hit->s.number == ent->s.number)
01179 {
01180 continue;
01181 }
01182
01183 if (ent->r.ownerNum == hit->s.number)
01184 {
01185 continue;
01186 }
01187
01188
01189 G_Damage ( hit, ent, ent, NULL, NULL,
01190 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
01191 }
01192
01193 }
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206 void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ) {
01207 if ( !ent->client ) {
01208 return;
01209 }
01210 BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps );
01211 }
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221 void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
01222 int bits;
01223
01224 if ( !event ) {
01225 G_Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
01226 return;
01227 }
01228
01229
01230 if ( ent->client ) {
01231 bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
01232 bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
01233 ent->client->ps.externalEvent = event | bits;
01234 ent->client->ps.externalEventParm = eventParm;
01235 ent->client->ps.externalEventTime = level.time;
01236 } else {
01237 bits = ent->s.event & EV_EVENT_BITS;
01238 bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
01239 ent->s.event = event | bits;
01240 ent->s.eventParm = eventParm;
01241 }
01242 ent->eventTime = level.time;
01243 }
01244
01245
01246
01247
01248
01249
01250 gentity_t *G_PlayEffect(int fxID, vec3_t org, vec3_t ang)
01251 {
01252 gentity_t *te;
01253
01254 te = G_TempEntity( org, EV_PLAY_EFFECT );
01255 VectorCopy(ang, te->s.angles);
01256 VectorCopy(org, te->s.origin);
01257 te->s.eventParm = fxID;
01258
01259 return te;
01260 }
01261
01262
01263
01264
01265
01266
01267 gentity_t *G_PlayEffectID(const int fxID, vec3_t org, vec3_t ang)
01268 {
01269 gentity_t *te;
01270
01271 te = G_TempEntity( org, EV_PLAY_EFFECT_ID );
01272 VectorCopy(ang, te->s.angles);
01273 VectorCopy(org, te->s.origin);
01274 te->s.eventParm = fxID;
01275
01276 if (!te->s.angles[0] &&
01277 !te->s.angles[1] &&
01278 !te->s.angles[2])
01279 {
01280 te->s.angles[1] = 1;
01281 }
01282
01283 return te;
01284 }
01285
01286
01287
01288
01289
01290
01291 gentity_t *G_ScreenShake(vec3_t org, gentity_t *target, float intensity, int duration, qboolean global)
01292 {
01293 gentity_t *te;
01294
01295 te = G_TempEntity( org, EV_SCREENSHAKE );
01296 VectorCopy(org, te->s.origin);
01297 te->s.angles[0] = intensity;
01298 te->s.time = duration;
01299
01300 if (target)
01301 {
01302 te->s.modelindex = target->s.number+1;
01303 }
01304 else
01305 {
01306 te->s.modelindex = 0;
01307 }
01308
01309 if (global)
01310 {
01311 te->r.svFlags |= SVF_BROADCAST;
01312 }
01313
01314 return te;
01315 }
01316
01317
01318
01319
01320
01321
01322 void G_MuteSound( int entnum, int channel )
01323 {
01324 gentity_t *te, *e;
01325
01326 te = G_TempEntity( vec3_origin, EV_MUTE_SOUND );
01327 te->r.svFlags = SVF_BROADCAST;
01328 te->s.trickedentindex2 = entnum;
01329 te->s.trickedentindex = channel;
01330
01331 e = &g_entities[entnum];
01332
01333 if (e && (e->s.eFlags & EF_SOUNDTRACKER))
01334 {
01335 G_FreeEntity(e);
01336 e->s.eFlags = 0;
01337 }
01338 }
01339
01340
01341
01342
01343
01344
01345 void G_Sound( gentity_t *ent, int channel, int soundIndex ) {
01346 gentity_t *te;
01347
01348 assert(soundIndex);
01349
01350 te = G_SoundTempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND, channel );
01351 te->s.eventParm = soundIndex;
01352 te->s.saberEntityNum = channel;
01353
01354 if (ent && ent->client && channel > TRACK_CHANNEL_NONE)
01355 {
01356 if (g_entities[ent->client->ps.fd.killSoundEntIndex[channel-50]].inuse &&
01357 ent->client->ps.fd.killSoundEntIndex[channel-50] > MAX_CLIENTS)
01358 {
01359 G_MuteSound(ent->client->ps.fd.killSoundEntIndex[channel-50], CHAN_VOICE);
01360 if (ent->client->ps.fd.killSoundEntIndex[channel-50] > MAX_CLIENTS && g_entities[ent->client->ps.fd.killSoundEntIndex[channel-50]].inuse)
01361 {
01362 G_FreeEntity(&g_entities[ent->client->ps.fd.killSoundEntIndex[channel-50]]);
01363 }
01364 ent->client->ps.fd.killSoundEntIndex[channel-50] = 0;
01365 }
01366
01367 ent->client->ps.fd.killSoundEntIndex[channel-50] = te->s.number;
01368 te->s.trickedentindex = ent->s.number;
01369 te->s.eFlags = EF_SOUNDTRACKER;
01370
01371 }
01372 }
01373
01374
01375
01376
01377
01378
01379 void G_SoundAtLoc( vec3_t loc, int channel, int soundIndex ) {
01380 gentity_t *te;
01381
01382 te = G_TempEntity( loc, EV_GENERAL_SOUND );
01383 te->s.eventParm = soundIndex;
01384 te->s.saberEntityNum = channel;
01385 }
01386
01387
01388
01389
01390
01391
01392 void G_EntitySound( gentity_t *ent, int channel, int soundIndex ) {
01393 gentity_t *te;
01394
01395 te = G_TempEntity( ent->r.currentOrigin, EV_ENTITY_SOUND );
01396 te->s.eventParm = soundIndex;
01397 te->s.clientNum = ent->s.number;
01398 te->s.trickedentindex = channel;
01399 }
01400
01401
01402 void G_SoundOnEnt( gentity_t *ent, int channel, const char *soundPath )
01403 {
01404 gentity_t *te;
01405
01406 te = G_TempEntity( ent->r.currentOrigin, EV_ENTITY_SOUND );
01407 te->s.eventParm = G_SoundIndex((char *)soundPath);
01408 te->s.clientNum = ent->s.number;
01409 te->s.trickedentindex = channel;
01410
01411 }
01412
01413 #ifdef _XBOX
01414
01415 void G_EntityPosition( int i, vec3_t ret )
01416 {
01417 if ( i >= 0 && i < MAX_GENTITIES && g_entities[i].inuse)
01418 {
01419 #if 0 // VVFIXME - Do we really care about doing this? It's slow and unnecessary
01420 gentity_t *ent = g_entities + i;
01421
01422 if (ent->bmodel)
01423 {
01424 vec3_t mins, maxs;
01425 clipHandle_t h = CM_InlineModel( ent->s.modelindex );
01426 CM_ModelBounds( cmg, h, mins, maxs );
01427 ret[0] = (mins[0] + maxs[0]) / 2 + ent->currentOrigin[0];
01428 ret[1] = (mins[1] + maxs[1]) / 2 + ent->currentOrigin[1];
01429 ret[2] = (mins[2] + maxs[2]) / 2 + ent->currentOrigin[2];
01430 }
01431 else
01432 #endif
01433 {
01434 VectorCopy(g_entities[i].r.currentOrigin, ret);
01435 }
01436 }
01437 else
01438 {
01439 ret[0] = ret[1] = ret[2] = 0;
01440 }
01441 }
01442 #endif
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453 qboolean ValidUseTarget( gentity_t *ent )
01454 {
01455 if ( !ent->use )
01456 {
01457 return qfalse;
01458 }
01459
01460 if ( ent->flags & FL_INACTIVE )
01461 {
01462 return qfalse;
01463 }
01464
01465 if ( !(ent->r.svFlags & SVF_PLAYER_USABLE) )
01466 {
01467 return qfalse;
01468 }
01469
01470 return qtrue;
01471 }
01472
01473
01474 void G_UseDispenserOn(gentity_t *ent, int dispType, gentity_t *target)
01475 {
01476 if (dispType == HI_HEALTHDISP)
01477 {
01478 target->client->ps.stats[STAT_HEALTH] += 4;
01479
01480 if (target->client->ps.stats[STAT_HEALTH] > target->client->ps.stats[STAT_MAX_HEALTH])
01481 {
01482 target->client->ps.stats[STAT_HEALTH] = target->client->ps.stats[STAT_MAX_HEALTH];
01483 }
01484
01485 target->client->isMedHealed = level.time + 500;
01486 target->health = target->client->ps.stats[STAT_HEALTH];
01487 }
01488 else if (dispType == HI_AMMODISP)
01489 {
01490 if (ent->client->medSupplyDebounce < level.time)
01491 {
01492
01493 target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] += weaponData[target->client->ps.weapon].energyPerShot;
01494
01495 if (target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] > ammoData[weaponData[target->client->ps.weapon].ammoIndex].max)
01496 {
01497 target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] = ammoData[weaponData[target->client->ps.weapon].ammoIndex].max;
01498 }
01499
01500
01501 ent->client->medSupplyDebounce = level.time + weaponData[target->client->ps.weapon].fireTime;
01502 }
01503 target->client->isMedSupplied = level.time + 500;
01504 }
01505 }
01506
01507
01508 int G_CanUseDispOn(gentity_t *ent, int dispType)
01509 {
01510 if (!ent->client || !ent->inuse || ent->health < 1 ||
01511 ent->client->ps.stats[STAT_HEALTH] < 1)
01512 {
01513 return 0;
01514 }
01515
01516 if (dispType == HI_HEALTHDISP)
01517 {
01518 if (ent->client->ps.stats[STAT_HEALTH] < ent->client->ps.stats[STAT_MAX_HEALTH])
01519 {
01520 return 1;
01521 }
01522
01523
01524 return 0;
01525 }
01526 else if (dispType == HI_AMMODISP)
01527 {
01528 if (ent->client->ps.weapon <= WP_NONE || ent->client->ps.weapon > LAST_USEABLE_WEAPON)
01529 {
01530 return 0;
01531 }
01532
01533 if (ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex] < ammoData[weaponData[ent->client->ps.weapon].ammoIndex].max)
01534 {
01535 return 1;
01536 }
01537
01538
01539 return 0;
01540 }
01541
01542
01543 return 0;
01544 }
01545
01546 qboolean TryHeal(gentity_t *ent, gentity_t *target)
01547 {
01548 if (g_gametype.integer == GT_SIEGE && ent->client->siegeClass != -1 &&
01549 target && target->inuse && target->maxHealth && target->healingclass &&
01550 target->healingclass[0] && target->health > 0 && target->health < target->maxHealth)
01551 {
01552 siegeClass_t *scl = &bgSiegeClasses[ent->client->siegeClass];
01553
01554 if (!Q_stricmp(scl->name, target->healingclass))
01555 {
01556 if (target->healingDebounce < level.time)
01557 {
01558 target->health += 10;
01559 if (target->health > target->maxHealth)
01560 {
01561 target->health = target->maxHealth;
01562 }
01563 target->healingDebounce = level.time + target->healingrate;
01564 if (target->healingsound && target->healingsound[0])
01565 {
01566 if (target->s.solid == SOLID_BMODEL)
01567 {
01568 G_Sound(ent, CHAN_AUTO, G_SoundIndex(target->healingsound));
01569 }
01570 else
01571 {
01572 G_Sound(target, CHAN_AUTO, G_SoundIndex(target->healingsound));
01573 }
01574 }
01575
01576
01577 G_ScaleNetHealth(target);
01578 if (target->target_ent &&
01579 target->target_ent->maxHealth)
01580 {
01581 target->target_ent->health = target->health;
01582 G_ScaleNetHealth(target->target_ent);
01583 }
01584 }
01585
01586
01587 if (ent->client->ps.torsoAnim == BOTH_BUTTON_HOLD ||
01588 ent->client->ps.torsoAnim == BOTH_CONSOLE1)
01589 {
01590 ent->client->ps.torsoTimer = 500;
01591 }
01592 else
01593 {
01594 G_SetAnim( ent, NULL, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01595 }
01596
01597 return qtrue;
01598 }
01599 }
01600
01601 return qfalse;
01602 }
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612 #define USE_DISTANCE 64.0f
01613
01614 extern void Touch_Button(gentity_t *ent, gentity_t *other, trace_t *trace );
01615 extern qboolean gSiegeRoundBegun;
01616 static vec3_t playerMins = {-15, -15, DEFAULT_MINS_2};
01617 static vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2};
01618 void TryUse( gentity_t *ent )
01619 {
01620 gentity_t *target;
01621 trace_t trace;
01622 vec3_t src, dest, vf;
01623 vec3_t viewspot;
01624
01625 if (g_gametype.integer == GT_SIEGE &&
01626 !gSiegeRoundBegun)
01627 {
01628 return;
01629 }
01630
01631 if (!ent || !ent->client || (ent->client->ps.weaponTime > 0 && ent->client->ps.torsoAnim != BOTH_BUTTON_HOLD && ent->client->ps.torsoAnim != BOTH_CONSOLE1) || ent->health < 1 ||
01632 (ent->client->ps.pm_flags & PMF_FOLLOW) || ent->client->sess.sessionTeam == TEAM_SPECTATOR ||
01633 (ent->client->ps.forceHandExtend != HANDEXTEND_NONE && ent->client->ps.forceHandExtend != HANDEXTEND_DRAGGING))
01634 {
01635 return;
01636 }
01637
01638 if (ent->client->ps.emplacedIndex)
01639 {
01640 return;
01641 }
01642
01643 if (ent->s.number < MAX_CLIENTS && ent->client && ent->client->ps.m_iVehicleNum)
01644 {
01645 gentity_t *currentVeh = &g_entities[ent->client->ps.m_iVehicleNum];
01646 if (currentVeh->inuse && currentVeh->m_pVehicle)
01647 {
01648 Vehicle_t *pVeh = currentVeh->m_pVehicle;
01649 if (!pVeh->m_iBoarding)
01650 {
01651 pVeh->m_pVehicleInfo->Eject( pVeh, (bgEntity_t *)ent, qfalse );
01652 }
01653 return;
01654 }
01655 }
01656
01657 if (ent->client->jetPackOn)
01658 {
01659 goto tryJetPack;
01660 }
01661
01662 if (ent->client->bodyGrabIndex != ENTITYNUM_NONE)
01663 {
01664 if (ent->client->bodyGrabTime < level.time)
01665 {
01666 gentity_t *grabbed = &g_entities[ent->client->bodyGrabIndex];
01667
01668 if (grabbed->inuse)
01669 {
01670 if (grabbed->client)
01671 {
01672 grabbed->client->ps.ragAttach = 0;
01673 }
01674 else
01675 {
01676 grabbed->s.ragAttach = 0;
01677 }
01678 }
01679 ent->client->bodyGrabIndex = ENTITYNUM_NONE;
01680 ent->client->bodyGrabTime = level.time + 1000;
01681 }
01682 return;
01683 }
01684
01685 VectorCopy(ent->client->ps.origin, viewspot);
01686 viewspot[2] += ent->client->ps.viewheight;
01687
01688 VectorCopy( viewspot, src );
01689 AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );
01690
01691 VectorMA( src, USE_DISTANCE, vf, dest );
01692
01693
01694 trap_Trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE );
01695
01696 if ( trace.fraction == 1.0f || trace.entityNum < 1 )
01697 {
01698 goto tryJetPack;
01699 }
01700
01701 target = &g_entities[trace.entityNum];
01702
01703
01704 #if 0
01705 if (target->inuse && target->s.eType == ET_BODY &&
01706 ent->client->bodyGrabTime < level.time)
01707 {
01708 target->s.eFlags |= EF_RAG;
01709 if (!ent->s.number)
01710 {
01711 target->s.ragAttach = ENTITYNUM_NONE;
01712 }
01713 else
01714 {
01715 target->s.ragAttach = ent->s.number;
01716 }
01717 ent->client->bodyGrabTime = level.time + 1000;
01718 ent->client->bodyGrabIndex = target->s.number;
01719 return;
01720 }
01721 #endif
01722
01723 if (target && target->m_pVehicle && target->client &&
01724 target->s.NPC_class == CLASS_VEHICLE &&
01725 !ent->client->ps.zoomMode)
01726 {
01727 Vehicle_t *pVeh = target->m_pVehicle;
01728
01729 if (pVeh->m_pVehicleInfo)
01730 {
01731 if ( ent->r.ownerNum == target->s.number )
01732 {
01733 pVeh->m_pVehicleInfo->Eject( pVeh, (bgEntity_t *)ent, qfalse );
01734 }
01735 else
01736 {
01737 if (g_gametype.integer < GT_TEAM ||
01738 !target->alliedTeam ||
01739 (target->alliedTeam == ent->client->sess.sessionTeam))
01740 {
01741 pVeh->m_pVehicleInfo->Board( pVeh, (bgEntity_t *)ent );
01742 }
01743 }
01744
01745 ent->client->pers.cmd.buttons &= ~BUTTON_USE;
01746 return;
01747 }
01748 }
01749
01750 #if 0 //ye olde method
01751 if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] > 0 &&
01752 bg_itemlist[ent->client->ps.stats[STAT_HOLDABLE_ITEM]].giType == IT_HOLDABLE)
01753 {
01754 if (bg_itemlist[ent->client->ps.stats[STAT_HOLDABLE_ITEM]].giTag == HI_HEALTHDISP ||
01755 bg_itemlist[ent->client->ps.stats[STAT_HOLDABLE_ITEM]].giTag == HI_AMMODISP)
01756 {
01757 if (target && target->client && target->health > 0 && OnSameTeam(ent, target) &&
01758 G_CanUseDispOn(target, bg_itemlist[ent->client->ps.stats[STAT_HOLDABLE_ITEM]].giTag))
01759 {
01760 G_UseDispenserOn(ent, bg_itemlist[ent->client->ps.stats[STAT_HOLDABLE_ITEM]].giTag, target);
01761
01762
01763 if (ent->client->ps.torsoAnim == BOTH_BUTTON_HOLD)
01764 {
01765 ent->client->ps.torsoTimer = 500;
01766 }
01767 else
01768 {
01769 G_SetAnim( ent, NULL, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01770 }
01771 ent->client->ps.weaponTime = ent->client->ps.torsoTimer;
01772 return;
01773 }
01774 }
01775 }
01776 #else
01777 if ( ((ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_HEALTHDISP)) || (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_AMMODISP))) &&
01778 target && target->inuse && target->client && target->health > 0 && OnSameTeam(ent, target) &&
01779 (G_CanUseDispOn(target, HI_HEALTHDISP) || G_CanUseDispOn(target, HI_AMMODISP)) )
01780 {
01781 if (G_CanUseDispOn(target, HI_HEALTHDISP))
01782 {
01783 G_UseDispenserOn(ent, HI_HEALTHDISP, target);
01784 }
01785 if (G_CanUseDispOn(target, HI_AMMODISP))
01786 {
01787 G_UseDispenserOn(ent, HI_AMMODISP, target);
01788 }
01789
01790
01791 if (ent->client->ps.torsoAnim == BOTH_BUTTON_HOLD)
01792 {
01793 ent->client->ps.torsoTimer = 500;
01794 }
01795 else
01796 {
01797 G_SetAnim( ent, NULL, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01798 }
01799 ent->client->ps.weaponTime = ent->client->ps.torsoTimer;
01800 return;
01801 }
01802
01803 #endif
01804
01805
01806 if ( ValidUseTarget( target )
01807 && (g_gametype.integer != GT_SIEGE
01808 || !target->alliedTeam
01809 || target->alliedTeam != ent->client->sess.sessionTeam
01810 || g_ff_objectives.integer) )
01811 {
01812 if (ent->client->ps.torsoAnim == BOTH_BUTTON_HOLD ||
01813 ent->client->ps.torsoAnim == BOTH_CONSOLE1)
01814 {
01815 ent->client->ps.torsoTimer = 500;
01816 }
01817 else
01818 {
01819 G_SetAnim( ent, NULL, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
01820 }
01821 ent->client->ps.weaponTime = ent->client->ps.torsoTimer;
01822
01823
01824
01825
01826
01827
01828
01829 if ( target->touch == Touch_Button )
01830 {
01831 target->touch(target, ent, NULL);
01832 }
01833 else
01834 {
01835 GlobalUse(target, ent, ent);
01836 }
01837 return;
01838 }
01839
01840 if (TryHeal(ent, target))
01841 {
01842 return;
01843 }
01844
01845 tryJetPack:
01846
01847 if (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_JETPACK))
01848 {
01849 if (ent->client->jetPackOn || ent->client->ps.groundEntityNum == ENTITYNUM_NONE)
01850 {
01851 ItemUse_Jetpack(ent);
01852 return;
01853 }
01854 }
01855
01856 if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_AMMODISP))
01857 )
01858 {
01859 trace_t trToss;
01860 vec3_t fAng;
01861 vec3_t fwd;
01862
01863 VectorSet(fAng, 0.0f, ent->client->ps.viewangles[YAW], 0.0f);
01864 AngleVectors(fAng, fwd, 0, 0);
01865
01866 VectorMA(ent->client->ps.origin, 64.0f, fwd, fwd);
01867 trap_Trace(&trToss, ent->client->ps.origin, playerMins, playerMaxs, fwd, ent->s.number, ent->clipmask);
01868 if (trToss.fraction == 1.0f && !trToss.allsolid && !trToss.startsolid)
01869 {
01870 ItemUse_UseDisp(ent, HI_AMMODISP);
01871 G_AddEvent(ent, EV_USE_ITEM0+HI_AMMODISP, 0);
01872 return;
01873 }
01874 }
01875 }
01876
01877 qboolean G_PointInBounds( vec3_t point, vec3_t mins, vec3_t maxs )
01878 {
01879 int i;
01880
01881 for(i = 0; i < 3; i++ )
01882 {
01883 if ( point[i] < mins[i] )
01884 {
01885 return qfalse;
01886 }
01887 if ( point[i] > maxs[i] )
01888 {
01889 return qfalse;
01890 }
01891 }
01892
01893 return qtrue;
01894 }
01895
01896 qboolean G_BoxInBounds( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t boundsMins, vec3_t boundsMaxs )
01897 {
01898 vec3_t boxMins;
01899 vec3_t boxMaxs;
01900
01901 VectorAdd( point, mins, boxMins );
01902 VectorAdd( point, maxs, boxMaxs );
01903
01904 if(boxMaxs[0]>boundsMaxs[0])
01905 return qfalse;
01906
01907 if(boxMaxs[1]>boundsMaxs[1])
01908 return qfalse;
01909
01910 if(boxMaxs[2]>boundsMaxs[2])
01911 return qfalse;
01912
01913 if(boxMins[0]<boundsMins[0])
01914 return qfalse;
01915
01916 if(boxMins[1]<boundsMins[1])
01917 return qfalse;
01918
01919 if(boxMins[2]<boundsMins[2])
01920 return qfalse;
01921
01922
01923 return qtrue;
01924 }
01925
01926
01927 void G_SetAngles( gentity_t *ent, vec3_t angles )
01928 {
01929 VectorCopy( angles, ent->r.currentAngles );
01930 VectorCopy( angles, ent->s.angles );
01931 VectorCopy( angles, ent->s.apos.trBase );
01932 }
01933
01934 qboolean G_ClearTrace( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int ignore, int clipmask )
01935 {
01936 static trace_t tr;
01937
01938 trap_Trace( &tr, start, mins, maxs, end, ignore, clipmask );
01939
01940 if ( tr.allsolid || tr.startsolid || tr.fraction < 1.0 )
01941 {
01942 return qfalse;
01943 }
01944
01945 return qtrue;
01946 }
01947
01948
01949
01950
01951
01952
01953
01954
01955 void G_SetOrigin( gentity_t *ent, vec3_t origin ) {
01956 VectorCopy( origin, ent->s.pos.trBase );
01957 ent->s.pos.trType = TR_STATIONARY;
01958 ent->s.pos.trTime = 0;
01959 ent->s.pos.trDuration = 0;
01960 VectorClear( ent->s.pos.trDelta );
01961
01962 VectorCopy( origin, ent->r.currentOrigin );
01963 }
01964
01965 qboolean G_CheckInSolid (gentity_t *self, qboolean fix)
01966 {
01967 trace_t trace;
01968 vec3_t end, mins;
01969
01970 VectorCopy(self->r.currentOrigin, end);
01971 end[2] += self->r.mins[2];
01972 VectorCopy(self->r.mins, mins);
01973 mins[2] = 0;
01974
01975 trap_Trace(&trace, self->r.currentOrigin, mins, self->r.maxs, end, self->s.number, self->clipmask);
01976 if(trace.allsolid || trace.startsolid)
01977 {
01978 return qtrue;
01979 }
01980
01981 if(trace.fraction < 1.0)
01982 {
01983 if(fix)
01984 {
01985 vec3_t neworg;
01986
01987 VectorCopy(trace.endpos, neworg);
01988 neworg[2] -= self->r.mins[2];
01989 G_SetOrigin(self, neworg);
01990 trap_LinkEntity(self);
01991
01992 return G_CheckInSolid(self, qfalse);
01993 }
01994 else
01995 {
01996 return qtrue;
01997 }
01998 }
01999
02000 return qfalse;
02001 }
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011 int DebugLine(vec3_t start, vec3_t end, int color) {
02012 vec3_t points[4], dir, cross, up = {0, 0, 1};
02013 float dot;
02014
02015 VectorCopy(start, points[0]);
02016 VectorCopy(start, points[1]);
02017
02018 VectorCopy(end, points[2]);
02019
02020 VectorCopy(end, points[3]);
02021
02022
02023 VectorSubtract(end, start, dir);
02024 VectorNormalize(dir);
02025 dot = DotProduct(dir, up);
02026 if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
02027 else CrossProduct(dir, up, cross);
02028
02029 VectorNormalize(cross);
02030
02031 VectorMA(points[0], 2, cross, points[0]);
02032 VectorMA(points[1], -2, cross, points[1]);
02033 VectorMA(points[2], -2, cross, points[2]);
02034 VectorMA(points[3], 2, cross, points[3]);
02035
02036 return trap_DebugPolygonCreate(color, 4, points);
02037 }
02038
02039 void G_ROFF_NotetrackCallback( gentity_t *cent, const char *notetrack)
02040 {
02041 char type[256];
02042 int i = 0;
02043 int addlArg = 0;
02044
02045 if (!cent || !notetrack)
02046 {
02047 return;
02048 }
02049
02050 while (notetrack[i] && notetrack[i] != ' ')
02051 {
02052 type[i] = notetrack[i];
02053 i++;
02054 }
02055
02056 type[i] = '\0';
02057
02058 if (!i || !type[0])
02059 {
02060 return;
02061 }
02062
02063 if (notetrack[i] == ' ')
02064 {
02065 addlArg = 1;
02066 }
02067
02068 if (strcmp(type, "loop") == 0)
02069 {
02070 if (addlArg)
02071 {
02072 VectorCopy(cent->s.origin2, cent->s.pos.trBase);
02073 VectorCopy(cent->s.origin2, cent->r.currentOrigin);
02074 VectorCopy(cent->s.angles2, cent->s.apos.trBase);
02075 VectorCopy(cent->s.angles2, cent->r.currentAngles);
02076 }
02077
02078 trap_ROFF_Play(cent->s.number, cent->roffid, qfalse);
02079 }
02080 }
02081
02082 void G_SpeechEvent( gentity_t *self, int event )
02083 {
02084 G_AddEvent(self, event, 0);
02085 }
02086
02087 qboolean G_ExpandPointToBBox( vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask )
02088 {
02089 trace_t tr;
02090 vec3_t start, end;
02091 int i;
02092
02093 VectorCopy( point, start );
02094
02095 for ( i = 0; i < 3; i++ )
02096 {
02097 VectorCopy( start, end );
02098 end[i] += mins[i];
02099 trap_Trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask );
02100 if ( tr.allsolid || tr.startsolid )
02101 {
02102 return qfalse;
02103 }
02104 if ( tr.fraction < 1.0 )
02105 {
02106 VectorCopy( start, end );
02107 end[i] += maxs[i]-(mins[i]*tr.fraction);
02108 trap_Trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask );
02109 if ( tr.allsolid || tr.startsolid )
02110 {
02111 return qfalse;
02112 }
02113 if ( tr.fraction < 1.0 )
02114 {
02115 return qfalse;
02116 }
02117 VectorCopy( end, start );
02118 }
02119 }
02120
02121 trap_Trace( &tr, start, mins, maxs, start, ignore, clipmask );
02122 if ( tr.allsolid || tr.startsolid )
02123 {
02124 return qfalse;
02125 }
02126 VectorCopy( start, point );
02127 return qtrue;
02128 }
02129
02130 extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result );
02131 float ShortestLineSegBewteen2LineSegs( vec3_t start1, vec3_t end1, vec3_t start2, vec3_t end2, vec3_t close_pnt1, vec3_t close_pnt2 )
02132 {
02133 float current_dist, new_dist;
02134 vec3_t new_pnt;
02135
02136
02137
02138
02139
02140
02141
02142 vec3_t start_dif;
02143 vec3_t v1;
02144 vec3_t v2;
02145 float v1v1, v2v2, v1v2;
02146 float denom;
02147
02148 VectorSubtract( start2, start1, start_dif );
02149
02150 VectorSubtract( end1, start1, v1 );
02151
02152 VectorSubtract( end2, start2, v2 );
02153
02154 v1v1 = DotProduct( v1, v1 );
02155 v2v2 = DotProduct( v2, v2 );
02156 v1v2 = DotProduct( v1, v2 );
02157
02158
02159
02160 denom = (v1v2 * v1v2) - (v1v1 * v2v2);
02161
02162
02163 if ( fabs(denom) > 0.001f )
02164 {
02165 float s = -( (v2v2*DotProduct( v1, start_dif )) - (v1v2*DotProduct( v2, start_dif )) ) / denom;
02166 float t = ( (v1v1*DotProduct( v2, start_dif )) - (v1v2*DotProduct( v1, start_dif )) ) / denom;
02167 qboolean done = qtrue;
02168
02169 if ( s < 0 )
02170 {
02171 done = qfalse;
02172 s = 0;
02173 }
02174
02175 if ( s > 1 )
02176 {
02177 done = qfalse;
02178 s = 1;
02179 }
02180
02181 if ( t < 0 )
02182 {
02183 done = qfalse;
02184 t = 0;
02185 }
02186
02187 if ( t > 1 )
02188 {
02189 done = qfalse;
02190 t = 1;
02191 }
02192
02193
02194 VectorMA( start1, s, v1, close_pnt1 );
02195
02196 VectorMA( start2, t, v2, close_pnt2 );
02197
02198 current_dist = Distance( close_pnt1, close_pnt2 );
02199
02200 if ( done )
02201 {
02202 return current_dist;
02203 }
02204
02205
02206
02207
02208 }
02209 else
02210 {
02211
02212 current_dist = Q3_INFINITE;
02213 }
02214
02215
02216
02217
02218
02219
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236 new_dist = Distance( start1, start2 );
02237 if ( new_dist < current_dist )
02238 {
02239 VectorCopy( start1, close_pnt1 );
02240 VectorCopy( start2, close_pnt2 );
02241 current_dist = new_dist;
02242 }
02243
02244 new_dist = Distance( start1, end2 );
02245 if ( new_dist < current_dist )
02246 {
02247 VectorCopy( start1, close_pnt1 );
02248 VectorCopy( end2, close_pnt2 );
02249 current_dist = new_dist;
02250 }
02251
02252 new_dist = Distance( end1, start2 );
02253 if ( new_dist < current_dist )
02254 {
02255 VectorCopy( end1, close_pnt1 );
02256 VectorCopy( start2, close_pnt2 );
02257 current_dist = new_dist;
02258 }
02259
02260 new_dist = Distance( end1, end2 );
02261 if ( new_dist < current_dist )
02262 {
02263 VectorCopy( end1, close_pnt1 );
02264 VectorCopy( end2, close_pnt2 );
02265 current_dist = new_dist;
02266 }
02267
02268
02269
02270 G_FindClosestPointOnLineSegment( start2, end2, start1, new_pnt );
02271 new_dist = Distance( start1, new_pnt );
02272 if ( new_dist < current_dist )
02273 {
02274 VectorCopy( start1, close_pnt1 );
02275 VectorCopy( new_pnt, close_pnt2 );
02276 current_dist = new_dist;
02277 }
02278
02279 G_FindClosestPointOnLineSegment( start2, end2, end1, new_pnt );
02280 new_dist = Distance( end1, new_pnt );
02281 if ( new_dist < current_dist )
02282 {
02283 VectorCopy( end1, close_pnt1 );
02284 VectorCopy( new_pnt, close_pnt2 );
02285 current_dist = new_dist;
02286 }
02287
02288 G_FindClosestPointOnLineSegment( start1, end1, start2, new_pnt );
02289 new_dist = Distance( start2, new_pnt );
02290 if ( new_dist < current_dist )
02291 {
02292 VectorCopy( new_pnt, close_pnt1 );
02293 VectorCopy( start2, close_pnt2 );
02294 current_dist = new_dist;
02295 }
02296
02297 G_FindClosestPointOnLineSegment( start1, end1, end2, new_pnt );
02298 new_dist = Distance( end2, new_pnt );
02299 if ( new_dist < current_dist )
02300 {
02301 VectorCopy( new_pnt, close_pnt1 );
02302 VectorCopy( end2, close_pnt2 );
02303 current_dist = new_dist;
02304 }
02305
02306 return current_dist;
02307 }
02308
02309 void GetAnglesForDirection( const vec3_t p1, const vec3_t p2, vec3_t out )
02310 {
02311 vec3_t v;
02312
02313 VectorSubtract( p2, p1, v );
02314 vectoangles( v, out );
02315 }