1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182 |
- // g_utils.c -- misc utility functions for game module
- // leave this line at the top for all g_xxxx.cpp files...
- #include "g_headers.h"
- #include "Q3_Interface.h"
- #include "g_local.h"
- #include "g_functions.h"
- #ifdef _XBOX
- #include "anims.h"
- #include "..\renderer\mdx_format.h"
- #include "..\qcommon\cm_public.h"
- #include "..\qcommon\cm_local.h"
- #endif
- #define ACT_ACTIVE qtrue
- #define ACT_INACTIVE qfalse
- extern void NPC_UseResponse ( gentity_t *self, gentity_t *user, qboolean useWhenDone );
- extern qboolean PM_CrouchAnim( int anim );
- /*
- =========================================================================
- model / sound configstring indexes
- =========================================================================
- */
- /*
- ================
- G_FindConfigstringIndex
- ================
- */
- extern void ForceTelepathy( gentity_t *self );
- int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create ) {
- int i;
- char s[MAX_STRING_CHARS];
- if ( !name || !name[0] ) {
- return 0;
- }
- for ( i=1 ; i<max ; i++ ) {
- gi.GetConfigstring( start + i, s, sizeof( s ) );
- if ( !s[0] ) {
- break;
- }
- if ( !stricmp( s, name ) ) {
- return i;
- }
- }
- if ( !create ) {
- return 0;
- }
- if ( i == max ) {
- G_Error( "G_FindConfigstringIndex: overflow adding %s to set %d-%d", name, start, max );
- }
- gi.SetConfigstring( start + i, name );
- return i;
- }
- /*
- Ghoul2 Insert Start
- */
- int G_SkinIndex( const char *name ) {
- return G_FindConfigstringIndex (name, CS_CHARSKINS, MAX_CHARSKINS, qtrue);
- }
- /*
- Ghoul2 Insert End
- */
- int G_ModelIndex( const char *name ) {
- return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
- }
- int G_SoundIndex( const char *name ) {
- assert( name && name[0] );
- char stripped[MAX_QPATH];
- COM_StripExtension(name, stripped);
- return G_FindConfigstringIndex (stripped, CS_SOUNDS, MAX_SOUNDS, qtrue);
- }
- int G_EffectIndex( const char *name )
- {
- char temp[MAX_QPATH];
- // We just don't want extensions on the things we are registering
- COM_StripExtension( name, temp );
- return G_FindConfigstringIndex( temp, CS_EFFECTS, MAX_FX, qtrue );
- }
- int G_BSPIndex( char *name )
- {
- return G_FindConfigstringIndex (name, CS_BSP_MODELS, MAX_SUB_BSP, qtrue);
- }
- #define FX_ENT_RADIUS 32
- //-----------------------------
- // Effect playing utilities
- //-----------------------------
- //-----------------------------
- void G_PlayEffect( int fxID, const vec3_t origin, const vec3_t fwd )
- {
- gentity_t *tent;
- vec3_t temp;
- tent = G_TempEntity( origin, EV_PLAY_EFFECT );
- tent->s.eventParm = fxID;
- VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( tent->maxs, -1, tent->mins );
- VectorCopy( fwd, tent->pos3 );
- // Assume angles, we'll do a cross product on the other end to finish up
- MakeNormalVectors( fwd, tent->pos4, temp );
- gi.linkentity( tent );
- }
- // Play an effect at the origin of the specified entity
- //----------------------------
- void G_PlayEffect( int fxID, int entNum, const vec3_t fwd )
- {
- gentity_t *tent;
- vec3_t temp;
- tent = G_TempEntity( g_entities[entNum].currentOrigin, EV_PLAY_EFFECT );
- tent->s.eventParm = fxID;
- tent->s.otherEntityNum = entNum;
- VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( tent->maxs, -1, tent->mins );
- VectorCopy( fwd, tent->pos3 );
- // Assume angles, we'll do a cross product on the other end to finish up
- MakeNormalVectors( fwd, tent->pos4, temp );
- }
- // Play an effect bolted onto the muzzle of the specified client
- //----------------------------
- void G_PlayEffect( const char *name, int clientNum )
- {
- gentity_t *tent;
- tent = G_TempEntity( g_entities[clientNum].currentOrigin, EV_PLAY_MUZZLE_EFFECT );
- tent->s.eventParm = G_EffectIndex( name );
- tent->s.otherEntityNum = clientNum;
- VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( tent->maxs, -1, tent->mins );
- }
- #ifdef _IMMERSION
- // Play an effect at a position on an entity.
- // Mostly for G_MissileBounceEffect so we can play an effect on the bouncee.
- //-----------------------------
- void G_PlayEffect( int fxID, int clientNum, const vec3_t origin, const vec3_t fwd )
- {
- gentity_t *tent;
- vec3_t temp;
- tent = G_TempEntity( origin, EV_PLAY_EFFECT );
- tent->s.eventParm = fxID;
- if ( clientNum != -1 )
- {
- tent->s.saberActive = 1;
- tent->s.otherEntityNum = clientNum;
- }
- VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( tent->maxs, -1, tent->mins );
- VectorCopy( fwd, tent->pos3 );
- // Assume angles, we'll do a cross product on the other end to finish up
- MakeNormalVectors( fwd, tent->pos4, temp );
- }
- //-----------------------------
- void G_PlayEffect( const char *name, int clientNum, const vec3_t origin, const vec3_t fwd )
- {
- G_PlayEffect( G_EffectIndex( name ), clientNum, origin, fwd );
- }
- #endif // _IMMERSION
- //-----------------------------
- void G_PlayEffect( int fxID, const vec3_t origin, const vec3_t axis[3] )
- {
- gentity_t *tent;
- tent = G_TempEntity( origin, EV_PLAY_EFFECT );
- tent->s.eventParm = fxID;
- VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( tent->maxs, -1, tent->mins );
- // We can just assume axis[2] from doing a cross product on these.
- VectorCopy( axis[0], tent->pos3 );
- VectorCopy( axis[1], tent->pos4 );
- }
- // Effect playing utilities - bolt an effect to a ghoul2 models bolton point
- //-----------------------------
- void G_PlayEffect( int fxID, const int modelIndex, const int boltIndex, const int entNum, const vec3_t origin, int iLoopTime, qboolean isRelative )//iLoopTime 0 = not looping, 1 for infinite, else duration
- {
- gentity_t *tent;
- tent = G_TempEntity( origin, EV_PLAY_EFFECT );
- tent->s.eventParm = fxID;
- tent->s.loopSound = iLoopTime;
- tent->s.weapon = isRelative;
- tent->svFlags |=SVF_BROADCAST;
- gi.G2API_AttachEnt(&tent->s.boltInfo, &g_entities[entNum].ghoul2[modelIndex], boltIndex, entNum, modelIndex);
- }
- //-----------------------------
- void G_PlayEffect( const char *name, const vec3_t origin )
- {
- vec3_t up = {0, 0, 1};
- G_PlayEffect( G_EffectIndex( name ), origin, up );
- }
- //-----------------------------
- void G_PlayEffect( const char *name, const vec3_t origin, const vec3_t fwd )
- {
- G_PlayEffect( G_EffectIndex( name ), origin, fwd );
- }
- //-----------------------------
- void G_PlayEffect( const char *name, const vec3_t origin, const vec3_t axis[3] )
- {
- G_PlayEffect( G_EffectIndex( name ), origin, axis );
- }
- void G_StopEffect( int fxID, const int modelIndex, const int boltIndex, const int entNum )
- {
- gentity_t *tent;
- tent = G_TempEntity( g_entities[entNum].currentOrigin, EV_STOP_EFFECT );
- tent->s.eventParm = fxID;
- tent->svFlags |= SVF_BROADCAST;
- gi.G2API_AttachEnt( &tent->s.boltInfo, &g_entities[entNum].ghoul2[modelIndex], boltIndex, entNum, modelIndex );
- }
- void G_StopEffect(const char *name, const int modelIndex, const int boltIndex, const int entNum )
- {
- G_StopEffect( G_EffectIndex( name ), modelIndex, boltIndex, entNum );
- }
- #ifdef _XBOX
- //-----------------------------
- void G_EntityPosition( int i, vec3_t ret )
- {
- if ( /*g_entities &&*/ i >= 0 && i < MAX_GENTITIES && g_entities[i].inuse)
- {
- gentity_t *ent = g_entities + i;
- if (ent->bmodel)
- {
- vec3_t mins, maxs;
- clipHandle_t h = CM_InlineModel( ent->s.modelindex );
- CM_ModelBounds( cmg, h, mins, maxs );
- ret[0] = (mins[0] + maxs[0]) / 2 + ent->currentOrigin[0];
- ret[1] = (mins[1] + maxs[1]) / 2 + ent->currentOrigin[1];
- ret[2] = (mins[2] + maxs[2]) / 2 + ent->currentOrigin[2];
- }
- else
- {
- VectorCopy(g_entities[i].currentOrigin, ret);
- }
- }
- else
- {
- ret[0] = ret[1] = ret[2] = 0;
- }
- }
- #endif
- //===Bypass network for sounds on specific channels====================
- extern void cgi_S_StartSound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx );
- #include "..\cgame\cg_media.h" //access to cgs
- extern qboolean CG_TryPlayCustomSound( vec3_t origin, int entityNum, soundChannel_t channel, const char *soundName, int customSoundSet );
- extern cvar_t *g_timescale;
- //NOTE: Do NOT Try to use this before the cgame DLL is valid, it will NOT work!
- void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath )
- {
- int index = G_SoundIndex( (char *)soundPath );
- if ( !ent )
- {
- return;
- }
- if ( g_timescale->integer > 50 )
- {//Skip the sound!
- return;
- }
- cgi_S_UpdateEntityPosition( ent->s.number, ent->currentOrigin );
- if ( cgs.sound_precache[ index ] )
- {
- cgi_S_StartSound( NULL, ent->s.number, channel, cgs.sound_precache[ index ] );
- }
- else
- {
- CG_TryPlayCustomSound( NULL, ent->s.number, channel, soundPath, -1 );
- }
- }
- void G_SoundIndexOnEnt( gentity_t *ent, soundChannel_t channel, int index )
- {
- if ( !ent )
- {
- return;
- }
- cgi_S_UpdateEntityPosition( ent->s.number, ent->currentOrigin );
- if ( cgs.sound_precache[ index ] )
- {
- cgi_S_StartSound( NULL, ent->s.number, channel, cgs.sound_precache[ index ] );
- }
- }
- extern cvar_t *g_skippingcin;
- void G_SpeechEvent( gentity_t *self, int event )
- {
- if ( in_camera
- && g_skippingcin
- && g_skippingcin->integer )
- {//Skipping a cinematic, so skip NPC voice sounds...
- return;
- }
- //update entity pos, too
- cgi_S_UpdateEntityPosition( self->s.number, self->currentOrigin );
- switch ( event )
- {
- case EV_ANGER1: //Say when acquire an enemy when didn't have one before
- case EV_ANGER2:
- case EV_ANGER3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*anger%i.wav", event - EV_ANGER1 + 1), CS_COMBAT );
- break;
- case EV_VICTORY1: //Say when killed an enemy
- case EV_VICTORY2:
- case EV_VICTORY3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*victory%i.wav", event - EV_VICTORY1 + 1), CS_COMBAT );
- break;
- case EV_CONFUSE1: //Say when confused
- case EV_CONFUSE2:
- case EV_CONFUSE3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*confuse%i.wav", event - EV_CONFUSE1 + 1), CS_COMBAT );
- break;
- case EV_PUSHED1: //Say when pushed
- case EV_PUSHED2:
- case EV_PUSHED3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*pushed%i.wav", event - EV_PUSHED1 + 1), CS_COMBAT );
- break;
- case EV_CHOKE1: //Say when choking
- case EV_CHOKE2:
- case EV_CHOKE3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*choke%i.wav", event - EV_CHOKE1 + 1), CS_COMBAT );
- break;
- case EV_FFWARN: //Warn ally to stop shooting you
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*ffwarn.wav", CS_COMBAT );
- break;
- case EV_FFTURN: //Turn on ally after being shot by them
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*ffturn.wav", CS_COMBAT );
- break;
- //extra sounds for ST
- case EV_CHASE1:
- case EV_CHASE2:
- case EV_CHASE3:
- if ( !CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*chase%i.wav", event - EV_CHASE1 + 1), CS_EXTRA ) )
- {
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*anger%i.wav", Q_irand(1,3)), CS_COMBAT );
- }
- break;
- case EV_COVER1:
- case EV_COVER2:
- case EV_COVER3:
- case EV_COVER4:
- case EV_COVER5:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*cover%i.wav", event - EV_COVER1 + 1), CS_EXTRA );
- break;
- case EV_DETECTED1:
- case EV_DETECTED2:
- case EV_DETECTED3:
- case EV_DETECTED4:
- case EV_DETECTED5:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*detected%i.wav", event - EV_DETECTED1 + 1), CS_EXTRA );
- break;
- case EV_GIVEUP1:
- case EV_GIVEUP2:
- case EV_GIVEUP3:
- case EV_GIVEUP4:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*giveup%i.wav", event - EV_GIVEUP1 + 1), CS_EXTRA );
- break;
- case EV_LOOK1:
- case EV_LOOK2:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*look%i.wav", event - EV_LOOK1 + 1), CS_EXTRA );
- break;
- case EV_LOST1:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*lost1.wav", CS_EXTRA );
- break;
- case EV_OUTFLANK1:
- case EV_OUTFLANK2:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*outflank%i.wav", event - EV_OUTFLANK1 + 1), CS_EXTRA );
- break;
- case EV_ESCAPING1:
- case EV_ESCAPING2:
- case EV_ESCAPING3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*escaping%i.wav", event - EV_ESCAPING1 + 1), CS_EXTRA );
- break;
- case EV_SIGHT1:
- case EV_SIGHT2:
- case EV_SIGHT3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*sight%i.wav", event - EV_SIGHT1 + 1), CS_EXTRA );
- break;
- case EV_SOUND1:
- case EV_SOUND2:
- case EV_SOUND3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*sound%i.wav", event - EV_SOUND1 + 1), CS_EXTRA );
- break;
- case EV_SUSPICIOUS1:
- case EV_SUSPICIOUS2:
- case EV_SUSPICIOUS3:
- case EV_SUSPICIOUS4:
- case EV_SUSPICIOUS5:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*suspicious%i.wav", event - EV_SUSPICIOUS1 + 1), CS_EXTRA );
- break;
- //extra sounds for Jedi
- case EV_COMBAT1:
- case EV_COMBAT2:
- case EV_COMBAT3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*combat%i.wav", event - EV_COMBAT1 + 1), CS_JEDI );
- break;
- case EV_JDETECTED1:
- case EV_JDETECTED2:
- case EV_JDETECTED3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jdetected%i.wav", event - EV_JDETECTED1 + 1), CS_JEDI );
- break;
- case EV_TAUNT1:
- case EV_TAUNT2:
- case EV_TAUNT3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*taunt%i.wav", event - EV_TAUNT1 + 1), CS_JEDI );
- break;
- case EV_JCHASE1:
- case EV_JCHASE2:
- case EV_JCHASE3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jchase%i.wav", event - EV_JCHASE1 + 1), CS_JEDI );
- break;
- case EV_JLOST1:
- case EV_JLOST2:
- case EV_JLOST3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jlost%i.wav", event - EV_JLOST1 + 1), CS_JEDI );
- break;
- case EV_DEFLECT1:
- case EV_DEFLECT2:
- case EV_DEFLECT3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*deflect%i.wav", event - EV_DEFLECT1 + 1), CS_JEDI );
- break;
- case EV_GLOAT1:
- case EV_GLOAT2:
- case EV_GLOAT3:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*gloat%i.wav", event - EV_GLOAT1 + 1), CS_JEDI );
- break;
- case EV_PUSHFAIL:
- CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*pushfail.wav", CS_JEDI );
- break;
- }
- }
- //=====================================================================
- /*
- =============
- G_Find
- Searches all active entities for the next one that holds
- the matching string at fieldofs (use the FOFS() macro) in the structure.
- Searches beginning at the entity after from, or the beginning if NULL
- NULL will be returned if the end of the list is reached.
- =============
- */
- gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
- {
- char *s;
-
- if(!match || !match[0])
- {
- return NULL;
- }
- if (!from)
- from = g_entities;
- else
- from++;
- // for ( ; from < &g_entities[globals.num_entities] ; from++)
- int i=from-g_entities;
- for ( ; i < globals.num_entities ; i++)
- {
- // if (!from->inuse)
- if(!PInUse(i))
- continue;
- from=&g_entities[i];
- s = *(char **) ((byte *)from + fieldofs);
- if (!s)
- continue;
- if (!Q_stricmp (s, match))
- return from;
- }
- return NULL;
- }
- /*
- ============
- G_RadiusList - given an origin and a radius, return all entities that are in use that are within the list
- ============
- */
- int G_RadiusList ( vec3_t origin, float radius, gentity_t *ignore, qboolean takeDamage, gentity_t *ent_list[MAX_GENTITIES])
- {
- float dist;
- gentity_t *ent;
- gentity_t *entityList[MAX_GENTITIES];
- int numListedEntities;
- vec3_t mins, maxs;
- vec3_t v;
- int i, e;
- int ent_count = 0;
- if ( radius < 1 )
- {
- radius = 1;
- }
- for ( i = 0 ; i < 3 ; i++ )
- {
- mins[i] = origin[i] - radius;
- maxs[i] = origin[i] + radius;
- }
- numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
- radius *= radius; //square for the length squared below
- for ( e = 0 ; e < numListedEntities ; e++ )
- {
- ent = entityList[ e ];
- if ((ent == ignore) || !(ent->inuse) || ent->takedamage != takeDamage)
- continue;
- // find the distance from the edge of the bounding box
- for ( i = 0 ; i < 3 ; i++ )
- {
- if ( origin[i] < ent->absmin[i] )
- {
- v[i] = ent->absmin[i] - origin[i];
- } else if ( origin[i] > ent->absmax[i] )
- {
- v[i] = origin[i] - ent->absmax[i];
- } else
- {
- v[i] = 0;
- }
- }
- dist = VectorLengthSquared( v );
- if ( dist >= radius )
- {
- continue;
- }
-
- // ok, we are within the radius, add us to the incoming list
- ent_list[ent_count] = ent;
- ent_count++;
- }
- // we are done, return how many we found
- return(ent_count);
- }
- /*
- =============
- G_PickTarget
- Selects a random entity from among the targets
- =============
- */
- #define MAXCHOICES 32
- gentity_t *G_PickTarget (char *targetname)
- {
- gentity_t *ent = NULL;
- int num_choices = 0;
- gentity_t *choice[MAXCHOICES];
- if (!targetname)
- {
- gi.Printf("G_PickTarget called with NULL targetname\n");
- return NULL;
- }
- while(1)
- {
- ent = G_Find (ent, FOFS(targetname), targetname);
- if (!ent)
- break;
- choice[num_choices++] = ent;
- if (num_choices == MAXCHOICES)
- break;
- }
- if (!num_choices)
- {
- gi.Printf("G_PickTarget: target %s not found\n", targetname);
- return NULL;
- }
- return choice[rand() % num_choices];
- }
- void G_UseTargets2 (gentity_t *ent, gentity_t *activator, const char *string)
- {
- gentity_t *t;
-
- //
- // fire targets
- //
- if (string)
- {
- if( !stricmp( string, "self") )
- {
- t = ent;
- if (t->e_UseFunc != useF_NULL) // check can be omitted
- {
- GEntity_UseFunc(t, ent, activator);
- }
- if (!ent->inuse)
- {
- gi.Printf("entity was removed while using targets\n");
- return;
- }
- }
- else
- {
- t = NULL;
- while ( (t = G_Find (t, FOFS(targetname), (char *) string)) != NULL )
- {
- if (t == ent)
- {
- // gi.Printf ("WARNING: Entity used itself.\n");
- }
- if (t->e_UseFunc != useF_NULL) // check can be omitted
- {
- GEntity_UseFunc(t, ent, activator);
- }
- if (!ent->inuse)
- {
- gi.Printf("entity was removed while using targets\n");
- return;
- }
- }
- }
- }
- }
- /*
- ==============================
- G_UseTargets
- "activator" should be set to the entity that initiated the firing.
- Search for (string)targetname in all entities that
- match (string)self.target and call their .use function
- ==============================
- */
- void G_UseTargets (gentity_t *ent, gentity_t *activator)
- {
- //
- // fire targets
- //
- G_UseTargets2 (ent, activator, ent->target);
- }
- /*
- =============
- VectorToString
- This is just a convenience function
- for printing vectors
- =============
- */
- char *vtos( const vec3_t v ) {
- static int index;
- static char str[8][32];
- char *s;
- // use an array so that multiple vtos won't collide
- s = str[index];
- index = (index + 1)&7;
- Com_sprintf (s, 32, "(%4.2f %4.2f %4.2f)", v[0], v[1], v[2]);
- return s;
- }
- /*
- ===============
- G_SetMovedir
- The editor only specifies a single value for angles (yaw),
- but we have special constants to generate an up or down direction.
- Angles will be cleared, because it is being used to represent a direction
- instead of an orientation.
- ===============
- */
- void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
- static vec3_t VEC_UP = {0, -1, 0};
- static vec3_t MOVEDIR_UP = {0, 0, 1};
- static vec3_t VEC_DOWN = {0, -2, 0};
- static vec3_t MOVEDIR_DOWN = {0, 0, -1};
- if ( VectorCompare (angles, VEC_UP) ) {
- VectorCopy (MOVEDIR_UP, movedir);
- } else if ( VectorCompare (angles, VEC_DOWN) ) {
- VectorCopy (MOVEDIR_DOWN, movedir);
- } else {
- AngleVectors (angles, movedir, NULL, NULL);
- }
- VectorClear( angles );
- }
- float vectoyaw( const vec3_t vec ) {
- float yaw;
-
- if (vec[YAW] == 0 && vec[PITCH] == 0) {
- yaw = 0;
- } else {
- if (vec[PITCH]) {
- yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
- } else if (vec[YAW] > 0) {
- yaw = 90;
- } else {
- yaw = 270;
- }
- if (yaw < 0) {
- yaw += 360;
- }
- }
- return yaw;
- }
- void G_InitGentity( gentity_t *e, qboolean bFreeG2 )
- {
- e->inuse = qtrue;
- SetInUse(e);
- e->m_iIcarusID = IIcarusInterface::ICARUS_INVALID;
- e->classname = "noclass";
- e->s.number = e - g_entities;
-
- // remove any ghoul2 models here in case we're reusing
- if (bFreeG2 && e->ghoul2.IsValid())
- {
- gi.G2API_CleanGhoul2Models(e->ghoul2);
- }
- //Navigational setups
- e->waypoint = WAYPOINT_NONE;
- e->lastWaypoint = WAYPOINT_NONE;
- }
- /*
- =================
- G_Spawn
- Either finds a free entity, or allocates a new one.
- The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
- never be used by anything else.
- Try to avoid reusing an entity that was recently freed, because it
- can cause the client to think the entity morphed into something else
- instead of being removed and recreated, which can cause interpolated
- angles and bad trails.
- =================
- */
- extern void G_MassFreeUselessThings( void );
- gentity_t *G_Spawn( int itr )
- {
- int i, force;
- gentity_t *e;
- e = NULL; // shut up warning
- i = 0; // shut up warning
- for ( force = 0 ; force < 2 ; force++ )
- {
- // if we go through all entities and can't find one to free,
- // override the normal minimum times before use
- e = &g_entities[MAX_CLIENTS];
- // for ( i = MAX_CLIENTS ; i<globals.num_entities ; i++, e++)
- // {
- // if ( e->inuse )
- // {
- // continue;
- // }
- for ( i = MAX_CLIENTS ; i<globals.num_entities ; i++)
- {
- if(PInUse(i))
- {
- continue;
- }
- e=&g_entities[i];
- // the first couple seconds of server time can involve a lot of
- // freeing and allocating, so relax the replacement policy
- if ( !force && e->freetime > 2000 && level.time - e->freetime < 1000 ) {
- continue;
- }
- // reuse this slot
- G_InitGentity( e, qtrue );
- return e;
- }
- e=&g_entities[i];
- if ( i != ENTITYNUM_MAX_NORMAL )
- {
- break;
- }
- }
- if ( i == ENTITYNUM_MAX_NORMAL )
- {
- #ifndef _XBOX
- //#ifndef FINAL_BUILD
- e = &g_entities[0];
- //--------------Use this to dump directly to a file
- char buff[256];
- FILE *fp;
- fp = fopen( "c:/nofreeentities.txt", "w" );
- for ( i = 0 ; i<globals.num_entities ; i++, e++)
- {
- if ( e->classname )
- {
- sprintf( buff, "%d: %s\n", i, e->classname );
- }
- fputs( buff, fp );
- }
- fclose( fp );
- /*
- //---------------Or use this to dump to the console -- beware though, the console will fill quickly and you probably won't see the full list
- for ( i = 0 ; i<globals.num_entities ; i++, e++)
- {
- if ( e->classname )
- {
- Com_Printf( "%d: %s\n", i, e->classname );
- }
- }
- */
- #endif//FINAL_BUILD
- if(itr)
- {
- G_MassFreeUselessThings();
- return G_Spawn( 0 );
- }
- else
- {
- G_Error( "G_Spawn: no free entities" );
- }
- }
-
- // open up a new slot
- globals.num_entities++;
- G_InitGentity( e, qtrue );
- return e;
- }
- extern void Vehicle_Remove(gentity_t *ent);
- /*
- =================
- G_FreeEntity
- Marks the entity as free
- =================
- */
- extern short max_speeders;
- extern char current_speeders;
- void G_FreeEntity( gentity_t *ed ) {
- if( ed->m_pVehicle &&
- ed->m_pVehicle->m_pVehicleInfo &&
- ed->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER)
- {
- // if this speeder was destroyed by cleanup code, alreadCleaned will be set, so
- // don't account for it again, otherwise the death occured somewhere else
- if(!ed->m_pVehicle->alreadyCleaned)
- current_speeders--;
- }
- gi.unlinkentity (ed); // unlink from world
- // Free the Game Element (the entity) and delete the Icarus ID.
- Quake3Game()->FreeEntity( ed );
- /*if ( ed->neverFree ) {
- return;
- }*/
- if (ed->wayedge!=0)
- {
- NAV::WayEdgesNowClear(ed);
- }
- // remove any ghoul2 models here
- gi.G2API_CleanGhoul2Models(ed->ghoul2);
- if (ed->client && ed->client->NPC_class == CLASS_VEHICLE)
- {
- int iVehIndex = BG_VehicleGetIndex( ed->NPC_type );
- Vehicle_Remove(ed);
- if ( ed->m_pVehicle )
- {
- gi.Free( ed->m_pVehicle );
- }
- //CVehicleNPC *pVeh = static_cast< CVehicleNPC * >( ed->NPC );
- //delete pVeh;
- //gi.Free((char*)ed->NPC-4);//crazy hack for class vtables
- }
- //free this stuff now, rather than waiting until the level ends.
- if (ed->NPC)
- {
- gi.Free(ed->NPC);
- if(ed->client->clientInfo.customBasicSoundDir && gi.bIsFromZone(ed->client->clientInfo.customBasicSoundDir, TAG_G_ALLOC)) {
- gi.Free(ed->client->clientInfo.customBasicSoundDir);
- }
- if(ed->client->clientInfo.customCombatSoundDir) {
- assert(*(int*)ed->client->clientInfo.customCombatSoundDir != 0xfeeefeee);
- gi.Free(ed->client->clientInfo.customCombatSoundDir);
- }
- if(ed->client->clientInfo.customExtraSoundDir) {
- assert(*(int*)ed->client->clientInfo.customExtraSoundDir != 0xfeeefeee);
- gi.Free(ed->client->clientInfo.customExtraSoundDir);
- }
- if(ed->client->clientInfo.customJediSoundDir) {
- gi.Free(ed->client->clientInfo.customJediSoundDir);
- }
- if(ed->client->ps.saber[0].name && gi.bIsFromZone(ed->client->ps.saber[0].name, TAG_G_ALLOC) ) {
- gi.Free(ed->client->ps.saber[0].name);
- }
- if(ed->client->ps.saber[0].model && gi.bIsFromZone(ed->client->ps.saber[0].model, TAG_G_ALLOC) ) {
- gi.Free(ed->client->ps.saber[0].model);
- }
- if(ed->client->ps.saber[1].name && gi.bIsFromZone(ed->client->ps.saber[1].name, TAG_G_ALLOC) ) {
- gi.Free(ed->client->ps.saber[1].name);
- }
- if(ed->client->ps.saber[1].model && gi.bIsFromZone(ed->client->ps.saber[1].model, TAG_G_ALLOC) ) {
- gi.Free(ed->client->ps.saber[1].model);
- }
- gi.Free(ed->client);
- }
- if (ed->soundSet && gi.bIsFromZone(ed->soundSet, TAG_G_ALLOC)) {
- gi.Free(ed->soundSet);
- }
- if (ed->targetname && gi.bIsFromZone(ed->targetname, TAG_G_ALLOC)) {
- gi.Free(ed->targetname);
- }
- if (ed->NPC_targetname && gi.bIsFromZone(ed->NPC_targetname, TAG_G_ALLOC)) {
- gi.Free(ed->NPC_targetname);
- }
- if (ed->NPC_type && gi.bIsFromZone(ed->NPC_type, TAG_G_ALLOC)) {
- gi.Free(ed->NPC_type);
- }
- if (ed->classname && gi.bIsFromZone(ed->classname, TAG_G_ALLOC)) {
- gi.Free(ed->classname );
- }
- if (ed->message && gi.bIsFromZone(ed->message, TAG_G_ALLOC)) {
- gi.Free(ed->message);
- }
- if (ed->model && gi.bIsFromZone(ed->model, TAG_G_ALLOC)) {
- gi.Free(ed->model);
- }
- //scripting
- if (ed->script_targetname && gi.bIsFromZone(ed->script_targetname, TAG_G_ALLOC)) {
- gi.Free(ed->script_targetname);
- }
- if (ed->cameraGroup && gi.bIsFromZone(ed->cameraGroup, TAG_G_ALLOC)) {
- gi.Free(ed->cameraGroup);
- }
- if (ed->paintarget && gi.bIsFromZone(ed->paintarget, TAG_G_ALLOC)) {
- gi.Free(ed->paintarget);
- }
- if(ed->parms) {
- gi.Free(ed->parms);
- }
-
- //Limbs
- if (ed->target && gi.bIsFromZone(ed->target , TAG_G_ALLOC)) {
- gi.Free(ed->target);
- }
- if (ed->target2 && gi.bIsFromZone(ed->target2 , TAG_G_ALLOC)) {
- gi.Free(ed->target2);
- }
- if (ed->target3 && gi.bIsFromZone(ed->target3 , TAG_G_ALLOC)) {
- gi.Free(ed->target3);
- }
- if (ed->target4 && gi.bIsFromZone(ed->target4 , TAG_G_ALLOC)) {
- gi.Free(ed->target4);
- }
- if (ed->opentarget) {
- gi.Free(ed->opentarget);
- }
- if (ed->closetarget) {
- gi.Free(ed->closetarget);
- }
- // Free any associated timers
- TIMER_Clear(ed->s.number);
- memset (ed, 0, sizeof(*ed));
- ed->s.number = ENTITYNUM_NONE;
- ed->classname = "freed";
- ed->freetime = level.time;
- ed->inuse = qfalse;
- ClearInUse(ed);
- }
- /*
- =================
- G_TempEntity
- Spawns an event entity that will be auto-removed
- The origin will be snapped to save net bandwidth, so care
- must be taken if the origin is right on a surface (snap towards start vector first)
- =================
- */
- gentity_t *G_TempEntity( const vec3_t origin, int event ) {
- gentity_t *e;
- vec3_t snapped;
- e = G_Spawn();
- e->s.eType = ET_EVENTS + event;
- e->classname = "tempEntity";
- e->eventTime = level.time;
- e->freeAfterEvent = qtrue;
- VectorCopy( origin, snapped );
- SnapVector( snapped ); // save network bandwidth
- G_SetOrigin( e, snapped );
- // find cluster for PVS
- gi.linkentity( e );
- return e;
- }
- /*
- ==============================================================================
- Kill box
- ==============================================================================
- */
- /*
- =================
- G_KillBox
- Kills all entities that would touch the proposed new positioning
- of ent. Ent should be unlinked before calling this!
- =================
- */
- void G_KillBox (gentity_t *ent) {
- int i, num;
- gentity_t *touch[MAX_GENTITIES], *hit;
- vec3_t mins, maxs;
- VectorAdd( ent->client->ps.origin, ent->mins, mins );
- VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
- num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
- for (i=0 ; i<num ; i++) {
- hit = touch[i];
- if ( !hit->client ) {
- continue;
- }
- if ( hit == ent ) {
- continue;
- }
- if ( ent->s.number && hit->client->ps.stats[STAT_HEALTH] <= 0 )
- {//NPC
- continue;
- }
- if ( ent->s.number )
- {//NPC
- if ( !(hit->contents&CONTENTS_BODY) )
- {
- continue;
- }
- }
- else
- {//player
- if ( !(hit->contents & ent->contents) )
- {
- continue;
- }
- }
- // nail it
- G_Damage ( hit, ent, ent, NULL, NULL,
- 100000, DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
- }
- }
- //==============================================================================
- /*
- ===============
- G_AddEvent
- Adds an event+parm and twiddles the event counter
- ===============
- */
- void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
- int bits;
- if ( !event ) {
- gi.Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
- return;
- }
- #if 0 // FIXME: allow multiple events on an entity
- // if the entity has an event that hasn't expired yet, don't overwrite
- // it unless it is identical (repeated footsteps / muzzleflashes / etc )
- if ( ent->s.event && ent->s.event != event ) {
- gentity_t *temp;
- // generate a temp entity that references the original entity
- gi.Printf( "eventPush\n" );
- temp = G_Spawn();
- temp->s.eType = ET_EVENT_ONLY;
- temp->s.otherEntityNum = ent->s.number;
- G_SetOrigin( temp, ent->s.origin );
- G_AddEvent( temp, event, eventParm );
- temp->freeAfterEvent = qtrue;
- gi.linkentity( temp );
- return;
- }
- #endif
- // clients need to add the event in playerState_t instead of entityState_t
- if ( !ent->s.number ) //only one client
- {
- #if 0
- bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
- bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
- ent->client->ps.externalEvent = event | bits;
- ent->client->ps.externalEventParm = eventParm;
- ent->client->ps.externalEventTime = level.time;
- #endif
- if ( eventParm > 255 )
- {
- if ( event == EV_PAIN )
- {//must have cheated, in undying?
- eventParm = 255;
- }
- else
- {
- assert( eventParm < 256 );
- }
- }
- AddEventToPlayerstate( event, eventParm, &ent->client->ps );
- } else {
- bits = ent->s.event & EV_EVENT_BITS;
- bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
- ent->s.event = event | bits;
- ent->s.eventParm = eventParm;
- }
- ent->eventTime = level.time;
- }
- /*
- =============
- G_Sound
- =============
- */
- void G_Sound( gentity_t *ent, int soundIndex )
- {
- gentity_t *te;
- te = G_TempEntity( ent->currentOrigin, EV_GENERAL_SOUND );
- te->s.eventParm = soundIndex;
- }
- /*
- =============
- G_Sound
- =============
- */
- void G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast )
- {
- gentity_t *te;
- te = G_TempEntity( org, EV_GENERAL_SOUND );
- te->s.eventParm = soundIndex;
- if ( broadcast )
- {
- te->svFlags |= SVF_BROADCAST;
- }
- }
- /*
- =============
- G_SoundBroadcast
- Plays sound that can permeate PVS blockage
- =============
- */
- void G_SoundBroadcast( gentity_t *ent, int soundIndex )
- {
- gentity_t *te;
- te = G_TempEntity( ent->currentOrigin, EV_GLOBAL_SOUND ); //full volume
- te->s.eventParm = soundIndex;
- te->svFlags |= SVF_BROADCAST;
- }
- //==============================================================================
- #ifdef _IMMERSION
- int G_ForceIndex( const char *name, int channel )
- {
- if ( channel >= 0 && channel < FF_CHANNEL_MAX )
- {
- // no point in storing a name with a bogus channel
- // (registering an effect on multiple channels will eat up memory)
- char temp[512];
- sprintf( temp, "%d,%s", channel, name );
- return G_FindConfigstringIndex ( temp, CS_FORCES, MAX_FORCES, qtrue );
- }
- return 0;
- }
- gentity_t *G_OwnerClient( gentity_t *ent )
- {
- // This test is not adequate... should check for the following...
- // 1) owned entity is touching client entity
- // 2) owned entity is a weapon being used by client
- for
- (
- ; ent
- && !ent->client
- ; ent = ent->owner
- );
- return ent;
- }
- void G_Force( gentity_t *ent, int forceIndex )
- {
- gentity_t *client = G_OwnerClient( ent );
- if ( client )
- {
- G_AddEvent( client, EV_ENTITY_FORCE, forceIndex );
- }
- }
- void G_ForceArea( gentity_t *ent, int forceIndex )
- {
- gentity_t *te;
- te = G_TempEntity( ent->s.origin, EV_AREA_FORCE );
- te->s.eventParm = forceIndex;
- }
- void G_ForceBroadcast( gentity_t *ent, int forceIndex )
- {
- gentity_t *te;
- te = G_TempEntity( ent->s.origin, EV_GLOBAL_FORCE );
- te->s.eventParm = forceIndex;
- te->svFlags |= SVF_BROADCAST;
- }
- void G_ForceStop( gentity_t *ent, int forceIndex )
- {
- G_AddEvent( ent, EV_FORCE_STOP, forceIndex );
- }
- #endif // _IMMERSION
- /*
- ================
- G_SetOrigin
- Sets the pos trajectory for a fixed position
- ================
- */
- void G_SetOrigin( gentity_t *ent, const vec3_t origin )
- {
- VectorCopy( origin, ent->s.pos.trBase );
- if(ent->client)
- {
- VectorCopy( origin, ent->client->ps.origin );
- VectorCopy( origin, ent->s.origin );
- }
- else
- {
- ent->s.pos.trType = TR_STATIONARY;
- }
- ent->s.pos.trTime = 0;
- ent->s.pos.trDuration = 0;
- VectorClear( ent->s.pos.trDelta );
- VectorCopy( origin, ent->currentOrigin );
- // clear waypoints
- if( ent->client && ent->NPC )
- {
- ent->waypoint = 0;
- ent->lastWaypoint = 0;
- if( NAV::HasPath( ent ) )
- {
- NAV::ClearPath( ent );
- }
- }
- }
- qboolean G_CheckInSolidTeleport (const vec3_t& teleportPos, gentity_t *self)
- {
- trace_t trace;
- vec3_t end, mins;
- VectorCopy(teleportPos, end);
- end[2] += self->mins[2];
- VectorCopy(self->mins, mins);
- mins[2] = 0;
- gi.trace(&trace, teleportPos, mins, self->maxs, end, self->s.number, self->clipmask);
- if(trace.allsolid || trace.startsolid)
- {
- return qtrue;
- }
- return qfalse;
- }
- //===============================================================================
- qboolean G_CheckInSolid (gentity_t *self, qboolean fix)
- {
- trace_t trace;
- vec3_t end, mins;
- VectorCopy(self->currentOrigin, end);
- end[2] += self->mins[2];
- VectorCopy(self->mins, mins);
- mins[2] = 0;
- gi.trace(&trace, self->currentOrigin, mins, self->maxs, end, self->s.number, self->clipmask);
- if(trace.allsolid || trace.startsolid)
- {
- return qtrue;
- }
-
- #ifdef _DEBUG
- if(trace.fraction < 0.99999713)
- #else
- if(trace.fraction < 1.0)
- #endif
- {
- if(fix)
- {//Put them at end of trace and check again
- vec3_t neworg;
- VectorCopy(trace.endpos, neworg);
- neworg[2] -= self->mins[2];
- G_SetOrigin(self, neworg);
- gi.linkentity(self);
- return G_CheckInSolid(self, qfalse);
- }
- else
- {
- return qtrue;
- }
- }
-
- return qfalse;
- }
- qboolean infront(gentity_t *from, gentity_t *to)
- {
- vec3_t angles, dir, forward;
- float dot;
- angles[PITCH] = angles[ROLL] = 0;
- angles[YAW] = from->s.angles[YAW];
- AngleVectors(angles, forward, NULL, NULL);
- VectorSubtract(to->s.origin, from->s.origin, dir);
- VectorNormalize(dir);
- dot = DotProduct(forward, dir);
- if(dot < 0.0f)
- {
- return qfalse;
- }
- return qtrue;
- }
- void Svcmd_Use_f( void )
- {
- char *cmd1 = gi.argv(1);
- if ( !cmd1 || !cmd1[0] )
- {
- //FIXME: warning message
- gi.Printf( "'use' takes targetname of ent or 'list' (lists all usable ents)\n" );
- return;
- }
- else if ( !Q_stricmp("list", cmd1) )
- {
- gentity_t *ent;
- gi.Printf("Listing all usable entities:\n");
- for ( int i = 1; i < ENTITYNUM_WORLD; i++ )
- {
- ent = &g_entities[i];
- if ( ent )
- {
- if ( ent->targetname && ent->targetname[0] )
- {
- if ( ent->e_UseFunc != useF_NULL )
- {
- if ( ent->NPC )
- {
- gi.Printf( "%s (NPC)\n", ent->targetname );
- }
- else
- {
- gi.Printf( "%s\n", ent->targetname );
- }
- }
- }
- }
- }
- gi.Printf("End of list.\n");
- }
- else
- {
- G_UseTargets2( &g_entities[0], &g_entities[0], cmd1 );
- }
- }
- //======================================================
- void G_SetActiveState(char *targetstring, qboolean actState)
- {
- gentity_t *target = NULL;
- while( NULL != (target = G_Find(target, FOFS(targetname), targetstring)) )
- {
- target->svFlags = actState ? (target->svFlags&~SVF_INACTIVE) : (target->svFlags|SVF_INACTIVE);
- }
- }
- void target_activate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- G_ActivateBehavior(self,BSET_USE);
- G_SetActiveState(self->target, ACT_ACTIVE);
- }
- void target_deactivate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- G_ActivateBehavior(self,BSET_USE);
- G_SetActiveState(self->target, ACT_INACTIVE);
- }
- //FIXME: make these apply to doors, etc too?
- /*QUAKED target_activate (1 0 0) (-4 -4 -4) (4 4 4)
- Will set the target(s) to be usable/triggerable
- */
- void SP_target_activate( gentity_t *self )
- {
- G_SetOrigin( self, self->s.origin );
- self->e_UseFunc = useF_target_activate_use;
- }
- /*QUAKED target_deactivate (1 0 0) (-4 -4 -4) (4 4 4)
- Will set the target(s) to be non-usable/triggerable
- */
- void SP_target_deactivate( gentity_t *self )
- {
- G_SetOrigin( self, self->s.origin );
- self->e_UseFunc = useF_target_deactivate_use;
- }
- //======================================================
- /*
- ==============
- ValidUseTarget
- Returns whether or not the targeted entity is useable
- ==============
- */
- qboolean ValidUseTarget( gentity_t *ent )
- {
- if ( ent->e_UseFunc == useF_NULL )
- {
- return qfalse;
- }
- if ( ent->svFlags & SVF_INACTIVE )
- {//set by target_deactivate
- return qfalse;
- }
-
- if ( !(ent->svFlags & SVF_PLAYER_USABLE) )
- {//Check for flag that denotes BUTTON_USE useability
- return qfalse;
- }
-
- //FIXME: This is only a temp fix..
- if ( !Q_strncmp( ent->classname, "trigger", 7) )
- {
- return qfalse;
- }
- return qtrue;
- }
- static void DebugTraceForNPC(gentity_t *ent)
- {
- trace_t trace;
- vec3_t src, dest, vf;
- VectorCopy( ent->client->renderInfo.eyePoint, src );
-
- AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically
- //extend to find end of use trace
- VectorMA( src, 4096, vf, dest );
- //Trace ahead to find a valid target
- gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE );
- if (trace.fraction < 0.99f)
- {
- gentity_t *found = &g_entities[trace.entityNum];
- if (found)
- {
- const char *targetName = found->targetname;
- const char *className = found->classname;
- if (targetName == 0)
- {
- targetName = "<NULL>";
- }
- if (className == 0)
- {
- className = "<NULL>";
- }
- Com_Printf("found targetname '%s', classname '%s'\n", targetName, className);
- }
- }
- }
- static qboolean G_ValidActivateBehavior (gentity_t* self, int bset)
- {
- if ( !self )
- {
- return qfalse;
- }
- const char *bs_name = self->behaviorSet[bset];
-
- if( !(VALIDSTRING( bs_name )) )
- {
- return qfalse;
- }
- return qtrue;
- }
- static qboolean G_IsTriggerUsable(gentity_t* self, gentity_t* other)
- {
- if ( self->svFlags & SVF_INACTIVE )
- {//set by target_deactivate
- return qfalse;
- }
- if( self->noDamageTeam )
- {
- if ( other->client->playerTeam != self->noDamageTeam )
- {
- return qfalse;
- }
- }
- if ( self->spawnflags & 4 )
- {//USE_BUTTON
- if ( !other->client )
- {
- return qfalse;
- }
- }
- else
- {
- return qfalse;
- }
- if ( self->spawnflags & 2 )
- {//FACING
- vec3_t forward;
- if ( other->client )
- {
- AngleVectors( other->client->ps.viewangles, forward, NULL, NULL );
- }
- else
- {
- AngleVectors( other->currentAngles, forward, NULL, NULL );
- }
- if ( DotProduct( self->movedir, forward ) < 0.5 )
- {//Not Within 45 degrees
- return qfalse;
- }
- }
- if ((!G_ValidActivateBehavior (self, BSET_USE) && !self->target) ||
- (self->target &&
- (stricmp(self->target, "n") == 0 ||
- (stricmp(self->target, "neveropen") == 0 ||
- (stricmp(self->target, "run_gran_drop") == 0) ||
- (stricmp(self->target, "speaker") == 0) ||
- (stricmp(self->target, "locked") == 0)
- ))))
- {
- return qfalse;
- }
- /*
- //NOTE: This doesn't stop you from using it, just delays the use action!
- if(self->delay && self->painDebounceTime < (level.time + self->delay) )
- {
- return qfalse;
- }
- */
- return qtrue;
- }
- static qboolean CanUseInfrontOfPartOfLevel(gentity_t* ent ) //originally from VV
- {
- int i, num;
- gentity_t *touch[MAX_GENTITIES], *hit;
- vec3_t mins, maxs;
- const vec3_t range = { 40, 40, 52 };
- if ( !ent->client ) {
- return qfalse;
- }
- VectorSubtract( ent->client->ps.origin, range, mins );
- VectorAdd( ent->client->ps.origin, range, maxs );
- num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
- // can't use ent->absmin, because that has a one unit pad
- VectorAdd( ent->client->ps.origin, ent->mins, mins );
- VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
- for ( i=0 ; i<num ; i++ ) {
- hit = touch[i];
- if ( (hit->e_TouchFunc == touchF_NULL) && (ent->e_TouchFunc == touchF_NULL) ) {
- continue;
- }
- if ( !( hit->contents & CONTENTS_TRIGGER ) ) {
- continue;
- }
- if ( !gi.EntityContact( mins, maxs, hit ) ) {
- continue;
- }
-
- if ( hit->e_TouchFunc != touchF_NULL ) {
- switch (hit->e_TouchFunc )
- {
- case touchF_Touch_Multi:
- if (G_IsTriggerUsable(hit, ent))
- {
- return qtrue;
- }
- continue;
- break;
- default:
- continue;
- }
- }
- }
- return qfalse;
- }
- #define USE_DISTANCE 64.0f
- extern qboolean eweb_can_be_used( gentity_t *self, gentity_t *other, gentity_t *activator );
- qboolean CanUseInfrontOf(gentity_t *ent)
- {
- gentity_t *target;
- trace_t trace;
- vec3_t src, dest, vf;
- if ( ent->s.number && ent->client->NPC_class == CLASS_ATST )
- {//a player trying to get out of his ATST
- // GEntity_UseFunc( ent->activator, ent, ent );
- return qfalse;
- }
- if (ent->client->ps.viewEntity != ent->s.number)
- {
- ent = &g_entities[ent->client->ps.viewEntity];
- if ( !Q_stricmp( "misc_camera", ent->classname ) )
- { // we are in a camera
- gentity_t *next = 0;
- if ( ent->target2 != NULL )
- {
- next = G_Find( NULL, FOFS(targetname), ent->target2 );
- }
- if ( next )
- {//found another one
- if ( !Q_stricmp( "misc_camera", next->classname ) )
- {//make sure it's another camera
- return qtrue;
- }
- }
- else //if ( ent->health > 0 )
- {//I was the last (only?) one, clear out the viewentity
- return qfalse;
- }
- }
- }
- if ( !ent->client ) {
- return qfalse;
- }
-
- //FIXME: this does not match where the new accurate crosshair aims...
- //cg.refdef.vieworg, basically
- VectorCopy( ent->client->renderInfo.eyePoint, src );
-
- AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );
- //extend to find end of use trace
- VectorMA( src, USE_DISTANCE, vf, dest );
- //Trace ahead to find a valid target
- gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE , G2_NOCOLLIDE, 10);
-
- if ( trace.fraction == 1.0f || trace.entityNum >= ENTITYNUM_WORLD )
- {
- return (CanUseInfrontOfPartOfLevel(ent));
- }
- target = &g_entities[trace.entityNum];
- if ( target && target->client && target->client->NPC_class == CLASS_VEHICLE )
- {
- // Attempt to board this vehicle.
- return qtrue;
- }
- //Check for a use command
- if (ValidUseTarget( target )) {
- if ( target->s.eType == ET_ITEM )
- {//item, see if we could actually pick it up
- if ( !BG_CanItemBeGrabbed( &target->s, &ent->client->ps ) )
- {//nope, so don't indicate that we can use it
- return qfalse;
- }
- }
- else if ( target->e_UseFunc == useF_misc_atst_use )
- {//drivable AT-ST from JK2
- if ( ent->client->ps.groundEntityNum != target->s.number )
- {//must be standing on it to use it
- return qfalse;
- }
- }
- else if ( target->NPC!=NULL && target->health<=0 )
- {
- return qfalse;
- }
- else if ( target->e_UseFunc == useF_eweb_use )
- {
- if ( !eweb_can_be_used( target, ent, ent ) )
- {
- return qfalse;
- }
- }
- return qtrue;
- }
- if ( target->client
- && target->client->ps.pm_type < PM_DEAD
- && target->NPC!=NULL
- && target->client->playerTeam
- && (target->client->playerTeam == ent->client->playerTeam || target->client->playerTeam == TEAM_NEUTRAL)
- && !(target->NPC->scriptFlags&SCF_NO_RESPONSE)
- && G_ValidActivateBehavior (target, BSET_USE))
- {
- return qtrue;
- }
- if (CanUseInfrontOfPartOfLevel(ent)) {
- return qtrue;
- }
- return qfalse;
- }
- /*
- ==============
- TryUse
- Try and use an entity in the world, directly ahead of us
- ==============
- */
- void TryUse( gentity_t *ent )
- {
- gentity_t *target;
- trace_t trace;
- vec3_t src, dest, vf;
- if (ent->s.number == 0 && g_npcdebug->integer == 1)
- {
- DebugTraceForNPC(ent);
- }
- if ( ent->s.number == 0 && ent->client->NPC_class == CLASS_ATST )
- {//a player trying to get out of his ATST
- GEntity_UseFunc( ent->activator, ent, ent );
- return;
- }
- // TODO: turo-boost.
- /* if ( ent->client->ps.vehicleIndex != VEHICLE_NONE )
- {//in a vehicle, use key makes you turbo-boost
- return;
- }*/
- //FIXME: this does not match where the new accurate crosshair aims...
- //cg.refdef.vieworg, basically
- VectorCopy( ent->client->renderInfo.eyePoint, src );
-
- AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically
- //extend to find end of use trace
- VectorMA( src, USE_DISTANCE, vf, dest );
- //Trace ahead to find a valid target
- gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE , G2_NOCOLLIDE, 10);
-
- if ( trace.fraction == 1.0f || trace.entityNum >= ENTITYNUM_WORLD )
- {
- //TODO: Play a failure sound
- /*
- if ( ent->s.number == 0 )
- {//if nothing else, try the force telepathy power
- ForceTelepathy( ent );
- }
- */
- return;
- }
- target = &g_entities[trace.entityNum];
- if ( target && target->client && target->client->NPC_class == CLASS_VEHICLE )
- {
- // Attempt to board this vehicle.
- target->m_pVehicle->m_pVehicleInfo->Board( target->m_pVehicle, ent );
- return;
- }
- //Check for a use command
- if ( ValidUseTarget( target ) )
- {
- NPC_SetAnim( ent, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- /*
- if ( !VectorLengthSquared( ent->client->ps.velocity ) && !PM_CrouchAnim( ent->client->ps.legsAnim ) )
- {
- NPC_SetAnim( ent, SETANIM_LEGS, BOTH_BUTTON_HOLD, SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD );
- }
- */
- //ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer;
- GEntity_UseFunc( target, ent, ent );
- return;
- }
- else if ( target->client
- && target->client->ps.pm_type < PM_DEAD
- && target->NPC!=NULL
- && target->client->playerTeam
- && (target->client->playerTeam == ent->client->playerTeam || target->client->playerTeam == TEAM_NEUTRAL)
- && !(target->NPC->scriptFlags&SCF_NO_RESPONSE) )
- {
- NPC_UseResponse ( target, ent, qfalse );
- return;
- }
- /*
- if ( ent->s.number == 0 )
- {//if nothing else, try the force telepathy power
- ForceTelepathy( ent );
- }
- */
- }
- extern int killPlayerTimer;
- void G_ChangeMap (const char *mapname, const char *spawntarget, qboolean hub)
- {
- // gi.Printf("Loading...");
- //ignore if player is dead
- if (g_entities[0].client->ps.pm_type == PM_DEAD)
- return;
- if ( killPlayerTimer )
- {//can't go to next map if your allies have turned on you
- return;
- }
- if (mapname[0] == '+') //fire up the menu instead
- {
- // Flush sound memory to make room for any UI loading that doesn't happen
- // until the end of the level:
- extern int SND_FreeOldestSound( void );
- SND_FreeOldestSound();
- gi.SendConsoleCommand( va("uimenu %s\n", mapname+1) );
- gi.cvar_set("skippingCinematic", "0");
- gi.cvar_set("timescale", "1");
- return;
- }
- if ( spawntarget == NULL ) {
- spawntarget = ""; //prevent it from becoming "(null)"
- }
- if ( hub == qtrue )
- {
- gi.SendConsoleCommand( va("loadtransition %s %s\n", mapname, spawntarget) );
- }
- else
- {
- gi.SendConsoleCommand( va("maptransition %s %s\n", mapname, spawntarget) );
- }
- }
- qboolean G_PointInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs )
- {
- for(int i = 0; i < 3; i++ )
- {
- if ( point[i] < mins[i] )
- {
- return qfalse;
- }
- if ( point[i] > maxs[i] )
- {
- return qfalse;
- }
- }
- return qtrue;
- }
- qboolean G_BoxInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs, const vec3_t boundsMins, const vec3_t boundsMaxs )
- {
- vec3_t boxMins;
- vec3_t boxMaxs;
- VectorAdd( point, mins, boxMins );
- VectorAdd( point, maxs, boxMaxs );
- if(boxMaxs[0]>boundsMaxs[0])
- return qfalse;
- if(boxMaxs[1]>boundsMaxs[1])
- return qfalse;
- if(boxMaxs[2]>boundsMaxs[2])
- return qfalse;
- if(boxMins[0]<boundsMins[0])
- return qfalse;
- if(boxMins[1]<boundsMins[1])
- return qfalse;
- if(boxMins[2]<boundsMins[2])
- return qfalse;
- //box is completely contained within bounds
- return qtrue;
- }
- void G_SetAngles( gentity_t *ent, const vec3_t angles )
- {
- VectorCopy( angles, ent->currentAngles );
- VectorCopy( angles, ent->s.angles );
- VectorCopy( angles, ent->s.apos.trBase );
- }
- qboolean G_ClearTrace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int ignore, int clipmask )
- {
- static trace_t tr;
- gi.trace( &tr, start, mins, maxs, end, ignore, clipmask );
- if ( tr.allsolid || tr.startsolid || tr.fraction < 1.0 )
- {
- return qfalse;
- }
- return qtrue;
- }
- extern void CG_TestLine( vec3_t start, vec3_t end, int time, unsigned int color, int radius);
- void G_DebugLine(vec3_t A, vec3_t B, int duration, int color, qboolean deleteornot)
- {
- /*
- gentity_t *tent = G_TempEntity( A, EV_DEBUG_LINE );
- VectorCopy(B, tent->s.origin2 );
- tent->s.time = duration; // Pause
- tent->s.time2 = color; // Color
- tent->s.weapon = 1; // Dimater
- tent->freeAfterEvent = deleteornot;
- */
- CG_TestLine( A, B, duration, color, 1 );
- }
- qboolean G_ExpandPointToBBox( vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask )
- {
- trace_t tr;
- vec3_t start, end;
- VectorCopy( point, start );
-
- for ( int i = 0; i < 3; i++ )
- {
- VectorCopy( start, end );
- end[i] += mins[i];
- gi.trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask );
- if ( tr.allsolid || tr.startsolid )
- {
- return qfalse;
- }
- if ( tr.fraction < 1.0 )
- {
- VectorCopy( start, end );
- end[i] += maxs[i]-(mins[i]*tr.fraction);
- gi.trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask );
- if ( tr.allsolid || tr.startsolid )
- {
- return qfalse;
- }
- if ( tr.fraction < 1.0 )
- {
- return qfalse;
- }
- VectorCopy( end, start );
- }
- }
- //expanded it, now see if it's all clear
- gi.trace( &tr, start, mins, maxs, start, ignore, clipmask );
- if ( tr.allsolid || tr.startsolid )
- {
- return qfalse;
- }
- VectorCopy( start, point );
- return qtrue;
- }
- /*
- Ghoul2 Insert Start
- */
- void removeBoltSurface( gentity_t *ent)
- {
- gentity_t *hitEnt = &g_entities[ent->cantHitEnemyCounter];
- // check first to be sure the bolt is still there on the model
- if ((hitEnt->ghoul2.size() > ent->damage) &&
- (hitEnt->ghoul2[ent->damage].mModelindex != -1) &&
- (hitEnt->ghoul2[ent->damage].mSlist.size() > ent->aimDebounceTime) &&
- (hitEnt->ghoul2[ent->damage].mSlist[ent->aimDebounceTime].surface != -1) &&
- (hitEnt->ghoul2[ent->damage].mSlist[ent->aimDebounceTime].offFlags == G2SURFACEFLAG_GENERATED))
- {
- // remove the bolt
- gi.G2API_RemoveBolt(&hitEnt->ghoul2[ent->damage], ent->attackDebounceTime);
- // now remove a surface if there is one
- if (ent->aimDebounceTime != -1)
- {
- gi.G2API_RemoveSurface(&hitEnt->ghoul2[ent->damage], ent->aimDebounceTime);
- }
- }
- // we are done with this entity.
- G_FreeEntity(ent);
- }
- void G_SetBoltSurfaceRemoval( const int entNum, const int modelIndex, const int boltIndex, const int surfaceIndex , float duration ) {
- gentity_t *e;
- vec3_t snapped = {0,0,0};
- e = G_Spawn();
- e->classname = "BoltRemoval";
- e->cantHitEnemyCounter = entNum;
- e->damage = modelIndex;
- e->attackDebounceTime = boltIndex;
- e->aimDebounceTime = surfaceIndex;
- G_SetOrigin( e, snapped );
- // find cluster for PVS
- gi.linkentity( e );
- e->nextthink = level.time + duration;
- e->e_ThinkFunc = thinkF_removeBoltSurface;
- }
- /*
- Ghoul2 Insert End
- */
|