123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301 |
- // leave this line at the top for all g_xxxx.cpp files...
- #include "g_headers.h"
- #include <xtl.h>
- #include "g_local.h"
- #include "g_functions.h"
- #include "Q3_Interface.h"
- #include "g_nav.h"
- #include "g_roff.h"
- #include "g_navigator.h"
- #include "b_local.h"
- #include "anims.h"
- #include "objectives.h"
- #include "../cgame/cg_local.h" // yeah I know this is naughty, but we're shipping soon...
- //rww - RAGDOLL_BEGIN
- #include "../ghoul2/ghoul2_gore.h"
- //rww - RAGDOLL_END
- extern void WP_SaberLoadParms( void );
- extern qboolean G_PlayerSpawned( void );
- extern void Rail_Initialize( void );
- extern void Rail_Update( void );
- extern void Rail_Reset(void);
- extern void Troop_Initialize( void );
- extern void Troop_Update( void );
- extern void Troop_Reset(void);
- extern void Pilot_Reset(void);
- extern void Pilot_Update(void);
- extern void G_ASPreCacheFree(void);
- static int navCalcPathTime = 0;
- int eventClearTime = 0;
- extern qboolean g_bCollidableRoffs;
- #define STEPSIZE 18
- level_locals_t level;
- game_import_t gi;
- game_export_t globals;
- //gentity_t g_entities[MAX_GENTITIES];
- gentity_t *g_entities = NULL;
- unsigned int g_entityInUseBits[MAX_GENTITIES/32];
- static void ClearAllInUse(void)
- {
- memset(g_entityInUseBits,0,sizeof(g_entityInUseBits));
- }
- void SetInUse(gentity_t *ent)
- {
- assert(((unsigned int)ent)>=(unsigned int)g_entities);
- assert(((unsigned int)ent)<=(unsigned int)(g_entities+MAX_GENTITIES-1));
- unsigned int entNum=ent-g_entities;
- g_entityInUseBits[entNum/32]|=((unsigned int)1)<<(entNum&0x1f);
- }
- void ClearInUse(gentity_t *ent)
- {
- assert(((unsigned int)ent)>=(unsigned int)g_entities);
- assert(((unsigned int)ent)<=(unsigned int)(g_entities+MAX_GENTITIES-1));
- unsigned int entNum=ent-g_entities;
- g_entityInUseBits[entNum/32]&=~(((unsigned int)1)<<(entNum&0x1f));
- }
- qboolean PInUse(unsigned int entNum)
- {
- assert(entNum>=0);
- assert(entNum<MAX_GENTITIES);
- return((g_entityInUseBits[entNum/32]&(((unsigned int)1)<<(entNum&0x1f)))!=0);
- }
- /*qboolean PInUse2(gentity_t *ent)
- {
- assert(((unsigned int)ent)>=(unsigned int)g_entities);
- assert(((unsigned int)ent)<=(unsigned int)(g_entities+MAX_GENTITIES-1));
- unsigned int entNum=ent-g_entities;
- return((g_entityInUseBits[entNum/32]&(((unsigned int)1)<<(entNum&0x1f)))!=0);
- }
- */
- void WriteInUseBits(void)
- {
- gi.AppendToSaveGame('INUS', &g_entityInUseBits, sizeof(g_entityInUseBits) );
- }
- void ReadInUseBits(void)
- {
- gi.ReadFromSaveGame('INUS', &g_entityInUseBits, sizeof(g_entityInUseBits));
- // This is only temporary. Once I have converted all the ent->inuse refs,
- // it won;t be needed -MW.
- for(int i=0;i<MAX_GENTITIES;i++)
- {
- g_entities[i].inuse=PInUse(i);
- }
- }
- #ifdef _DEBUG
- static void ValidateInUseBits(void)
- {
- for(int i=0;i<MAX_GENTITIES;i++)
- {
- assert(g_entities[i].inuse==PInUse(i));
- }
- }
- #endif
- gentity_t *player;
- cvar_t *g_speed;
- cvar_t *g_gravity;
- cvar_t *g_stepSlideFix;
- cvar_t *g_sex;
- cvar_t *g_spskill;
- cvar_t *g_cheats;
- cvar_t *g_developer;
- cvar_t *g_timescale;
- cvar_t *g_knockback;
- cvar_t *g_dismemberment;
- cvar_t *g_dismemberProbabilities;
- cvar_t *g_corpseRemovalTime;
- cvar_t *g_synchSplitAnims;
- #ifndef FINAL_BUILD
- cvar_t *g_AnimWarning;
- #endif
- cvar_t *g_noFootSlide;
- cvar_t *g_noFootSlideRunScale;
- cvar_t *g_noFootSlideWalkScale;
- cvar_t *g_nav1;
- cvar_t *g_nav2;
- cvar_t *g_bobaDebug;
- cvar_t *g_delayedShutdown;
- cvar_t *g_inactivity;
- cvar_t *g_debugMove;
- cvar_t *g_debugDamage;
- cvar_t *g_weaponRespawn;
- cvar_t *g_subtitles;
- cvar_t *g_ICARUSDebug;
- #ifdef _XBOX
- extern cvar_t *com_buildScript;
- #else
- cvar_t *com_buildScript;
- #endif
- cvar_t *g_skippingcin;
- cvar_t *g_AIsurrender;
- cvar_t *g_numEntities;
- //cvar_t *g_iscensored;
- cvar_t *g_saberAutoBlocking;
- cvar_t *g_saberRealisticCombat;
- cvar_t *g_saberDamageCapping;
- cvar_t *g_saberMoveSpeed;
- cvar_t *g_saberAnimSpeed;
- cvar_t *g_saberAutoAim;
- cvar_t *g_saberNewControlScheme;
- cvar_t *g_debugSaberLock;
- cvar_t *g_saberLockRandomNess;
- cvar_t *g_debugMelee;
- cvar_t *g_saberRestrictForce;
- cvar_t *g_speederControlScheme;
- cvar_t *g_char_model;
- cvar_t *g_char_skin_head;
- cvar_t *g_char_skin_torso;
- cvar_t *g_char_skin_legs;
- cvar_t *g_char_color_red;
- cvar_t *g_char_color_green;
- cvar_t *g_char_color_blue;
- cvar_t *g_saber;
- cvar_t *g_saber2;
- cvar_t *g_saber_color;
- cvar_t *g_saber2_color;
- cvar_t *g_saberDarkSideSaberColor;
- // kef -- used with DebugTraceForNPC
- cvar_t *g_npcdebug;
- // mcg -- testing: make NPCs obey do not enter brushes better?
- cvar_t *g_navSafetyChecks;
- cvar_t *g_broadsword;
- qboolean stop_icarus = qfalse;
- extern char *G_GetLocationForEnt( gentity_t *ent );
- extern void CP_FindCombatPointWaypoints( void );
- extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f );
- void G_RunFrame (int levelTime);
- void PrintEntClassname( int gentNum );
- void ClearNPCGlobals( void );
- extern void AI_UpdateGroups( void );
- void ClearPlayerAlertEvents( void );
- extern void NPC_ShowDebugInfo (void);
- extern int killPlayerTimer;
- /*
- static void G_DynamicMusicUpdate( usercmd_t *ucmd )
- FIXME: can we merge any of this with the G_ChooseLookEnemy stuff?
- */
- static void G_DynamicMusicUpdate( void )
- {
- gentity_t *ent;
- gentity_t *entityList[MAX_GENTITIES];
- int numListedEntities;
- vec3_t mins, maxs;
- int i, e;
- int distSq, radius = 2048;
- vec3_t center;
- int danger = 0;
- int battle = 0;
- int entTeam;
- qboolean dangerNear = qfalse;
- qboolean suspicious = qfalse;
- qboolean LOScalced = qfalse, clearLOS = qfalse;
- //FIXME: intro and/or other cues? (one-shot music sounds)
- //loops
- //player-based
- if ( !player )
- {//WTF?
- player = &g_entities[0];
- return;
- }
- if ( !G_PlayerSpawned() )
- {//player hasn't spawned yet!
- return;
- }
- if ( player->health <= 0 && player->max_health > 0 )
- {//defeat music
- if ( level.dmState != DM_DEATH )
- {
- level.dmState = DM_DEATH;
- }
- }
- if ( level.dmState == DM_DEATH )
- {
- gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "death" );
- return;
- }
- if ( level.dmState == DM_BOSS )
- {
- gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "boss" );
- return;
- }
- if ( level.dmState == DM_SILENCE )
- {
- gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "silence" );
- return;
- }
- if ( level.dmBeatTime > level.time )
- {//not on a beat
- return;
- }
- level.dmBeatTime = level.time + 1000;//1 second beats
- if ( player->health <= 20 )
- {
- danger = 1;
- }
- //enemy-based
- VectorCopy( player->currentOrigin, center );
- for ( i = 0 ; i < 3 ; i++ )
- {
- mins[i] = center[i] - radius;
- maxs[i] = center[i] + radius;
- }
-
- numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
- for ( e = 0 ; e < numListedEntities ; e++ )
- {
- ent = entityList[ e ];
- if ( !ent || !ent->inuse )
- {
- continue;
- }
- if ( !ent->client || !ent->NPC )
- {
- if ( ent->classname && (!Q_stricmp( "PAS", ent->classname )||!Q_stricmp( "misc_turret", ent->classname )) )
- {//a turret
- entTeam = ent->noDamageTeam;
- }
- else
- {
- continue;
- }
- }
- else
- {//an NPC
- entTeam = ent->client->playerTeam;
- }
- if ( entTeam == player->client->playerTeam )
- {//ally
- continue;
- }
- if ( entTeam == TEAM_NEUTRAL && (!ent->enemy || !ent->enemy->client || ent->enemy->client->playerTeam != player->client->playerTeam) )
- {//a droid that is not mad at me or my allies
- continue;
- }
- if ( !gi.inPVS( player->currentOrigin, ent->currentOrigin ) )
- {//not potentially visible
- continue;
- }
- if ( ent->client && ent->s.weapon == WP_NONE )
- {//they don't have a weapon... FIXME: only do this for droids?
- continue;
- }
- LOScalced = clearLOS = qfalse;
- if ( (ent->enemy==player&&(!ent->NPC||ent->NPC->confusionTime<level.time)) || (ent->client&&ent->client->ps.weaponTime) || (!ent->client&&ent->attackDebounceTime>level.time))
- {//mad
- if ( ent->health > 0 )
- {//alive
- //FIXME: do I really need this check?
- if ( ent->s.weapon == WP_SABER && ent->client && !ent->client->ps.SaberActive() && ent->enemy != player )
- {//a Jedi who has not yet gotten made at me
- continue;
- }
- if ( ent->NPC && ent->NPC->behaviorState == BS_CINEMATIC )
- {//they're not actually going to do anything about being mad at me...
- continue;
- }
- //okay, they're in my PVS, but how close are they? Are they actively attacking me?
- if ( !ent->client && ent->s.weapon == WP_TURRET && ent->fly_sound_debounce_time && ent->fly_sound_debounce_time - level.time < 10000 )
- {//a turret that shot at me less than ten seconds ago
- }
- else if ( ent->client && ent->client->ps.lastShotTime && ent->client->ps.lastShotTime - level.time < 10000 )
- {//an NPC that shot at me less than ten seconds ago
- }
- else
- {//not actively attacking me lately, see how far away they are
- distSq = DistanceSquared( ent->currentOrigin, player->currentOrigin );
- if ( distSq > 4194304/*2048*2048*/ )
- {//> 2048 away
- continue;
- }
- else if ( distSq > 1048576/*1024*1024*/ )
- {//> 1024 away
- clearLOS = G_ClearLOS( player, player->client->renderInfo.eyePoint, ent );
- LOScalced = qtrue;
- if ( clearLOS == qfalse )
- {//No LOS
- continue;
- }
- }
- }
- battle++;
- }
- }
- if ( level.dmState == DM_EXPLORE )
- {//only do these visibility checks if you're still in exploration mode
- if ( !InFront( ent->currentOrigin, player->currentOrigin, player->client->ps.viewangles, 0.0f) )
- {//not in front
- continue;
- }
- if ( !LOScalced )
- {
- clearLOS = G_ClearLOS( player, player->client->renderInfo.eyePoint, ent );
- }
- if ( !clearLOS )
- {//can't see them directly
- continue;
- }
- }
- if ( ent->health <= 0 )
- {//dead
- if ( !ent->client || level.time - ent->s.time > 10000 )
- {//corpse has been dead for more than 10 seconds
- //FIXME: coming across corpses should cause danger sounds too?
- continue;
- }
- }
- //we see enemies and/or corpses
- danger++;
- }
- if ( !battle )
- {//no active enemies, but look for missiles, shot impacts, etc...
- int alert = G_CheckAlertEvents( player, qtrue, qtrue, 1024, 1024, -1, qfalse, AEL_SUSPICIOUS );
- if ( alert != -1 )
- {//FIXME: maybe tripwires and other FIXED things need their own sound, some kind of danger/caution theme
- if ( G_CheckForDanger( player, alert ) )
- {//found danger near by
- danger++;
- battle = 1;
- }
- else if ( level.alertEvents[alert].owner && (level.alertEvents[alert].owner == player->enemy || (level.alertEvents[alert].owner->client && level.alertEvents[alert].owner->client->playerTeam == player->client->enemyTeam) ) )
- {//NPC on enemy team of player made some noise
- switch ( level.alertEvents[alert].level )
- {
- case AEL_DISCOVERED:
- dangerNear = qtrue;
- break;
- case AEL_SUSPICIOUS:
- suspicious = qtrue;
- break;
- case AEL_MINOR:
- //distraction = qtrue;
- break;
- }
- }
- }
- }
- if ( battle )
- {//battle - this can interrupt level.dmDebounceTime of lower intensity levels
- //play battle
- if ( level.dmState != DM_ACTION )
- {
- gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "action" );
- }
- level.dmState = DM_ACTION;
- if ( battle > 5 )
- {
- //level.dmDebounceTime = level.time + 8000;//don't change again for 5 seconds
- }
- else
- {
- //level.dmDebounceTime = level.time + 3000 + 1000*battle;
- }
- }
- else
- {
- if ( level.dmDebounceTime > level.time )
- {//not ready to switch yet
- return;
- }
- else
- {//at least 1 second (for beats)
- //level.dmDebounceTime = level.time + 1000;//FIXME: define beat time?
- }
- /*
- if ( danger || dangerNear )
- {//danger
- //stay on whatever we were on, action or exploration
- if ( !danger )
- {//minimum
- danger = 1;
- }
- if ( danger > 3 )
- {
- level.dmDebounceTime = level.time + 5000;
- }
- else
- {
- level.dmDebounceTime = level.time + 2000 + 1000*danger;
- }
- }
- else
- */
- {//still nothing dangerous going on
- if ( level.dmState != DM_EXPLORE )
- {//just went to explore, hold it for a couple seconds at least
- //level.dmDebounceTime = level.time + 2000;
- gi.SetConfigstring( CS_DYNAMIC_MUSIC_STATE, "explore" );
- }
- level.dmState = DM_EXPLORE;
- //FIXME: look for interest points and play "mysterious" music instead of exploration?
- //FIXME: suspicious and distraction sounds should play some cue or change music in a subtle way?
- //play exploration
- }
- //FIXME: when do we go to silence?
- }
- }
- void G_ConnectNavs( const char *mapname, int checkSum )
- {
- NAV::LoadFromEntitiesAndSaveToFile(mapname, checkSum);
- CP_FindCombatPointWaypoints();
- }
- /*
- ================
- G_FindTeams
- Chain together all entities with a matching team field.
- Entity teams are used for item groups and multi-entity mover groups.
- All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
- All but the last will have the teamchain field set to the next one
- ================
- */
- void G_FindTeams( void ) {
- gentity_t *e, *e2;
- int i, j;
- int c, c2;
- c = 0;
- c2 = 0;
- // for ( i=1, e=g_entities,i ; i < globals.num_entities ; i++,e++ )
- for ( i=1 ; i < globals.num_entities ; i++ )
- {
- // if (!e->inuse)
- // continue;
- if(!PInUse(i))
- continue;
- e=&g_entities[i];
- if (!e->team)
- continue;
- if (e->flags & FL_TEAMSLAVE)
- continue;
- e->teammaster = e;
- c++;
- c2++;
- // for (j=i+1, e2=e+1 ; j < globals.num_entities ; j++,e2++)
- for (j=i+1; j < globals.num_entities ; j++)
- {
- // if (!e2->inuse)
- // continue;
- if(!PInUse(j))
- continue;
-
- e2=&g_entities[j];
- if (!e2->team)
- continue;
- if (e2->flags & FL_TEAMSLAVE)
- continue;
- if (!strcmp(e->team, e2->team))
- {
- c2++;
- e2->teamchain = e->teamchain;
- e->teamchain = e2;
- e2->teammaster = e;
- e2->flags |= FL_TEAMSLAVE;
- // make sure that targets only point at the master
- if ( e2->targetname ) {
- e->targetname = G_NewString(e2->targetname);
- e2->targetname = NULL;
- }
- }
- }
- }
- //gi.Printf ("%i teams with %i entities\n", c, c2);
- }
- /*
- ============
- G_InitCvars
- ============
- */
- void G_InitCvars( void ) {
- // don't override the cheat state set by the system
- g_cheats = gi.cvar ("helpUsObi", "", 0);
- g_developer = gi.cvar ("developer", "", 0);
- // noset vars
- gi.cvar( "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM );
- gi.cvar( "gamedate", __DATE__ , CVAR_ROM );
- g_skippingcin = gi.cvar ("skippingCinematic", "0", CVAR_ROM);
- // latched vars
- // change anytime vars
- g_speed = gi.cvar( "g_speed", "250", CVAR_CHEAT );
- g_gravity = gi.cvar( "g_gravity", "800", CVAR_SAVEGAME|CVAR_ROM );
- g_stepSlideFix = gi.cvar( "g_stepSlideFix", "1", CVAR_ARCHIVE );
- g_sex = gi.cvar ("sex", "f", CVAR_USERINFO | CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_spskill = gi.cvar ("g_spskill", "0", CVAR_ARCHIVE | CVAR_SAVEGAME|CVAR_NORESTART);
- g_knockback = gi.cvar( "g_knockback", "1000", CVAR_CHEAT );
- g_dismemberment = gi.cvar ( "g_dismemberment", "3", CVAR_ARCHIVE );//0 = none, 1 = arms and hands, 2 = legs, 3 = waist and head, 4 = mega dismemberment
- g_dismemberProbabilities = gi.cvar ( "g_dismemberProbabilities", "1", CVAR_ARCHIVE );//0 = ignore probabilities, 1 = use probabilities
- // for now I'm making default 10 seconds
- g_corpseRemovalTime = gi.cvar ( "g_corpseRemovalTime", "10", CVAR_ARCHIVE );//number of seconds bodies stick around for, at least... 0 = never go away
- g_synchSplitAnims = gi.cvar ( "g_synchSplitAnims", "1", 0 );
- #ifndef FINAL_BUILD
- g_AnimWarning = gi.cvar ( "g_AnimWarning", "1", 0 );
- #endif
- g_noFootSlide = gi.cvar ( "g_noFootSlide", "1", 0 );
- g_noFootSlideRunScale = gi.cvar ( "g_noFootSlideRunScale", "150.0", 0 );
- g_noFootSlideWalkScale = gi.cvar ( "g_noFootSlideWalkScale", "50.0", 0 );
- g_nav1 = gi.cvar ( "g_nav1", "", 0 );
- g_nav2 = gi.cvar ( "g_nav2", "", 0 );
- g_bobaDebug = gi.cvar ( "g_bobaDebug", "", 0 );
- #if defined(FINAL_BUILD) || defined(_XBOX)
- g_delayedShutdown = gi.cvar ( "g_delayedShutdown", "0", 0 );
- #else
- g_delayedShutdown = gi.cvar ( "g_delayedShutdown", "1", 0 );
- #endif
- g_inactivity = gi.cvar ("g_inactivity", "0", 0);
- g_debugMove = gi.cvar ("g_debugMove", "0", CVAR_CHEAT );
- g_debugDamage = gi.cvar ("g_debugDamage", "0", CVAR_CHEAT );
- g_ICARUSDebug = gi.cvar( "g_ICARUSDebug", "0", CVAR_CHEAT );
- g_timescale = gi.cvar( "timescale", "1", 0 );
- g_npcdebug = gi.cvar( "g_npcdebug", "0", 0 );
- g_navSafetyChecks = gi.cvar( "g_navSafetyChecks", "0", 0 );
- // NOTE : I also create this is UI_Init()
- g_subtitles = gi.cvar( "g_subtitles", "0", CVAR_ARCHIVE );
- com_buildScript = gi.cvar ("com_buildscript", "0", 0);
- g_saberAutoBlocking = gi.cvar( "g_saberAutoBlocking", "1", CVAR_CHEAT );//must press +block button to do any blocking
- g_saberRealisticCombat = gi.cvar( "g_saberRealisticCombat", "0", CVAR_CHEAT );//makes collision more precise, increases damage
- g_saberDamageCapping = gi.cvar( "g_saberDamageCapping", "1", CVAR_CHEAT );//caps damage of sabers vs players and NPC who use sabers
- g_saberMoveSpeed = gi.cvar( "g_saberMoveSpeed", "1", CVAR_CHEAT );//how fast you run while attacking with a saber
- g_saberAnimSpeed = gi.cvar( "g_saberAnimSpeed", "1", CVAR_CHEAT );//how fast saber animations run
- g_saberAutoAim = gi.cvar( "g_saberAutoAim", "1", CVAR_CHEAT );//auto-aims at enemies when not moving or when just running forward
- g_saberNewControlScheme = gi.cvar( "g_saberNewControlScheme", "0", CVAR_ARCHIVE );//use +forcefocus to pull off all the special moves
- g_debugSaberLock = gi.cvar( "g_debugSaberLock", "0", CVAR_CHEAT );//just for debugging/development, makes saberlocks happen all the time
- g_saberLockRandomNess = gi.cvar( "g_saberLockRandomNess", "2", CVAR_ARCHIVE );//just for debugging/development, controls frequency of saberlocks
- g_debugMelee = gi.cvar( "g_debugMelee", "0", CVAR_CHEAT );//just for debugging/development, test kicks and grabs
- g_saberRestrictForce = gi.cvar( "g_saberRestrictForce", "0", CVAR_ARCHIVE );//restricts certain force powers when using a 2-handed saber or 2 sabers
- g_AIsurrender = gi.cvar( "g_AIsurrender", "0", CVAR_CHEAT );
- g_numEntities = gi.cvar( "g_numEntities", "0", 0 );
-
- gi.cvar( "newTotalSecrets", "0", CVAR_ROM );
- gi.cvar_set("newTotalSecrets", "0");//used to carry over the count from SP_target_secret to ClientBegin
- //g_iscensored = gi.cvar( "ui_iscensored", "0", CVAR_ARCHIVE|CVAR_ROM|CVAR_INIT|CVAR_CHEAT|CVAR_NORESTART );
- g_speederControlScheme = gi.cvar( "g_speederControlScheme", "2", CVAR_ARCHIVE|CVAR_ROM );//2 is default, 1 is alternate
- g_char_model = gi.cvar( "g_char_model", "jedi_tf", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_char_skin_head = gi.cvar( "g_char_skin_head", "head_a1", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_char_skin_torso = gi.cvar( "g_char_skin_torso", "torso_a1", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_char_skin_legs = gi.cvar( "g_char_skin_legs", "lower_a1", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_char_color_red = gi.cvar( "g_char_color_red", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_char_color_green = gi.cvar( "g_char_color_green", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_char_color_blue = gi.cvar( "g_char_color_blue", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_saber = gi.cvar( "g_saber", "single_1", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_saber2 = gi.cvar( "g_saber2", "", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_saber_color = gi.cvar( "g_saber_color", "yellow", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_saber2_color = gi.cvar( "g_saber2_color", "yellow", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART );
- g_saberDarkSideSaberColor = gi.cvar( "g_saberDarkSideSaberColor", "0", CVAR_ARCHIVE ); //when you turn evil, it turns your saber red!
- g_broadsword = gi.cvar( "broadsword", "1", 0);
- gi.cvar( "tier_storyinfo", "0", CVAR_ROM|CVAR_SAVEGAME|CVAR_NORESTART);
- gi.cvar( "tiers_complete", "", CVAR_ROM|CVAR_SAVEGAME|CVAR_NORESTART);
- gi.cvar( "ui_prisonerobj_currtotal", "0", CVAR_ROM|CVAR_SAVEGAME|CVAR_NORESTART);
- gi.cvar( "ui_prisonerobj_maxtotal", "0", CVAR_ROM|CVAR_SAVEGAME|CVAR_NORESTART);
- gi.cvar( "g_clearstats", "1", CVAR_ROM|CVAR_NORESTART);
- }
- /*
- ============
- InitGame
- ============
- */
- // I'm just declaring a global here which I need to get at in NAV_GenerateSquadPaths for deciding if pre-calc'd
- // data is valid, and this saves changing the proto of G_SpawnEntitiesFromString() to include a checksum param which
- // may get changed anyway if a new nav system is ever used. This way saves messing with g_local.h each time -slc
- int giMapChecksum;
- SavedGameJustLoaded_e g_eSavedGameJustLoaded;
- qboolean g_qbLoadTransition = qfalse;
- void InitGame( const char *mapname, const char *spawntarget, int checkSum, const char *entities, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition )
- {
- //rww - default this to 0, we will auto-set it to 1 if we run into a terrain ent
- gi.cvar_set("RMG", "0");
- g_bCollidableRoffs = false;
- giMapChecksum = checkSum;
- g_eSavedGameJustLoaded = eSavedGameJustLoaded;
- g_qbLoadTransition = qbLoadTransition;
- gi.Printf ("------- Game Initialization -------\n");
- gi.Printf ("gamename: %s\n", GAMEVERSION);
- gi.Printf ("gamedate: %s\n", __DATE__);
- srand( randomSeed );
- G_InitCvars();
- G_InitMemory();
- // set some level globals
- memset( &level, 0, sizeof( level ) );
- level.time = levelTime;
- level.globalTime = globalTime;
- Q_strncpyz( level.mapname, mapname, sizeof(level.mapname) );
- if ( spawntarget != NULL && spawntarget[0] )
- {
- Q_strncpyz( level.spawntarget, spawntarget, sizeof(level.spawntarget) );
- }
- else
- {
- level.spawntarget[0] = 0;
- }
- G_InitWorldSession();
- // initialize all entities for this game
- memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) );
- globals.gentities = g_entities;
- ClearAllInUse();
- // initialize all clients for this game
- level.maxclients = 1;
- level.clients = (struct gclient_s *) G_Alloc( level.maxclients * sizeof(level.clients[0]) );
- memset(level.clients, 0, level.maxclients * sizeof(level.clients[0]));
- // set client fields on player
- g_entities[0].client = level.clients;
- // always leave room for the max number of clients,
- // even if they aren't all used, so numbers inside that
- // range are NEVER anything but clients
- globals.num_entities = MAX_CLIENTS;
- //Load sabers.cfg data
- WP_SaberLoadParms();
- //Set up NPC init data
- NPC_InitGame();
-
- TIMER_Clear();
- Rail_Reset();
- Troop_Reset();
- Pilot_Reset();
- IT_LoadItemParms ();
- ClearRegisteredItems();
- // clear out old nav info, attempt to load from file
- NAV::LoadFromFile(level.mapname, giMapChecksum);
- // parse the key/value pairs and spawn gentities
- G_SpawnEntitiesFromString( entities );
- // general initialization
- G_FindTeams();
- // SaveRegisteredItems();
- gi.Printf ("-----------------------------------\n");
- Rail_Initialize();
- Troop_Initialize();
- player = &g_entities[0];
- //Init dynamic music
- level.dmState = DM_EXPLORE;
- level.dmDebounceTime = 0;
- level.dmBeatTime = 0;
- level.curAlertID = 1;//0 is default for lastAlertEvent, so...
- eventClearTime = 0;
- #ifdef _XBOX
- // clear out NPC water detection data
- npcsToUpdateTop = 0;
- npcsToUpdateCount = 0;
- memset(npcsToUpdate, -1, 2 * MAX_NPC_WATER_UPDATE);
- #endif // _XBOX
- }
- /*
- =================
- ShutdownGame
- =================
- */
- void ShutdownGame( void )
- {
- // write all the client session data so we can get it back
- G_WriteSessionData();
- #ifdef _XBOX
- // The following functions, cleverly disguised as memory freeing and dealloction,
- // actually allocate small blocks. Fooled you!
- extern void Z_SetNewDeleteTemporary(bool bTemp);
- Z_SetNewDeleteTemporary( true );
- #endif
- // Destroy the Game Interface.
- IGameInterface::Destroy();
- // Shut ICARUS down.
- IIcarusInterface::DestroyIcarus();
- // Destroy the Game Interface again. Only way to really free everything.
- IGameInterface::Destroy();
- #ifdef _XBOX
- Z_SetNewDeleteTemporary( false );
- #endif
- TAG_Init(); //Clear the reference tags
- /*
- Ghoul2 Insert Start
- */
- for (int i=0; i<MAX_GENTITIES; i++)
- {
- gi.G2API_CleanGhoul2Models(g_entities[i].ghoul2);
- }
- /*
- Ghoul2 Insert End
- */
- G_ASPreCacheFree();
- }
- //===================================================================
- static void G_Cvar_Create( const char *var_name, const char *var_value, int flags ) {
- gi.cvar( var_name, var_value, flags );
- }
- //BEGIN GAMESIDE RMG
- qboolean G_ParseSpawnVars( const char **data );
- void G_SpawnGEntityFromSpawnVars( void );
- void G_GameSpawnRMGEntity(char *s)
- {
- if (G_ParseSpawnVars((const char **)&s))
- {
- G_SpawnGEntityFromSpawnVars();
- }
- }
- //END GAMESIDE RMG
- /*
- =================
- GetGameAPI
- Returns a pointer to the structure with all entry points
- and global variables
- =================
- */
- extern int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float animSpeed );
- game_export_t *GetGameAPI( game_import_t *import ) {
- gameinfo_import_t gameinfo_import;
- gi = *import;
- globals.apiversion = GAME_API_VERSION;
- globals.Init = InitGame;
- globals.Shutdown = ShutdownGame;
- globals.WriteLevel = WriteLevel;
- globals.ReadLevel = ReadLevel;
- globals.GameAllowedToSaveHere = GameAllowedToSaveHere;
- globals.ClientThink = ClientThink;
- globals.ClientConnect = ClientConnect;
- globals.ClientUserinfoChanged = ClientUserinfoChanged;
- globals.ClientDisconnect = ClientDisconnect;
- globals.ClientBegin = ClientBegin;
- globals.ClientCommand = ClientCommand;
- globals.RunFrame = G_RunFrame;
- globals.ConnectNavs = G_ConnectNavs;
- globals.ConsoleCommand = ConsoleCommand;
- //globals.PrintEntClassname = PrintEntClassname;
- //globals.ValidateAnimRange = PM_ValidateAnimRange;
- globals.GameSpawnRMGEntity = G_GameSpawnRMGEntity;
- globals.gentitySize = sizeof(gentity_t);
- gameinfo_import.FS_FOpenFile = gi.FS_FOpenFile;
- gameinfo_import.FS_Read = gi.FS_Read;
- gameinfo_import.FS_FCloseFile = gi.FS_FCloseFile;
- gameinfo_import.Cvar_Set = gi.cvar_set;
- gameinfo_import.Cvar_VariableStringBuffer = gi.Cvar_VariableStringBuffer;
- gameinfo_import.Cvar_Create = G_Cvar_Create;
- GI_Init( &gameinfo_import );
- return &globals;
- }
- void QDECL G_Error( const char *fmt, ... ) {
- va_list argptr;
- char text[1024];
- va_start (argptr, fmt);
- vsprintf (text, fmt, argptr);
- va_end (argptr);
- gi.Error( ERR_DROP, "%s", text);
- }
- #ifndef GAME_HARD_LINKED
- // this is only here so the functions in q_shared.c and bg_*.c can link
- /*
- -------------------------
- Com_Error
- -------------------------
- */
- void Com_Error ( int level, const char *error, ... ) {
- va_list argptr;
- char text[1024];
- va_start (argptr, error);
- vsprintf (text, error, argptr);
- va_end (argptr);
- gi.Error( level, "%s", text);
- }
- /*
- -------------------------
- Com_Printf
- -------------------------
- */
- void Com_Printf( const char *msg, ... ) {
- va_list argptr;
- char text[1024];
- va_start (argptr, msg);
- vsprintf (text, msg, argptr);
- va_end (argptr);
- gi.Printf ("%s", text);
- }
- #endif
- /*
- ========================================================================
- MAP CHANGING
- ========================================================================
- */
- /*
- ========================================================================
- FUNCTIONS CALLED EVERY FRAME
- ========================================================================
- */
- static void G_CheckTasksCompleted (gentity_t *ent)
- {
- if ( Q3_TaskIDPending( ent, TID_CHAN_VOICE ) )
- {
- if ( !gi.VoiceVolume[ent->s.number] )
- {//not playing a voice sound
- //return task_complete
- Q3_TaskIDComplete( ent, TID_CHAN_VOICE );
- }
- }
- if ( Q3_TaskIDPending( ent, TID_LOCATION ) )
- {
- char *currentLoc = G_GetLocationForEnt( ent );
- if ( currentLoc && currentLoc[0] && Q_stricmp( ent->message, currentLoc ) == 0 )
- {//we're in the desired location
- Q3_TaskIDComplete( ent, TID_LOCATION );
- }
- //FIXME: else see if were in other trigger_locations?
- }
- }
- static void G_CheckSpecialPersistentEvents( gentity_t *ent )
- {//special-case alerts that would be a pain in the ass to have the ent's think funcs generate
- if ( ent == NULL )
- {
- return;
- }
- if ( ent->s.eType == ET_MISSILE && ent->s.weapon == WP_THERMAL && ent->s.pos.trType == TR_STATIONARY )
- {
- if ( eventClearTime == level.time + ALERT_CLEAR_TIME )
- {//events were just cleared out so add me again
- AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, qfalse, qtrue );
- AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER );
- }
- }
- if ( ent->forcePushTime >= level.time )
- {//being pushed
- if ( eventClearTime == level.time + ALERT_CLEAR_TIME )
- {//events were just cleared out so add me again
- //NOTE: presumes the player did the pushing, this is not always true, but shouldn't really matter?
- if ( ent->item && ent->item->giTag == INV_SECURITY_KEY )
- {
- AddSightEvent( player, ent->currentOrigin, 128, AEL_DISCOVERED );//security keys are more important
- }
- else
- {
- AddSightEvent( player, ent->currentOrigin, 128, AEL_SUSPICIOUS );//hmm... or should this always be discovered?
- }
- }
- }
- if ( ent->contents == CONTENTS_LIGHTSABER && !Q_stricmp( "lightsaber", ent->classname ) )
- {//lightsaber
- if( ent->owner && ent->owner->client )
- {
- if ( ent->owner->client->ps.SaberLength() > 0 )
- {//it's on
- //sight event
- AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED );
- }
- }
- }
- }
- /*
- =============
- G_RunThink
- Runs thinking code for this frame if necessary
- =============
- */
- #include "../client/client.h"
- void G_RunThink (gentity_t *ent)
- {
- if ( (ent->nextthink <= 0) || (ent->nextthink > level.time) )
- {
- goto runicarus;
- }
-
- ent->nextthink = 0;
- if ( ent->e_ThinkFunc == thinkF_NULL ) // actually you don't need this since I check for it in the next function -slc
- {
- goto runicarus;
- }
- GEntity_ThinkFunc( ent );
- runicarus:
- if ( ent->inuse ) // GEntity_ThinkFunc( ent ) can have freed up this ent if it was a type flier_child (stasis1 crash)
- {
- if ( ent->NPC == NULL )
- {
- if ( ent->m_iIcarusID != IIcarusInterface::ICARUS_INVALID && !stop_icarus && cls.state != CA_CONNECTED )
- {
- IIcarusInterface::GetIcarus()->Update( ent->m_iIcarusID );
- }
- }
- }
- }
- /*
- -------------------------
- G_Animate
- -------------------------
- */
- static void G_Animate ( gentity_t *self )
- {
- if ( self->s.eFlags & EF_SHADER_ANIM )
- {
- return;
- }
- if ( self->s.frame == self->endFrame )
- {
- if ( self->svFlags & SVF_ANIMATING )
- {
- // ghoul2 requires some extra checks to see if the animation is done since it doesn't set the current frame directly
- if ( self->ghoul2.size() )
- {
- float frame, junk2;
- int junk;
- // I guess query ghoul2 to find out what the current frame is and see if we are done.
- gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone,
- (cg.time?cg.time:level.time), &frame, &junk, &junk, &junk, &junk2, NULL );
- // It NEVER seems to get to what you'd think the last frame would be, so I'm doing this to try and catch when the animation has stopped
- if ( frame + 1 >= self->endFrame )
- {
- self->svFlags &= ~SVF_ANIMATING;
- Q3_TaskIDComplete( self, TID_ANIM_BOTH );
- }
- }
- else // not ghoul2
- {
- if ( self->loopAnim )
- {
- self->s.frame = self->startFrame;
- }
- else
- {
- self->svFlags &= ~SVF_ANIMATING;
- }
- //Finished sequence - FIXME: only do this once even on looping anims?
- Q3_TaskIDComplete( self, TID_ANIM_BOTH );
- }
- }
- return;
- }
- self->svFlags |= SVF_ANIMATING;
- // With ghoul2, we'll just set the desired start and end frame and let it do it's thing.
- if ( self->ghoul2.size())
- {
- self->s.frame = self->endFrame;
-
- gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone,
- self->startFrame, self->endFrame, BONE_ANIM_OVERRIDE_FREEZE, 1.0f, cg.time );
- return;
- }
- if ( self->startFrame < self->endFrame )
- {
- if ( self->s.frame < self->startFrame || self->s.frame > self->endFrame )
- {
- self->s.frame = self->startFrame;
- }
- else
- {
- self->s.frame++;
- }
- }
- else if ( self->startFrame > self->endFrame )
- {
- if ( self->s.frame > self->startFrame || self->s.frame < self->endFrame )
- {
- self->s.frame = self->startFrame;
- }
- else
- {
- self->s.frame--;
- }
- }
- else
- {
- self->s.frame = self->endFrame;
- }
- }
- /*
- -------------------------
- ResetTeamCounters
- -------------------------
- */
- /*
- void ResetTeamCounters( void )
- {
- //clear team enemy counters
- for ( int team = TEAM_FREE; team < TEAM_NUM_TEAMS; team++ )
- {
- teamEnemyCount[team] = 0;
- teamCount[team] = 0;
- }
- }
- */
- /*
- -------------------------
- UpdateTeamCounters
- -------------------------
- */
- /*
- void UpdateTeamCounters( gentity_t *ent )
- {
- if ( !ent->NPC )
- {
- return;
- }
- if ( !ent->client )
- {
- return;
- }
- if ( ent->health <= 0 )
- {
- return;
- }
- if ( (ent->s.eFlags&EF_NODRAW) )
- {
- return;
- }
- if ( ent->client->playerTeam == TEAM_FREE )
- {
- return;
- }
- //this is an NPC who is alive and visible and is on a specific team
- teamCount[ent->client->playerTeam]++;
- if ( !ent->enemy )
- {
- return;
- }
- //ent has an enemy
- if ( !ent->enemy->client )
- {//enemy is a normal ent
- if ( ent->noDamageTeam == ent->client->playerTeam )
- {//it's on my team, don't count it as an enemy
- return;
- }
- }
- else
- {//enemy is another NPC/player
- if ( ent->enemy->client->playerTeam == ent->client->playerTeam)
- {//enemy is on the same team, don't count it as an enemy
- return;
- }
- }
- //ent's enemy is not on the same team
- teamLastEnemyTime[ent->client->playerTeam] = level.time;
- teamEnemyCount[ent->client->playerTeam]++;
- }
- */
- void G_PlayerGuiltDeath( void )
- {
- if ( player && player->client )
- {//simulate death
- player->client->ps.stats[STAT_HEALTH] = 0;
- //turn off saber
- if ( player->client->ps.weapon == WP_SABER && player->client->ps.SaberActive() )
- {
- G_SoundIndexOnEnt( player, CHAN_WEAPON, player->client->ps.saber[0].soundOff );
- player->client->ps.SaberDeactivate();
- }
- //play the "what have I done?!" anim
- NPC_SetAnim( player, SETANIM_BOTH, BOTH_FORCEHEAL_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- player->client->ps.legsAnimTimer = player->client->ps.torsoAnimTimer = -1;
- //look at yourself
- player->client->ps.stats[STAT_DEAD_YAW] = player->client->ps.viewangles[YAW]+180;
- }
- }
- extern void NPC_SetAnim(gentity_t *ent,int setAnimParts,int anim,int setAnimFlags, int iBlend);
- extern void G_MakeTeamVulnerable( void );
- int killPlayerTimer = 0;
- static void G_CheckEndLevelTimers( gentity_t *ent )
- {
- if ( killPlayerTimer && level.time > killPlayerTimer )
- {
- killPlayerTimer = 0;
- ent->health = 0;
- if ( ent->client && ent->client->ps.stats[STAT_HEALTH] > 0 )
- {
- G_PlayerGuiltDeath();
- //cg.missionStatusShow = qtrue;
- statusTextIndex = MISSIONFAILED_TURNED;
- //debounce respawn time
- ent->client->respawnTime = level.time + 2000;
- //stop all scripts
- stop_icarus = qtrue;
- //make the team killable
- G_MakeTeamVulnerable();
- }
- }
- }
- //rww - RAGDOLL_BEGIN
- class CGameRagDollUpdateParams : public CRagDollUpdateParams
- {
- void EffectorCollision(const SRagDollEffectorCollision &data)
- {
- //Com_Printf("Effector Collision at (%f %f %f)\n",data.effectorPosition[0],data.effectorPosition[1],data.effectorPosition[2]);
- vec3_t effectorPosDif;
- if (data.useTracePlane)
- {
- float magicFactor42=64.0f;
- VectorScale(data.tr.plane.normal,magicFactor42,effectorPosDif);
- }
- else
- {
- gentity_t *thisguy = &g_entities[me];
- if (thisguy && thisguy->client)
- {
- VectorSubtract(thisguy->client->ps.origin, data.effectorPosition, effectorPosDif);
- }
- else
- {
- return;
- }
- }
- VectorAdd(effectorTotal, effectorPosDif, effectorTotal);
- hasEffectorData = qtrue;
- return;
- }
- void RagDollBegin()
- {
- return;
- }
- virtual void RagDollSettled()
- {
- return;
- }
- void Collision() // we had a collision, please stop animating and (sometime soon) call SetRagDoll RP_DEATH_COLLISION
- {
- return;
- }
-
- #ifdef _DEBUG
- void DebugLine(vec3_t p1,vec3_t p2,int color,bool bbox)
- {
- if (!bbox)
- {
- CG_TestLine(p1, p2, 50, color, 1);
- }
- return;
- }
- #endif
- public:
- vec3_t effectorTotal;
- qboolean hasEffectorData;
- };
- //list of valid ragdoll effectors
- static const char *g_effectorStringTable[] =
- { //commented out the ones I don't want dragging to affect
- // "thoracic",
- // "rhand",
- "lhand",
- "rtibia",
- "ltibia",
- "rtalus",
- "ltalus",
- // "rradiusX",
- "lradiusX",
- "rfemurX",
- "lfemurX",
- // "ceyebrow",
- NULL //always terminate
- };
- extern qboolean G_StandardHumanoid( gentity_t *self );
- extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
- extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
- extern qboolean G_ReleaseEntity( gentity_t *grabber );
- static void G_BodyDragUpdate(gentity_t *ent, gentity_t *dragger)
- {
- vec3_t handVec;
- float handDist;
- assert(ent && ent->inuse && ent->client && ent->ghoul2.size() &&
- dragger && dragger->inuse && dragger->client && dragger->ghoul2.size());
- VectorSubtract(dragger->client->renderInfo.handRPoint, ent->client->renderInfo.torsoPoint, handVec);
- handDist = VectorLength(handVec);
- if (handDist > 64.0f)
- {
- G_ReleaseEntity(dragger);
- }
- else if (handDist > 12.0f)
- {
- VectorNormalize(handVec);
- VectorScale(handVec, 256.0f, handVec);
- handVec[2] = 0;
- //VectorAdd(ent->client->ps.velocity, handVec, ent->client->ps.velocity);
- //VectorCopy(handVec, ent->client->ps.velocity);
- ent->client->ps.velocity[0] = handVec[0];
- ent->client->ps.velocity[1] = handVec[1];
- }
- }
- //we want to see which way the pelvis is facing to get a relatively oriented base settling frame
- //this is to avoid the arms stretching in opposite directions on the body trying to reach the base
- //pose if the pelvis is flipped opposite of the base pose or something -rww
- static int G_RagAnimForPositioning(gentity_t *ent)
- {
- vec3_t dir;
- mdxaBone_t matrix;
- assert(ent->client);
- vec3_t G2Angles = {0, ent->client->ps.viewangles[YAW], 0};
- assert(ent->ghoul2.size() > 0);
- assert(ent->crotchBolt > -1);
- gi.G2API_GetBoltMatrix(ent->ghoul2, ent->playerModel, ent->crotchBolt, &matrix, G2Angles, ent->client->ps.origin,
- (cg.time?cg.time:level.time), NULL, ent->s.modelScale);
- gi.G2API_GiveMeVectorFromMatrix(matrix, NEGATIVE_Z, dir);
- if (dir[2] > 0.1f)
- { //facing up
- return BOTH_DEADFLOP2;
- }
- else
- { //facing down
- return BOTH_DEADFLOP1;
- }
- }
- static inline qboolean G_RagWantsHumanoidsOnly( CGhoul2Info *ghlInfo )
- {
- char *GLAName;
- GLAName = gi.G2API_GetGLAName( ghlInfo );
- assert(GLAName);
- if ( !Q_stricmp( "models/players/_humanoid/_humanoid", GLAName ) )
- {//only _humanoid skeleton is expected to have these
- return qtrue;
- }
- return qfalse;
- }
- //rww - game interface for the ragdoll stuff.
- //Returns qtrue if the entity is now in a ragdoll state, otherwise qfalse.
- //(ported from MP's CG version)
- qboolean G_RagDoll(gentity_t *ent, vec3_t forcedAngles)
- {
- vec3_t G2Angles;
- vec3_t usedOrg;
- qboolean inSomething = qfalse;
- int ragAnim;
- //int ragVar = gi.Cvar_VariableIntegerValue("broadsword");
- int ragVar = g_broadsword->integer;
- if (!ragVar)
- {
- return qfalse;
- }
- if (!ent ||
- !ent->inuse ||
- !ent->client ||
- ent->health > 0 ||
- ent->client->noRagTime >= level.time ||
- ent->client->noRagTime==-1 ||
- (ent->s.powerups & (1 << PW_DISRUPTION)) ||
- !ent->e_DieFunc ||
- ent->playerModel < 0 ||
- !ent->ghoul2.size() ||
- !G_RagWantsHumanoidsOnly(&ent->ghoul2[ent->playerModel])
- )
- {
- return qfalse;
- }
- VectorCopy(forcedAngles, G2Angles);
- forcedAngles[0] = forcedAngles[2] = 0;
- if (ent->client->ps.heldByClient <= ENTITYNUM_WORLD)
- {
- gentity_t *grabbedBy = &g_entities[ent->client->ps.heldByClient];
- if (grabbedBy->inuse && grabbedBy->client &&
- grabbedBy->ghoul2.size())
- {
- G_BodyDragUpdate(ent, grabbedBy);
- }
- }
- //--FIXME: do not go into ragdoll if in a spinning death anim or something, it really messes things up
- //rww 12/17/02 - should be ok now actually with my new stuff
- VectorCopy(ent->client->ps.origin, usedOrg);
- if (!ent->client->isRagging)
- { //If we're not in a ragdoll state, perform the checks.
- if (ent->client->ps.heldByClient <= ENTITYNUM_WORLD)
- { //want to rag no matter what then
- inSomething = qtrue;
- }
- else if (ent->client->ps.groundEntityNum == ENTITYNUM_NONE)
- {
- vec3_t cVel;
- VectorCopy(ent->client->ps.velocity, cVel);
- if (VectorNormalize(cVel) > 400)
- { //if he's flying through the air at a good enough speed, switch into ragdoll
- inSomething = qtrue;
- }
- }
- if (ragVar > 1)
- { //go into rag instantly upon death
- inSomething = qtrue;
- //also shove them a tad so they don't just collapse onto the floor
- //VectorScale(ent->client->ps.velocity, 1.3f, ent->client->ps.velocity);
- ent->client->ps.velocity[2] += 32;
- }
- if (!inSomething)
- {
- int i = 0;
- int boltChecks[5];
- vec3_t boltPoints[5];
- vec3_t trStart, trEnd;
- vec3_t tAng;
- qboolean deathDone = qfalse;
- trace_t tr;
- mdxaBone_t boltMatrix;
- VectorSet( tAng, 0, ent->client->ps.viewangles[YAW], 0 );
- if (ent->client->ps.legsAnimTimer <= 0)
- { //Looks like the death anim is done playing
- deathDone = qtrue;
- }
- //if (deathDone)
- if (1)
- { //only trace from the hands if the death anim is already done.
- boltChecks[0] = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "rhand");
- boltChecks[1] = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lhand");
- }
- else
- { //otherwise start the trace loop at the cranium.
- i = 2;
- }
- boltChecks[2] = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cranium");
- boltChecks[3] = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "rtalus");
- boltChecks[4] = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "ltalus");
- //Do the head first, because the hands reference it anyway.
- gi.G2API_GetBoltMatrix(ent->ghoul2, ent->playerModel, boltChecks[2], &boltMatrix, tAng, ent->client->ps.origin, (cg.time?cg.time:level.time), NULL, ent->s.modelScale);
- gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, boltPoints[2]);
- while (i < 5)
- {
- if (i < 2)
- { //when doing hands, trace to the head instead of origin
- gi.G2API_GetBoltMatrix(ent->ghoul2, ent->playerModel, boltChecks[i], &boltMatrix, tAng, ent->client->ps.origin, (cg.time?cg.time:level.time), NULL, ent->s.modelScale);
- gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, boltPoints[i]);
- VectorCopy(boltPoints[i], trStart);
- VectorCopy(boltPoints[2], trEnd);
- }
- else
- {
- if (i > 2)
- { //2 is the head, which already has the bolt point.
- gi.G2API_GetBoltMatrix(ent->ghoul2, ent->playerModel, boltChecks[i], &boltMatrix, tAng, ent->client->ps.origin, (cg.time?cg.time:level.time), NULL, ent->s.modelScale);
- gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, boltPoints[i]);
- }
- VectorCopy(boltPoints[i], trStart);
- VectorCopy(ent->client->ps.origin, trEnd);
- }
- //Now that we have all that sorted out, trace between the two points we desire.
- gi.trace(&tr, trStart, NULL, NULL, trEnd, ent->s.number, MASK_SOLID);
- if (tr.fraction != 1.0 || tr.startsolid || tr.allsolid)
- { //Hit something or start in solid, so flag it and break.
- //This is a slight hack, but if we aren't done with the death anim, we don't really want to
- //go into ragdoll unless our body has a relatively "flat" pitch.
- #if 0
- vec3_t vSub;
- //Check the pitch from the head to the right foot (should be reasonable)
- VectorSubtract(boltPoints[2], boltPoints[3], vSub);
- VectorNormalize(vSub);
- vectoangles(vSub, vSub);
- if (deathDone || (vSub[PITCH] < 50 && vSub[PITCH] > -50))
- {
- inSomething = qtrue;
- }
- #else
- inSomething = qtrue;
- #endif
- break;
- }
- i++;
- }
- }
- if (inSomething)
- {
- /*
- PM_SetTorsoAnimTimer( ent, &ent->client->ps.torsoAnimTimer, 0 );
- PM_SetLegsAnimTimer( ent, &ent->client->ps.legsAnimTimer, 0 );
- PM_SetAnimFinal(&ent->client->ps.torsoAnim,&ent->client->ps.legsAnim,SETANIM_BOTH,ragAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD,
- &ent->client->ps.torsoAnimTimer,&ent->client->ps.legsAnimTimer,ent,500);
- */
- ent->client->isRagging = qtrue;
- }
- }
- if (ent->client->isRagging)
- { //We're in a ragdoll state, so make the call to keep our positions updated and whatnot.
- CRagDollParams tParms;
- CGameRagDollUpdateParams tuParms;
-
- ragAnim = G_RagAnimForPositioning(ent);
- /*
- if (ent->ikStatus)
- { //ik must be reset before ragdoll is started, or you'll get some interesting results.
- trap_G2API_SetBoneIKState(cent->ghoul2, cg.time, NULL, IKS_NONE, NULL);
- cent->ikStatus = qfalse;
- }
- */
- //these will be used as "base" frames for the ragoll settling.
- tParms.startFrame = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations[ragAnim].firstFrame;
- tParms.endFrame = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations[ragAnim].firstFrame+level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations[ragAnim].numFrames;
- #if 1
- {
- float currentFrame;
- int startFrame, endFrame;
- int flags;
- float animSpeed;
- if (gi.G2API_GetBoneAnim(&ent->ghoul2[0], "model_root", (cg.time?cg.time:level.time), ¤tFrame, &startFrame, &endFrame, &flags, &animSpeed, NULL))
- { //lock the anim on the current frame.
- int blendTime = 500;
- gi.G2API_SetBoneAnim(&ent->ghoul2[0], "lower_lumbar", currentFrame, currentFrame+1, flags, animSpeed,(cg.time?cg.time:level.time), currentFrame, blendTime);
- gi.G2API_SetBoneAnim(&ent->ghoul2[0], "model_root", currentFrame, currentFrame+1, flags, animSpeed, (cg.time?cg.time:level.time), currentFrame, blendTime);
- gi.G2API_SetBoneAnim(&ent->ghoul2[0], "Motion", currentFrame, currentFrame+1, flags, animSpeed, (cg.time?cg.time:level.time), currentFrame, blendTime);
- }
- }
- #endif
- gi.G2API_SetBoneAngles( &ent->ghoul2[ent->playerModel], "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 100, (cg.time?cg.time:level.time) );
- gi.G2API_SetBoneAngles( &ent->ghoul2[ent->playerModel], "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 100, (cg.time?cg.time:level.time) );
- gi.G2API_SetBoneAngles( &ent->ghoul2[ent->playerModel], "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 100, (cg.time?cg.time:level.time) );
- gi.G2API_SetBoneAngles( &ent->ghoul2[ent->playerModel], "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 100, (cg.time?cg.time:level.time) );
- VectorCopy(G2Angles, tParms.angles);
- VectorCopy(usedOrg, tParms.position);
- VectorCopy(ent->s.modelScale, tParms.scale);
- tParms.me = ent->s.number;
- tParms.groundEnt = ent->client->ps.groundEntityNum;
- tParms.collisionType = 1;
- tParms.RagPhase=CRagDollParams::ERagPhase::RP_DEATH_COLLISION;
- tParms.fShotStrength = 4;
- gi.G2API_SetRagDoll(ent->ghoul2, &tParms);
- tuParms.hasEffectorData = qfalse;
- VectorClear(tuParms.effectorTotal);
- VectorCopy(G2Angles, tuParms.angles);
- VectorCopy(usedOrg, tuParms.position);
- VectorCopy(ent->s.modelScale, tuParms.scale);
- tuParms.me = ent->s.number;
- tuParms.settleFrame = tParms.endFrame-1;
- tuParms.groundEnt = ent->client->ps.groundEntityNum;
- if (ent->client->ps.groundEntityNum != ENTITYNUM_NONE)
- {
- VectorClear(tuParms.velocity);
- }
- else
- {
- VectorScale(ent->client->ps.velocity, 0.4f, tuParms.velocity);
- }
- gi.G2API_AnimateG2Models(ent->ghoul2, (cg.time?cg.time:level.time), &tuParms);
- if (ent->client->ps.heldByClient <= ENTITYNUM_WORLD)
- {
- gentity_t *grabEnt;
- grabEnt = &g_entities[ent->client->ps.heldByClient];
- if (grabEnt->client && grabEnt->ghoul2.size())
- {
- vec3_t bOrg;
- vec3_t thisHand;
- vec3_t hands;
- vec3_t pcjMin, pcjMax;
- vec3_t pDif;
- vec3_t thorPoint;
- float difLen;
- //Get the person who is holding our hand's hand location
- //gi.G2API_GetBoltMatrix(grabEnt->ghoul2, 0, grabEnt->gent->client->renderInfo.handRPoint, &matrix, grabEnt->turAngles, grabEnt->lerpOrigin,
- // cg.time, cgs.gameModels, grabEnt->modelScale);
- //BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, bOrg);
- VectorCopy(grabEnt->client->renderInfo.handRPoint, bOrg);
- //Get our hand's location
- //trap_G2API_GetBoltMatrix(cent->ghoul2, 0, 0, &matrix, cent->turAngles, cent->lerpOrigin,
- // cg.time, cgs.gameModels, cent->modelScale);
- //BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, thisHand);
- VectorCopy(ent->client->renderInfo.handRPoint, thisHand);
- //Get the position of the thoracic bone for hinting its velocity later on
- //thorBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "thoracic");
- //trap_G2API_GetBoltMatrix(cent->ghoul2, 0, thorBolt, &matrix, cent->turAngles, cent->lerpOrigin,
- // cg.time, cgs.gameModels, cent->modelScale);
- //BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, thorPoint);
- VectorCopy(ent->client->renderInfo.torsoPoint, thorPoint);
- VectorSubtract(bOrg, thisHand, hands);
- if (VectorLength(hands) < 3.0f)
- {
- gi.G2API_RagForceSolve(ent->ghoul2, qfalse);
- }
- else
- {
- gi.G2API_RagForceSolve(ent->ghoul2, qtrue);
- }
- //got the hand pos of him, now we want to make our hand go to it
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rhand", bOrg);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rradius", bOrg);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rradiusX", bOrg);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rhumerusX", bOrg);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rhumerus", bOrg);
- //Make these two solve quickly so we can update decently
- gi.G2API_RagPCJGradientSpeed(ent->ghoul2, "rhumerus", 1.5f);
- gi.G2API_RagPCJGradientSpeed(ent->ghoul2, "rradius", 1.5f);
- //Break the constraints on them I suppose
- VectorSet(pcjMin, -999, -999, -999);
- VectorSet(pcjMax, 999, 999, 999);
- gi.G2API_RagPCJConstraint(ent->ghoul2, "rhumerus", pcjMin, pcjMax);
- gi.G2API_RagPCJConstraint(ent->ghoul2, "rradius", pcjMin, pcjMax);
- ent->client->overridingBones = level.time + 2000;
- //hit the thoracic velocity to the hand point
- VectorSubtract(bOrg, thorPoint, hands);
- VectorNormalize(hands);
- VectorScale(hands, 2048.0f, hands);
- gi.G2API_RagEffectorKick(ent->ghoul2, "thoracic", hands);
- gi.G2API_RagEffectorKick(ent->ghoul2, "ceyebrow", hands);
- VectorSubtract(ent->client->ragLastOrigin, ent->client->ps.origin, pDif);
- VectorCopy(ent->client->ps.origin, ent->client->ragLastOrigin);
- if (ent->client->ragLastOriginTime >= level.time && ent->client->ps.groundEntityNum != ENTITYNUM_NONE)
- { //make sure it's reasonably updated
- difLen = VectorLength(pDif);
- if (difLen > 0.0f)
- { //if we're being dragged, then kick all the bones around a bit
- vec3_t dVel;
- vec3_t rVel;
- int i = 0;
- if (difLen < 12.0f)
- {
- VectorScale(pDif, 12.0f/difLen, pDif);
- difLen = 12.0f;
- }
- while (g_effectorStringTable[i])
- {
- VectorCopy(pDif, dVel);
- dVel[2] = 0;
- //Factor in a random velocity
- VectorSet(rVel, Q_flrand(-0.1f, 0.1f), Q_flrand(-0.1f, 0.1f), Q_flrand(0.1f, 0.5));
- VectorScale(rVel, 8.0f, rVel);
- VectorAdd(dVel, rVel, dVel);
- VectorScale(dVel, 10.0f, dVel);
- gi.G2API_RagEffectorKick(ent->ghoul2, g_effectorStringTable[i], dVel);
- i++;
- }
- }
- }
- ent->client->ragLastOriginTime = level.time + 1000;
- }
- }
- else if (ent->client->overridingBones)
- { //reset things to their normal rag state
- vec3_t pcjMin, pcjMax;
- vec3_t dVel;
- //got the hand pos of him, now we want to make our hand go to it
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rhand", NULL);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rradius", NULL);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rradiusX", NULL);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rhumerusX", NULL);
- gi.G2API_RagEffectorGoal(ent->ghoul2, "rhumerus", NULL);
- VectorSet(dVel, 0.0f, 0.0f, -64.0f);
- gi.G2API_RagEffectorKick(ent->ghoul2, "rhand", dVel);
- gi.G2API_RagPCJGradientSpeed(ent->ghoul2, "rhumerus", 0.0f);
- gi.G2API_RagPCJGradientSpeed(ent->ghoul2, "rradius", 0.0f);
- VectorSet(pcjMin,-100.0f,-40.0f,-15.0f);
- VectorSet(pcjMax,-15.0f,80.0f,15.0f);
- gi.G2API_RagPCJConstraint(ent->ghoul2, "rhumerus", pcjMin, pcjMax);
- VectorSet(pcjMin,-25.0f,-20.0f,-20.0f);
- VectorSet(pcjMax,90.0f,20.0f,-20.0f);
- gi.G2API_RagPCJConstraint(ent->ghoul2, "rradius", pcjMin, pcjMax);
- if (ent->client->overridingBones < level.time)
- {
- gi.G2API_RagForceSolve(ent->ghoul2, qfalse);
- ent->client->overridingBones = 0;
- }
- else
- {
- gi.G2API_RagForceSolve(ent->ghoul2, qtrue);
- }
- }
- if (tuParms.hasEffectorData)
- {
- /*
- vec3_t efDir;
- vec3_t existingVelocity;
- float evValue = 0;
- VectorCopy(tuParms.effectorTotal, efDir);
- VectorNormalize(efDir);
- VectorCopy(ent->client->ps.velocity, existingVelocity);
- evValue = VectorNormalize(existingVelocity);
- if (evValue < 32)
- {
- ent->client->ps.velocity[0] += efDir[0]*64;
- ent->client->ps.velocity[1] += efDir[1]*64;
- ent->client->ps.velocity[2] += efDir[2]*64;
- }
- */
- VectorNormalize(tuParms.effectorTotal);
- VectorScale(tuParms.effectorTotal, 7.0f, tuParms.effectorTotal);
- VectorAdd(ent->client->ps.velocity, tuParms.effectorTotal, ent->client->ps.velocity);
- }
- return qtrue;
- }
- return qfalse;
- }
- //rww - RAGDOLL_END
- /*
- ================
- G_FreeUselessEnemies
- Continually monitors for enemies that are no longer relevant,
- and kills them off. TODO: Also kill off allies (jedi in kor[12])
- ================
- */
- char current_speeders = 0;
- void G_FreeUselessEnemies(void)
- {
- // We check three entities per frame, cuts down on CPU impact, still
- // ends up checking every entity within about 10 seconds
- static int freeEntNum = 1;
- for( int j = 0; j < 3; ++j )
- {
- if( current_speeders > 24 && // too many speeders
- PInUse(freeEntNum) && // not in use
- g_entities[freeEntNum].health && // has health
- g_entities[freeEntNum].m_pVehicle && // valid vehicle
- g_entities[freeEntNum].m_pVehicle->m_pVehicleInfo && // valid vehicle info
- g_entities[freeEntNum].m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER && // a speeder
- !g_entities[freeEntNum].m_pVehicle->m_pPilot && // doesn't have a pilot
- strcmp(g_entities[freeEntNum].m_pVehicle->m_pVehicleInfo->name, "Swoop_cin")) // not needed for a cinematic
- {
- // blow it up
- G_Damage(&g_entities[freeEntNum], NULL, NULL, 0, 0, 200000, DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
- // decrement the number of speeders
- current_speeders--;
- // set the alreadyCleaned flag so that when the entitiy is removed current_speeders is not decremented again
- g_entities[freeEntNum].m_pVehicle->alreadyCleaned = true;
- }
- // If the entity is an NPC which has no key, isn't invulnerable,
- // has fired, but not in a while, has health, isn't on the player's team,
- // and is far away, destroy it.
- else if( PInUse(freeEntNum) &&
- g_entities[freeEntNum].NPC &&
- !g_entities[freeEntNum].message &&
- g_entities[freeEntNum].client &&
- g_entities[freeEntNum].health &&
- g_entities[freeEntNum].client->playerTeam != TEAM_PLAYER &&
- !(g_entities[freeEntNum].client->ps.powerups[PW_INVINCIBLE]>level.time) &&
- !(g_entities[freeEntNum].flags & FL_UNDYING) &&
- // !(g_entities[freeEntNum].client->ps.eFlags & EF_INVULNERABLE) &&
- g_entities[freeEntNum].NPC->shotTime &&
- g_entities[freeEntNum].NPC->shotTime + 10000 < level.time &&
- DistanceSquared(g_entities[freeEntNum].currentOrigin,
- g_entities[0].currentOrigin) > (2000 * 2000))
- {
- G_Damage(&g_entities[freeEntNum], NULL, NULL, 0, 0, 100000,
- DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
- g_entities[freeEntNum].NPC->timeOfDeath = 0;
- }
- freeEntNum++;
- freeEntNum %= MAX_GENTITIES;
- }
- }
- /*
- ================
- G_RunFrame
- Advances the non-player objects in the world
- ================
- */
- void G_MassFreeUselessThings( void )
- {
- // We check three entities per frame, cuts down on CPU impact, still
- // ends up checking every entity within about 10 seconds
- for( int j = 0; j < MAX_GENTITIES; ++j )
- {
- if( PInUse(j) && // not in use
- g_entities[j].health && // has health
- g_entities[j].m_pVehicle && // valid vehicle
- g_entities[j].m_pVehicle->m_pVehicleInfo && // valid vehicle info
- g_entities[j].m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER && // a speeder
- !g_entities[j].m_pVehicle->m_pPilot && // doesn't have a pilot
- strcmp(g_entities[j].m_pVehicle->m_pVehicleInfo->name, "Swoop_cin")) // not needed for a cinematic
- {
- G_Damage(&g_entities[j], NULL, NULL, 0, 0, 200000, DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
- }
- // If the entity is an NPC which has no key, isn't invulnerable,
- // has fired, but not in a while, has health, isn't on the player's team,
- // and is far away, destroy it.
- else if( (PInUse(j) &&
- g_entities[j].NPC &&
- !g_entities[j].message &&
- g_entities[j].client &&
- g_entities[j].health &&
- g_entities[j].client->playerTeam != TEAM_PLAYER &&
- !(g_entities[j].client->ps.powerups[PW_INVINCIBLE]>level.time) &&
- g_entities[j].NPC->shotTime &&
- g_entities[j].NPC->shotTime + 10000 < level.time &&
- DistanceSquared(g_entities[j].currentOrigin,
- g_entities[0].currentOrigin) > (2000 * 2000)) )
- {
- G_Damage(&g_entities[j], NULL, NULL, 0, 0, 100000,
- DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
- g_entities[j].NPC->timeOfDeath = 0;
- }
- }
- }
- /*
- ================
- G_RunFrame
- Advances the non-player objects in the world
- ================
- */
- #if AI_TIMERS
- int AITime = 0;
- int navTime = 0;
- #endif// AI_TIMERS
- extern cvar_t* in_shaking_rumble;
- void G_RunFrame( int levelTime ) {
- int i;
- gentity_t *ent;
- int msec;
- int ents_inuse=0; // someone's gonna be pissed I put this here...
- #if AI_TIMERS
- AITime = 0;
- navTime = 0;
- #endif// AI_TIMERS
-
- level.framenum++;
- level.previousTime = level.time;
- level.time = levelTime;
- msec = level.time - level.previousTime;
-
- //ResetTeamCounters();
- NAV::DecayDangerSenses();
- Rail_Update();
- Troop_Update();
- Pilot_Update();
-
- if (player && gi.WE_IsShaking(player->currentOrigin))
- {
- CGCam_Shake(0.45f, 100, in_shaking_rumble->integer);
- }
-
- AI_UpdateGroups();
- //Look to clear out old events
- ClearPlayerAlertEvents();
- //Run the frame for all entities
- // for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++)
- for ( i = 0; i < globals.num_entities ; i++)
- {
- // if ( !ent->inuse )
- // continue;
- if(!PInUse(i))
- continue;
- ents_inuse++;
- ent = &g_entities[i];
- // clear events that are too old
- if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) {
- if ( ent->s.event ) {
- ent->s.event = 0; // &= EV_EVENT_BITS;
- if ( ent->client ) {
- ent->client->ps.externalEvent = 0;
- }
- }
- if ( ent->freeAfterEvent ) {
- // tempEntities or dropped items completely go away after their event
- G_FreeEntity( ent );
- continue;
- }
- /* // This is never set to true anywhere. Killing the field (BTO - VV)
- else if ( ent->unlinkAfterEvent ) {
- // items that will respawn will hide themselves after their pickup event
- ent->unlinkAfterEvent = qfalse;
- gi.unlinkentity( ent );
- }
- */
- }
- // temporary entities don't think
- if ( ent->freeAfterEvent )
- continue;
- G_CheckTasksCompleted(ent);
- G_Roff( ent );
- if( !ent->client )
- {
- if ( !(ent->svFlags & SVF_SELF_ANIMATING) )
- {//FIXME: make sure this is done only for models with frames?
- //Or just flag as animating?
- if ( ent->s.eFlags & EF_ANIM_ONCE )
- {
- ent->s.frame++;
- }
- else if ( !(ent->s.eFlags & EF_ANIM_ALLFAST) )
- {
- G_Animate( ent );
- }
- }
- }
- G_CheckSpecialPersistentEvents( ent );
- if ( ent->s.eType == ET_MISSILE )
- {
- G_RunMissile( ent );
- continue;
- }
- if ( ent->s.eType == ET_ITEM )
- {
- G_RunItem( ent );
- continue;
- }
- if ( ent->s.eType == ET_MOVER )
- {
- if ( ent->model && Q_stricmp( "models/test/mikeg/tie_fighter.md3", ent->model ) == 0 )
- {
- TieFighterThink( ent );
- }
- G_RunMover( ent );
- continue;
- }
- //The player
- if ( i == 0 )
- {
- // decay batteries if the goggles are active
- if ( cg.zoomMode == 1 && ent->client->ps.batteryCharge > 0 )
- {
- ent->client->ps.batteryCharge--;
- }
- else if ( cg.zoomMode == 3 && ent->client->ps.batteryCharge > 0 )
- {
- ent->client->ps.batteryCharge -= 2;
- if ( ent->client->ps.batteryCharge < 0 )
- {
- ent->client->ps.batteryCharge = 0;
- }
- }
- G_CheckEndLevelTimers( ent );
- //Recalculate the nearest waypoint for the coming NPC updates
- NAV::GetNearestNode( ent );
- if( ent->m_iIcarusID != IIcarusInterface::ICARUS_INVALID && !stop_icarus )
- {
- IIcarusInterface::GetIcarus()->Update( ent->m_iIcarusID );
- }
- //dead
- if ( ent->health <= 0 )
- {
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//on the ground
- pitch_roll_for_slope( ent );
- }
- }
- continue; // players are ucmd driven
- }
- G_RunThink( ent ); // be aware that ent may be free after returning from here, at least one func frees them
- ClearNPCGlobals(); // but these 2 funcs are ok
- //UpdateTeamCounters( ent ); // to call anyway on a freed ent.
- }
- // perform final fixups on the player
- ent = &g_entities[0];
- if ( ent->inuse )
- {
- ClientEndFrame( ent );
- }
- if( g_numEntities->integer )
- {
- gi.Printf( S_COLOR_WHITE"Number of Entities in use : %d\n", ents_inuse );
- }
- //DEBUG STUFF
- NAV::ShowDebugInfo(ent->currentOrigin, ent->waypoint);
- NPC_ShowDebugInfo();
- G_DynamicMusicUpdate();
- #if AI_TIMERS
- AITime -= navTime;
- if ( AITime > 20 )
- {
- gi.Printf( S_COLOR_RED"ERROR: total AI time: %d\n", AITime );
- }
- else if ( AITime > 10 )
- {
- gi.Printf( S_COLOR_YELLOW"WARNING: total AI time: %d\n", AITime );
- }
- else if ( AITime > 2 )
- {
- gi.Printf( S_COLOR_GREEN"total AI time: %d\n", AITime );
- }
- if ( navTime > 20 )
- {
- gi.Printf( S_COLOR_RED"ERROR: total nav time: %d\n", navTime );
- }
- else if ( navTime > 10 )
- {
- gi.Printf( S_COLOR_YELLOW"WARNING: total nav time: %d\n", navTime );
- }
- else if ( navTime > 2 )
- {
- gi.Printf( S_COLOR_GREEN"total nav time: %d\n", navTime );
- }
- #endif// AI_TIMERS
- extern int delayedShutDown;
- if ( g_delayedShutdown->integer && delayedShutDown != 0 && delayedShutDown < level.time )
- {
- assert(0);
- G_Error( "Game Errors. Scroll up the console to read them.\n" );
- }
- #ifdef _DEBUG
- if(!(level.framenum&0xff))
- {
- ValidateInUseBits();
- }
- #endif
- #ifdef _XBOX
- // update the water levels for npcs
- extern void UpdateNPCWaterLevels(void);
- UpdateNPCWaterLevels();
- // Kill off any AI that are lingering!
- G_FreeUselessEnemies();
- #endif // _XBOX
- }
- extern qboolean player_locked;
- void G_LoadSave_WriteMiscData(void)
- {
- gi.AppendToSaveGame('LCKD', &player_locked, sizeof(player_locked));
- }
- void G_LoadSave_ReadMiscData(void)
- {
- gi.ReadFromSaveGame('LCKD', &player_locked, sizeof(player_locked));
- }
- /*
- void PrintEntClassname( int gentNum )
- {
- Com_Printf( "%d: %s in snapshot\n", gentNum, g_entities[gentNum].classname );
- }
- */
- IGhoul2InfoArray &TheGameGhoul2InfoArray()
- {
- return gi.TheGhoul2InfoArray();
- }
- extern bool bHadPersistedSurface;
- gentity_t *pReservedZoneGentities = NULL;
- void G_ReserveZoneGentities( void )
- {
- pReservedZoneGentities = (gentity_t *) Z_Malloc( sizeof(gentity_t) * MAX_GENTITIES, TAG_TEMP_WORKSPACE, qfalse, 4 );
- }
- void G_AllocGentities( void )
- {
- g_entities = (gentity_t *) HeapAlloc( GetProcessHeap(), 0, sizeof(gentity_t) * MAX_GENTITIES );
- // If it worked...
- if( g_entities )
- {
- // And we had a persisted surface. (Normal case). Free the reserved zone:
- if( bHadPersistedSurface )
- {
- Z_Free( pReservedZoneGentities );
- pReservedZoneGentities = NULL;
- return;
- }
- // Really bad case #1:
- Com_PrintfAlways( "G_AllocGentities: Extreme failure #1\n" );
- return;
- }
- else
- {
- // It failed. Hopefully that means that there was no persisted surface.
- if( !bHadPersistedSurface )
- {
- g_entities = pReservedZoneGentities;
- pReservedZoneGentities = NULL;
- return;
- }
- // Really bad case #2:
- Com_PrintfAlways( "G_AllocGentities: Extreme failure #2\n" );
- return;
- }
- }
|