g_client.cpp 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425
  1. // leave this line at the top for all g_xxxx.cpp files...
  2. #include "g_headers.h"
  3. #include "IcarusInterface.h"
  4. #include "Q3_Interface.h"
  5. #include "g_local.h"
  6. #include "g_functions.h"
  7. #include "anims.h"
  8. #include "wp_saber.h"
  9. #include "g_vehicles.h"
  10. #include "objectives.h"
  11. extern int WP_SaberInitBladeData( gentity_t *ent );
  12. extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
  13. extern qboolean CheatsOk( gentity_t *ent );
  14. extern void Boba_Precache( void );
  15. extern cvar_t *g_char_model;
  16. extern cvar_t *g_char_skin_head;
  17. extern cvar_t *g_char_skin_torso;
  18. extern cvar_t *g_char_skin_legs;
  19. extern cvar_t *g_char_color_red;
  20. extern cvar_t *g_char_color_green;
  21. extern cvar_t *g_char_color_blue;
  22. extern cvar_t *g_saber;
  23. extern cvar_t *g_saber2;
  24. extern cvar_t *g_saber_color;
  25. extern cvar_t *g_saber2_color;
  26. extern cvar_t *g_saberDarkSideSaberColor;
  27. // g_client.c -- client functions that don't happen every frame
  28. float DEFAULT_MINS_0 = -16;
  29. float DEFAULT_MINS_1 = -16;
  30. float DEFAULT_MAXS_0 = 16;
  31. float DEFAULT_MAXS_1 = 16;
  32. float DEFAULT_PLAYER_RADIUS = sqrt((DEFAULT_MAXS_0*DEFAULT_MAXS_0) + (DEFAULT_MAXS_1*DEFAULT_MAXS_1));
  33. vec3_t playerMins = {DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2};
  34. vec3_t playerMinsStep = {DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2+STEPSIZE};
  35. vec3_t playerMaxs = {DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2};
  36. void SP_misc_teleporter_dest (gentity_t *ent);
  37. /*QUAK-ED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) KEEP_PREV DROPTOFLOOR x x x STUN_BATON NOWEAPON x
  38. potential spawning position for deathmatch games.
  39. Targets will be fired when someone spawns in on them.
  40. */
  41. void SP_info_player_deathmatch(gentity_t *ent) {
  42. SP_misc_teleporter_dest (ent);
  43. if ( ent->spawnflags & 32 ) // STUN_BATON
  44. {
  45. RegisterItem( FindItemForWeapon( WP_STUN_BATON ));
  46. }
  47. else
  48. {
  49. RegisterItem( FindItemForWeapon( WP_SABER ) ); //these are given in ClientSpawn(), but we register them now before cgame starts
  50. saberInfo_t saber;
  51. WP_SaberParseParms( g_saber->string, &saber );//get saber sounds and models cached before client begins
  52. if (saber.model) G_ModelIndex( saber.model );
  53. if (saber.brokenSaber1) G_ModelIndex( saber.brokenSaber1 );
  54. if (saber.brokenSaber2) G_ModelIndex( saber.brokenSaber2 );
  55. if (saber.skin) G_SkinIndex( saber.skin );
  56. WP_SaberFreeStrings(saber);
  57. }
  58. }
  59. /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) KEEP_PREV DROPTOFLOOR x x x STUN_BATON NOWEAPON x
  60. KEEP_PREV - keep previous health + armor
  61. DROPTOFLOOR - Player will start on the first solid structure under it
  62. STUN_BATON - Gives player the stun baton and bryar pistol, but not the saber, plus any weapons they may have carried over from previous levels.
  63. Targets will be fired when someone spawns in on them.
  64. equivalant to info_player_deathmatch
  65. */
  66. void SP_info_player_start(gentity_t *ent) {
  67. ent->classname = "info_player_deathmatch";
  68. SP_info_player_deathmatch( ent );
  69. }
  70. /*
  71. =======================================================================
  72. SelectSpawnPoint
  73. =======================================================================
  74. */
  75. /*
  76. ================
  77. SpotWouldTelefrag
  78. ================
  79. */
  80. qboolean SpotWouldTelefrag( gentity_t *spot, team_t checkteam )
  81. {
  82. int i, num;
  83. gentity_t *touch[MAX_GENTITIES], *hit;
  84. vec3_t mins, maxs;
  85. // If we have a mins, use that instead of the hardcoded bounding box
  86. if ( spot->mins && VectorLength( spot->mins ) )
  87. VectorAdd( spot->s.origin, spot->mins, mins );
  88. else
  89. VectorAdd( spot->s.origin, playerMins, mins );
  90. // If we have a maxs, use that instead of the hardcoded bounding box
  91. if ( spot->maxs && VectorLength( spot->maxs ) )
  92. VectorAdd( spot->s.origin, spot->maxs, maxs );
  93. else
  94. VectorAdd( spot->s.origin, playerMaxs, maxs );
  95. num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  96. for (i=0 ; i<num ; i++)
  97. {
  98. hit = touch[i];
  99. if ( hit != spot && hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 )
  100. {
  101. if ( hit->contents & CONTENTS_BODY )
  102. {
  103. if( checkteam == TEAM_FREE || hit->client->playerTeam == checkteam )
  104. {//checking against teammates only...?
  105. return qtrue;
  106. }
  107. }
  108. }
  109. }
  110. return qfalse;
  111. }
  112. qboolean SpotWouldTelefrag2( gentity_t *mover, vec3_t dest )
  113. {
  114. int i, num;
  115. gentity_t *touch[MAX_GENTITIES], *hit;
  116. vec3_t mins, maxs;
  117. VectorAdd( dest, mover->mins, mins );
  118. VectorAdd( dest, mover->maxs, maxs );
  119. num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  120. for (i=0 ; i<num ; i++)
  121. {
  122. hit = touch[i];
  123. if ( hit == mover )
  124. {
  125. continue;
  126. }
  127. if ( hit->contents & mover->contents )
  128. {
  129. return qtrue;
  130. }
  131. }
  132. return qfalse;
  133. }
  134. /*
  135. ================
  136. SelectNearestDeathmatchSpawnPoint
  137. Find the spot that we DON'T want to use
  138. ================
  139. */
  140. #define MAX_SPAWN_POINTS 128
  141. gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from, team_t team ) {
  142. gentity_t *spot;
  143. float dist, nearestDist;
  144. gentity_t *nearestSpot;
  145. nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
  146. nearestSpot = NULL;
  147. spot = NULL;
  148. while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
  149. /*if ( team == TEAM_RED && ( spot->spawnflags & 2 ) ) {
  150. continue;
  151. }
  152. if ( team == TEAM_BLUE && ( spot->spawnflags & 1 ) ) {
  153. continue;
  154. }*/
  155. if ( spot->targetname != NULL ) {
  156. //this search routine should never find a spot that is targetted
  157. continue;
  158. }
  159. dist = DistanceSquared( spot->s.origin, from );
  160. if ( dist < nearestDist ) {
  161. nearestDist = dist;
  162. nearestSpot = spot;
  163. }
  164. }
  165. return nearestSpot;
  166. }
  167. /*
  168. ================
  169. SelectRandomDeathmatchSpawnPoint
  170. go to a random point that doesn't telefrag
  171. ================
  172. */
  173. #define MAX_SPAWN_POINTS 128
  174. gentity_t *SelectRandomDeathmatchSpawnPoint( team_t team ) {
  175. gentity_t *spot;
  176. int count;
  177. int selection;
  178. gentity_t *spots[MAX_SPAWN_POINTS];
  179. count = 0;
  180. spot = NULL;
  181. while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
  182. /*if ( team == TEAM_RED && ( spot->spawnflags & 2 ) ) {
  183. continue;
  184. }
  185. if ( team == TEAM_BLUE && ( spot->spawnflags & 1 ) ) {
  186. continue;
  187. }*/
  188. if ( spot->targetname != NULL ) {
  189. //this search routine should never find a spot that is targetted
  190. continue;
  191. }
  192. if ( SpotWouldTelefrag( spot, TEAM_FREE ) ) {
  193. continue;
  194. }
  195. spots[ count ] = spot;
  196. count++;
  197. }
  198. if ( !count ) { // no spots that won't telefrag
  199. spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch");
  200. if ( !spot )
  201. {
  202. return NULL;
  203. }
  204. if ( spot->targetname != NULL )
  205. {
  206. //this search routine should never find a spot that is targetted
  207. return NULL;
  208. }
  209. else
  210. {
  211. return spot;
  212. }
  213. }
  214. selection = rand() % count;
  215. return spots[ selection ];
  216. }
  217. /*
  218. ===========
  219. SelectSpawnPoint
  220. Chooses a player start, deathmatch start, etc
  221. ============
  222. */
  223. gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, team_t team, vec3_t origin, vec3_t angles ) {
  224. gentity_t *spot;
  225. gentity_t *nearestSpot;
  226. if ( level.spawntarget != NULL && level.spawntarget[0] )
  227. {//we have a spawnpoint specified, try to find it
  228. if ( (nearestSpot = spot = G_Find( NULL, FOFS(targetname), level.spawntarget )) == NULL )
  229. {//you HAVE to be able to find the desired spot
  230. G_Error( "Couldn't find spawntarget %s\n", level.spawntarget );
  231. return NULL;
  232. }
  233. }
  234. else
  235. {//not looking for a special startspot
  236. nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint, team );
  237. spot = SelectRandomDeathmatchSpawnPoint ( team );
  238. if ( spot == nearestSpot ) {
  239. // roll again if it would be real close to point of death
  240. spot = SelectRandomDeathmatchSpawnPoint ( team );
  241. }
  242. }
  243. // find a single player start spot
  244. if (!spot) {
  245. G_Error( "Couldn't find a spawn point\n" );
  246. }
  247. VectorCopy( spot->s.origin, origin );
  248. if ( spot->spawnflags & 2 )
  249. {
  250. trace_t tr;
  251. origin[2] = MIN_WORLD_COORD;
  252. gi.trace(&tr, spot->s.origin, playerMins, playerMaxs, origin, ENTITYNUM_NONE, MASK_PLAYERSOLID );
  253. if ( tr.fraction < 1.0 && !tr.allsolid && !tr.startsolid )
  254. {//found a floor
  255. VectorCopy(tr.endpos, origin );
  256. }
  257. else
  258. {//In solid or too far
  259. VectorCopy( spot->s.origin, origin );
  260. }
  261. }
  262. origin[2] += 9;
  263. VectorCopy (spot->s.angles, angles);
  264. return spot;
  265. }
  266. //======================================================================
  267. /*
  268. ==================
  269. SetClientViewAngle
  270. ==================
  271. */
  272. void SetClientViewAngle( gentity_t *ent, vec3_t angle ) {
  273. int i;
  274. // set the delta angle
  275. for (i=0 ; i<3 ; i++)
  276. {
  277. ent->client->ps.delta_angles[i] = (ANGLE2SHORT(angle[i]) - ent->client->pers.cmd_angles[i])&0xffff;
  278. }
  279. VectorCopy( angle, ent->s.angles );
  280. VectorCopy (ent->s.angles, ent->client->ps.viewangles);
  281. }
  282. /*
  283. ================
  284. respawn
  285. ================
  286. */
  287. void respawn( gentity_t *ent ) {
  288. if (Q_stricmpn(level.mapname,"_holo",5)) {
  289. gi.SendConsoleCommand("load *respawn\n"); // special case
  290. }
  291. else {//we're on the holodeck
  292. int flags;
  293. // toggle the teleport bit so the client knows to not lerp
  294. flags = ent->client->ps.eFlags;
  295. ClientSpawn(ent, eNO/*qfalse*/); // SavedGameJustLoaded_e
  296. ent->client->ps.eFlags = flags ^ EF_TELEPORT_BIT;
  297. }
  298. }
  299. /*
  300. ================
  301. PickTeam
  302. ================
  303. */
  304. team_t PickTeam( int ignoreClientNum ) {
  305. int i;
  306. int counts[TEAM_NUM_TEAMS];
  307. memset( counts, 0, sizeof( counts ) );
  308. for ( i = 0 ; i < level.maxclients ; i++ ) {
  309. if ( i == ignoreClientNum ) {
  310. continue;
  311. }
  312. if ( level.clients[i].pers.connected == CON_DISCONNECTED ) {
  313. continue;
  314. }
  315. }
  316. return TEAM_FREE;
  317. }
  318. /*
  319. ===========
  320. ForceClientSkin
  321. Forces a client's skin (for teamplay)
  322. ===========
  323. */
  324. void ForceClientSkin( gclient_t *client, char *model, const char *skin ) {
  325. char *p;
  326. if ((p = strchr(model, '/')) != NULL) {
  327. *p = 0;
  328. }
  329. Q_strcat(model, MAX_QPATH, "/");
  330. Q_strcat(model, MAX_QPATH, skin);
  331. }
  332. /*
  333. ===========
  334. ClientUserInfoChanged
  335. Called from ClientConnect when the player first connects and
  336. directly by the server system when the player updates a userinfo variable.
  337. The game can override any of the settings and call gi.SetUserinfo
  338. if desired.
  339. ============
  340. */
  341. void ClientUserinfoChanged( int clientNum ) {
  342. gentity_t *ent;
  343. char *s;
  344. char headModel[MAX_QPATH];
  345. char torsoModel[MAX_QPATH];
  346. char legsModel[MAX_QPATH];
  347. char sound[MAX_QPATH];
  348. char oldname[MAX_STRING_CHARS];
  349. gclient_t *client;
  350. char *sex;
  351. char userinfo[MAX_INFO_STRING];
  352. ent = g_entities + clientNum;
  353. client = ent->client;
  354. gi.GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
  355. // check for malformed or illegal info strings
  356. if ( !Info_Validate(userinfo) ) {
  357. strcpy (userinfo, "\\name\\badinfo");
  358. }
  359. // set name
  360. Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) );
  361. s = Info_ValueForKey (userinfo, "name");
  362. Q_strncpyz( client->pers.netname, s, sizeof(client->pers.netname) );
  363. /* if ( client->pers.connected == CON_CONNECTED ) {
  364. if ( strcmp( oldname, client->pers.netname ) ) {
  365. gi.SendServerCommand( -1, "print \"%s renamed to %s\n\"", oldname, client->pers.netname );
  366. }
  367. }
  368. */
  369. // set max health
  370. client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) );
  371. if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
  372. client->pers.maxHealth = 100;
  373. }
  374. client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
  375. // sounds
  376. Q_strncpyz( sound, Info_ValueForKey (userinfo, "snd"), sizeof( sound ) );
  377. // set model
  378. //Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headModel"), sizeof( headModel ) );
  379. //Q_strncpyz( torsoModel, Info_ValueForKey (userinfo, "torsoModel"), sizeof( torsoModel ) );
  380. //Q_strncpyz( legsModel, Info_ValueForKey (userinfo, "legsModel"), sizeof( legsModel ) );
  381. headModel[0]=0;
  382. torsoModel[0]=0;
  383. legsModel[0]=0;
  384. // sex
  385. sex = Info_ValueForKey( userinfo, "sex" );
  386. if ( !sex[0] ) {
  387. sex = "m";
  388. }
  389. // send over a subset of the userinfo keys so other clients can
  390. // print scoreboards, display models, and play custom sounds
  391. s = va("n\\%s\\t\\%i\\headModel\\%s\\torsoModel\\%s\\legsModel\\%s\\hc\\%i\\snd\\%s",
  392. client->pers.netname, client->sess.sessionTeam, headModel, torsoModel, legsModel,
  393. client->pers.maxHealth, sound );
  394. gi.SetConfigstring( CS_PLAYERS+clientNum, s );
  395. }
  396. /*
  397. ===========
  398. ClientConnect
  399. Called when a player begins connecting to the server.
  400. Called again for every map change or tournement restart.
  401. The session information will be valid after exit.
  402. Return NULL if the client should be allowed, otherwise return
  403. a string with the reason for denial.
  404. Otherwise, the client will be sent the current gamestate
  405. and will eventually get to ClientBegin.
  406. firstTime will be qtrue the very first time a client connects
  407. to the server machine, but qfalse on map changes and tournement
  408. restarts.
  409. ============
  410. */
  411. char *ClientConnect( int clientNum, qboolean firstTime, SavedGameJustLoaded_e eSavedGameJustLoaded )
  412. {
  413. gclient_t *client;
  414. char userinfo[MAX_INFO_STRING];
  415. gentity_t *ent;
  416. clientSession_t savedSess;
  417. ent = &g_entities[ clientNum ];
  418. gi.GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
  419. // they can connect
  420. ent->client = level.clients + clientNum;
  421. client = ent->client;
  422. // if (!qbFromSavedGame)
  423. if (eSavedGameJustLoaded != eFULL)
  424. {
  425. savedSess = client->sess; //
  426. memset( client, 0, sizeof(*client) );
  427. client->sess = savedSess;
  428. if ( firstTime ) { //not loading full, and directconnect
  429. client->playerTeam = TEAM_PLAYER; //set these now because after an auto_load kyle can see your team for a bit before you really join.
  430. client->enemyTeam = TEAM_ENEMY;
  431. }
  432. }
  433. client->pers.connected = CON_CONNECTING;
  434. if (eSavedGameJustLoaded == eFULL)//qbFromSavedGame)
  435. {
  436. // G_WriteClientSessionData( client ); // forget it, this is DM stuff anyway
  437. // get and distribute relevent paramters
  438. ClientUserinfoChanged( clientNum );
  439. }
  440. else
  441. {
  442. // read or initialize the session data
  443. if ( firstTime ) {
  444. G_InitSessionData( client, userinfo );
  445. }
  446. G_ReadSessionData( client );
  447. // get and distribute relevent paramters
  448. ClientUserinfoChanged( clientNum );
  449. // don't do the "xxx connected" messages if they were caried over from previous level
  450. if ( firstTime ) {
  451. gi.SendServerCommand( -1, "print \"%s connected\n\"", client->pers.netname);
  452. }
  453. }
  454. return NULL;
  455. }
  456. /*
  457. ===========
  458. ClientBegin
  459. called when a client has finished connecting, and is ready
  460. to be placed into the level. This will happen every level load,
  461. and on transition between teams, but doesn't happen on respawns
  462. ============
  463. */
  464. void ClientBegin( int clientNum, usercmd_t *cmd, SavedGameJustLoaded_e eSavedGameJustLoaded)
  465. // qboolean qbFromSavedGame
  466. {
  467. gentity_t *ent;
  468. gclient_t *client;
  469. ent = g_entities + clientNum;
  470. client = level.clients + clientNum;
  471. if (eSavedGameJustLoaded == eFULL)//qbFromSavedGame)
  472. {
  473. client->pers.connected = CON_CONNECTED;
  474. ent->client = client;
  475. ClientSpawn( ent, eSavedGameJustLoaded );
  476. }
  477. else
  478. {
  479. if ( ent->linked ) {
  480. gi.unlinkentity( ent );
  481. }
  482. G_InitGentity( ent, qfalse );
  483. ent->e_TouchFunc = touchF_NULL;
  484. ent->e_PainFunc = painF_PlayerPain;//painF_NULL;
  485. ent->client = client;
  486. client->pers.connected = CON_CONNECTED;
  487. client->pers.teamState.state = TEAM_BEGIN;
  488. _VectorCopy( cmd->angles, client->pers.cmd_angles );
  489. memset( &client->ps, 0, sizeof( client->ps ) );
  490. if( gi.Cvar_VariableIntegerValue( "g_clearstats" ) )
  491. {
  492. memset( &client->sess.missionStats, 0, sizeof( client->sess.missionStats ) );
  493. client->sess.missionStats.totalSecrets = gi.Cvar_VariableIntegerValue("newTotalSecrets");
  494. }
  495. // locate ent at a spawn point
  496. if ( ClientSpawn( ent, eSavedGameJustLoaded) ) // SavedGameJustLoaded_e
  497. {
  498. // send teleport event
  499. }
  500. client->ps.inventory[INV_GOODIE_KEY] = 0;
  501. client->ps.inventory[INV_SECURITY_KEY] = 0;
  502. }
  503. extern bool autosaveTrigger;
  504. extern bool doAutoSave;
  505. autosaveTrigger = true;
  506. if(eSavedGameJustLoaded == eNO)
  507. doAutoSave = true;
  508. else
  509. doAutoSave = false;
  510. }
  511. /*
  512. ============
  513. Player_CacheFromPrevLevel
  514. Description : just need to grab the weapon items we're going to have when we spawn so they'll be cached
  515. Return type : void
  516. Argument : void
  517. ============
  518. */
  519. void Player_CacheFromPrevLevel(void)
  520. {
  521. char s[MAX_STRING_CHARS];
  522. gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
  523. if (s[0]) // actually this would be safe anyway because of the way sscanf() works, but this is clearer
  524. {
  525. int iDummy, bits, ibits;
  526. sscanf( s, "%i %i %i %i",
  527. &iDummy,//client->ps.stats[STAT_HEALTH],
  528. &iDummy,//client->ps.stats[STAT_ARMOR],
  529. &bits, //client->ps.stats[STAT_WEAPONS]
  530. &ibits //client->ps.stats[STAT_ITEMS]
  531. );
  532. for ( int i = 1 ; i < 16 ; i++ )
  533. {
  534. if ( bits & ( 1 << i ) )
  535. {
  536. RegisterItem( FindItemForWeapon( (weapon_t)i ) );
  537. }
  538. }
  539. extern gitem_t *FindItemForInventory( int inv );
  540. for ( i = 1 ; i < 16 ; i++ )
  541. {
  542. if ( ibits & ( 1 << i ) )
  543. {
  544. RegisterItem( FindItemForInventory( i-1 ));
  545. }
  546. }
  547. }
  548. }
  549. /*
  550. ============
  551. Player_RestoreFromPrevLevel
  552. Description : retrieve maptransition data recorded by server when exiting previous level (to carry over weapons/ammo/health/etc)
  553. Return type : void
  554. Argument : gentity_t *ent
  555. ============
  556. */
  557. void Player_RestoreFromPrevLevel(gentity_t *ent)
  558. {
  559. gclient_t *client = ent->client;
  560. int i;
  561. assert(client);
  562. if (client) // though I can't see it not being true...
  563. {
  564. char s[MAX_STRING_CHARS];
  565. char saber0Name[MAX_QPATH];
  566. char saber1Name[MAX_QPATH];
  567. const char *var;
  568. gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
  569. if (strlen(s)) // actually this would be safe anyway because of the way sscanf() works, but this is clearer
  570. {// |general info |-force powers |-saber 1 |-saber 2 |-general saber
  571. sscanf( s, "%i %i %i %i %i %i %i %f %f %f %i %i %i %i %i %s %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %s %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
  572. &client->ps.stats[STAT_HEALTH],
  573. &client->ps.stats[STAT_ARMOR],
  574. &client->ps.stats[STAT_WEAPONS],
  575. &client->ps.stats[STAT_ITEMS],
  576. &client->ps.weapon,
  577. &client->ps.weaponstate,
  578. &client->ps.batteryCharge,
  579. &client->ps.viewangles[0],
  580. &client->ps.viewangles[1],
  581. &client->ps.viewangles[2],
  582. //force power data
  583. &client->ps.forcePowersKnown,
  584. &client->ps.forcePower,
  585. &client->ps.forcePowerMax,
  586. &client->ps.forcePowerRegenRate,
  587. &client->ps.forcePowerRegenAmount,
  588. //saber 1 data
  589. &saber0Name,
  590. &client->ps.saber[0].blade[0].active,
  591. &client->ps.saber[0].blade[1].active,
  592. &client->ps.saber[0].blade[2].active,
  593. &client->ps.saber[0].blade[3].active,
  594. &client->ps.saber[0].blade[4].active,
  595. &client->ps.saber[0].blade[5].active,
  596. &client->ps.saber[0].blade[6].active,
  597. &client->ps.saber[0].blade[7].active,
  598. &client->ps.saber[0].blade[0].color,
  599. &client->ps.saber[0].blade[1].color,
  600. &client->ps.saber[0].blade[2].color,
  601. &client->ps.saber[0].blade[3].color,
  602. &client->ps.saber[0].blade[4].color,
  603. &client->ps.saber[0].blade[5].color,
  604. &client->ps.saber[0].blade[6].color,
  605. &client->ps.saber[0].blade[7].color,
  606. //saber 2 data
  607. &saber1Name,
  608. &client->ps.saber[1].blade[0].active,
  609. &client->ps.saber[1].blade[1].active,
  610. &client->ps.saber[1].blade[2].active,
  611. &client->ps.saber[1].blade[3].active,
  612. &client->ps.saber[1].blade[4].active,
  613. &client->ps.saber[1].blade[5].active,
  614. &client->ps.saber[1].blade[6].active,
  615. &client->ps.saber[1].blade[7].active,
  616. &client->ps.saber[1].blade[0].color,
  617. &client->ps.saber[1].blade[1].color,
  618. &client->ps.saber[1].blade[2].color,
  619. &client->ps.saber[1].blade[3].color,
  620. &client->ps.saber[1].blade[4].color,
  621. &client->ps.saber[1].blade[5].color,
  622. &client->ps.saber[1].blade[6].color,
  623. &client->ps.saber[1].blade[7].color,
  624. //general saber data
  625. &client->ps.saberStylesKnown,
  626. &client->ps.saberAnimLevel,
  627. &client->ps.saberLockEnemy,
  628. &client->ps.saberLockTime
  629. );
  630. ent->health = client->ps.stats[STAT_HEALTH];
  631. if(ent->client->ps.saber[0].name && gi.bIsFromZone(ent->client->ps.saber[0].name, TAG_G_ALLOC)) {
  632. gi.Free(ent->client->ps.saber[0].name);
  633. }
  634. ent->client->ps.saber[0].name=0;
  635. if(ent->client->ps.saber[1].name && gi.bIsFromZone(ent->client->ps.saber[1].name, TAG_G_ALLOC) ) {
  636. gi.Free(ent->client->ps.saber[1].name);
  637. }
  638. ent->client->ps.saber[1].name=0;
  639. //NOTE: if sscanf can get a "(null)" out of strings that had NULL string pointers plugged into the original string
  640. if ( saber0Name[0] && ( Q_stricmp( saber0Name, "(null)" ) != 0 && Q_stricmp( saber0Name, "none" ) != 0) )
  641. {
  642. ent->client->ps.saber[0].name = G_NewString( saber0Name );
  643. }
  644. if ( saber1Name[0] && ( Q_stricmp( saber1Name, "(null)" ) != 0 && Q_stricmp( saber1Name, "none" ) != 0) )
  645. {//have a second saber
  646. ent->client->ps.saber[1].name = G_NewString( saber1Name );
  647. ent->client->ps.dualSabers = qtrue;
  648. }
  649. else
  650. {//have only 1 saber
  651. ent->client->ps.dualSabers = qfalse;
  652. }
  653. // slight issue with ths for the moment in that although it'll correctly restore angles it doesn't take into account
  654. // the overall map orientation, so (eg) exiting east to enter south will be out by 90 degrees, best keep spawn angles for now
  655. //
  656. // VectorClear (ent->client->pers.cmd_angles);
  657. //
  658. // SetClientViewAngle( ent, ent->client->ps.viewangles);
  659. //ammo
  660. gi.Cvar_VariableStringBuffer( "playerammo", s, sizeof(s) );
  661. i=0;
  662. var = strtok( s, " " );
  663. while( var != NULL )
  664. {
  665. /* While there are tokens in "s" */
  666. client->ps.ammo[i++] = atoi(var);
  667. /* Get next token: */
  668. var = strtok( NULL, " " );
  669. }
  670. assert (i==AMMO_MAX);
  671. //inventory
  672. gi.Cvar_VariableStringBuffer( "playerinv", s, sizeof(s) );
  673. i=0;
  674. var = strtok( s, " " );
  675. while( var != NULL )
  676. {
  677. /* While there are tokens in "s" */
  678. client->ps.inventory[i++] = atoi(var);
  679. /* Get next token: */
  680. var = strtok( NULL, " " );
  681. }
  682. assert (i==INV_MAX);
  683. // the new JK2 stuff - force powers, etc...
  684. //
  685. gi.Cvar_VariableStringBuffer( "playerfplvl", s, sizeof(s) );
  686. i=0;
  687. var = strtok( s, " " );
  688. while( var != NULL )
  689. {
  690. /* While there are tokens in "s" */
  691. client->ps.forcePowerLevel[i++] = atoi(var);
  692. /* Get next token: */
  693. var = strtok( NULL, " " );
  694. }
  695. assert (i==NUM_FORCE_POWERS);
  696. client->ps.forceGripEntityNum = client->ps.forceDrainEntityNum = ENTITYNUM_NONE;
  697. }
  698. }
  699. }
  700. /*
  701. Ghoul2 Insert Start
  702. */
  703. static void G_SetSkin( gentity_t *ent )
  704. {
  705. char skinName[MAX_QPATH];
  706. //ok, lets register the skin name, and then pass that name to the config strings so the client can get it too.
  707. if (Q_stricmp( "hoth2", level.mapname ) == 0 //hack, is this the only map?
  708. ||
  709. Q_stricmp( "hoth3", level.mapname ) == 0 // no! ;-)
  710. )
  711. {
  712. Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s|%s|%s", g_char_model->string, g_char_skin_head->string, "torso_g1", "lower_e1" );
  713. }
  714. else
  715. {
  716. Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s|%s|%s", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string );
  717. }
  718. // lets see if it's out there
  719. int skin = gi.RE_RegisterSkin( skinName );
  720. if ( skin )
  721. {//what if this returns 0 because *one* part of a multi-skin didn't load?
  722. // put it in the config strings
  723. // and set the ghoul2 model to use it
  724. gi.G2API_SetSkin( &ent->ghoul2[ent->playerModel], G_SkinIndex( skinName ), skin );
  725. }
  726. //color tinting
  727. if ( g_char_color_red->integer
  728. || g_char_color_green->integer
  729. || g_char_color_blue->integer )
  730. {
  731. ent->client->renderInfo.customRGBA[0] = g_char_color_red->integer;
  732. ent->client->renderInfo.customRGBA[1] = g_char_color_green->integer;
  733. ent->client->renderInfo.customRGBA[2] = g_char_color_blue->integer;
  734. ent->client->renderInfo.customRGBA[3] = 255;
  735. }
  736. }
  737. qboolean G_StandardHumanoid( gentity_t *self )
  738. {
  739. if ( !self || !self->ghoul2.size() )
  740. {
  741. return qfalse;
  742. }
  743. if ( self->playerModel < 0 || self->playerModel >= self->ghoul2.size() )
  744. {
  745. return qfalse;
  746. }
  747. const char *GLAName = gi.G2API_GetGLAName( &self->ghoul2[self->playerModel] );
  748. assert(GLAName);
  749. if (GLAName)
  750. {
  751. if ( !Q_stricmpn( "models/players/_humanoid", GLAName, 24 ) )///_humanoid", GLAName, 36) )
  752. {//only _humanoid skeleton is expected to have these
  753. return qtrue;
  754. }
  755. if ( !Q_stricmp( "models/players/protocol/protocol", GLAName ) )
  756. {//protocol droid duplicates many of these
  757. return qtrue;
  758. }
  759. if ( !Q_stricmp( "models/players/assassin_droid/model", GLAName ) )
  760. {//assassin_droid duplicates many of these
  761. return qtrue;
  762. }
  763. if ( !Q_stricmp( "models/players/saber_droid/model", GLAName ) )
  764. {//saber_droid duplicates many of these
  765. return qtrue;
  766. }
  767. if ( !Q_stricmp( "models/players/hazardtrooper/hazardtrooper", GLAName ) )
  768. {//hazardtrooper duplicates many of these
  769. return qtrue;
  770. }
  771. if ( !Q_stricmp( "models/players/rockettrooper/rockettrooper", GLAName ) )
  772. {//rockettrooper duplicates many of these
  773. return qtrue;
  774. }
  775. if ( !Q_stricmp( "models/players/wampa/wampa", GLAName ) )
  776. {//rockettrooper duplicates many of these
  777. return qtrue;
  778. }
  779. }
  780. return qfalse;
  781. }
  782. qboolean G_ClassHasBadBones( int NPC_class )
  783. {
  784. switch ( NPC_class )
  785. {
  786. case CLASS_WAMPA:
  787. case CLASS_ROCKETTROOPER:
  788. case CLASS_SABER_DROID:
  789. case CLASS_HAZARD_TROOPER:
  790. case CLASS_ASSASSIN_DROID:
  791. case CLASS_RANCOR:
  792. return qtrue;
  793. }
  794. return qfalse;
  795. }
  796. char *AxesNames[] =
  797. {
  798. "ORIGIN",//ORIGIN,
  799. "POSITIVE_X",//POSITIVE_X,
  800. "POSITIVE_Z",//POSITIVE_Z,
  801. "POSITIVE_Y",//POSITIVE_Y,
  802. "NEGATIVE_X",//NEGATIVE_X,
  803. "NEGATIVE_Z",//NEGATIVE_Z,
  804. "NEGATIVE_Y"//NEGATIVE_Y
  805. };
  806. Eorientations testAxes[3]={POSITIVE_X,POSITIVE_Z,POSITIVE_Y};
  807. int axes_0 = POSITIVE_X;
  808. int axes_1 = POSITIVE_Z;
  809. int axes_2 = POSITIVE_Y;
  810. void G_NextTestAxes( void )
  811. {
  812. static int whichAxes = 0;
  813. int axesCount = 0;
  814. do
  815. {
  816. whichAxes++;
  817. if ( whichAxes > 216 )
  818. {
  819. whichAxes = 0;
  820. Com_Printf( S_COLOR_RED"WRAPPED\n" );
  821. break;
  822. }
  823. axesCount = 0;
  824. axes_0 = 0;
  825. axes_1 = 0;
  826. axes_2 = 0;
  827. for ( axes_0 = 0; axes_0 < 6 && (axesCount<whichAxes); axes_0++ )
  828. {
  829. axesCount++;
  830. for ( axes_1 = 0; axes_1 < 6 && (axesCount<whichAxes); axes_1++ )
  831. {
  832. axesCount++;
  833. for ( axes_2 = 0; axes_2 < 6 && (axesCount<whichAxes); axes_2++ )
  834. {
  835. axesCount++;
  836. }
  837. }
  838. }
  839. testAxes[0] = (Eorientations)((axes_0%6)+1);
  840. testAxes[1] = (Eorientations)((axes_1%6)+1);
  841. testAxes[2] = (Eorientations)((axes_2%6)+1);
  842. } while ( testAxes[1] == testAxes[0] || (testAxes[1]-testAxes[0]) == 3 || (testAxes[0]-testAxes[1]) == 3
  843. || testAxes[2] == testAxes[0] || (testAxes[2]-testAxes[0]) == 3 || (testAxes[0]-testAxes[2]) == 3
  844. || testAxes[2] == testAxes[1] || (testAxes[2]-testAxes[1]) == 3 || (testAxes[1]-testAxes[2]) == 3 );
  845. Com_Printf( "Up: %s\nRight: %s\nForward: %s\n", AxesNames[testAxes[0]], AxesNames[testAxes[1]], AxesNames[testAxes[2]] );
  846. if ( testAxes[0] == POSITIVE_X
  847. && testAxes[1] == POSITIVE_Z
  848. && testAxes[2] == POSITIVE_Y )
  849. {
  850. Com_Printf( S_COLOR_RED"WRAPPED\n" );
  851. }
  852. }
  853. void G_BoneOrientationsForClass( int NPC_class, char *boneName, Eorientations *oUp, Eorientations *oRt, Eorientations *oFwd )
  854. {
  855. //defaults
  856. *oUp = POSITIVE_X;
  857. *oRt = NEGATIVE_Y;
  858. *oFwd = NEGATIVE_Z;
  859. //switch off class
  860. switch ( NPC_class )
  861. {
  862. case CLASS_RANCOR:
  863. *oUp = NEGATIVE_X;
  864. *oRt = POSITIVE_Y;
  865. *oFwd = POSITIVE_Z;
  866. //*oUp = testAxes[0];
  867. //*oRt = testAxes[1];
  868. //*oFwd = testAxes[2];
  869. break;
  870. case CLASS_ROCKETTROOPER:
  871. case CLASS_HAZARD_TROOPER:
  872. //Root is:
  873. //*oUp = POSITIVE_Z;
  874. //*oRt = NEGATIVE_X;
  875. //*oFwd = NEGATIVE_Y;
  876. if ( Q_stricmp( "pelvis", boneName ) == 0 )
  877. {//child of root
  878. //in ModView:
  879. //*oUp = NEGATIVE_X;
  880. //*oRt = NEGATIVE_Z;
  881. //*oFwd = NEGATIVE_Y;
  882. //actual, when differences with root are accounted for:
  883. *oUp = POSITIVE_Z;
  884. *oRt = NEGATIVE_X;
  885. *oFwd = NEGATIVE_Y;
  886. }
  887. else
  888. {//all the rest are the same, children of root (not pelvis)
  889. //in ModView:
  890. //*oUp = POSITIVE_X;
  891. //*oRt = POSITIVE_Y;
  892. //*oFwd = POSITIVE_Z;
  893. //actual, when differences with root are accounted for:
  894. //*oUp = POSITIVE_Z;
  895. //*oRt = NEGATIVE_Y;
  896. //*oFwd = NEGATIVE_X;
  897. *oUp = NEGATIVE_X;
  898. *oRt = POSITIVE_Y;
  899. *oFwd = POSITIVE_Z;
  900. }
  901. break;
  902. case CLASS_SABER_DROID:
  903. if ( Q_stricmp( "pelvis", boneName ) == 0
  904. || Q_stricmp( "thoracic", boneName ) == 0 )
  905. {
  906. *oUp = NEGATIVE_X;
  907. *oRt = NEGATIVE_Z;
  908. *oFwd = NEGATIVE_Y;
  909. }
  910. else
  911. {
  912. *oUp = NEGATIVE_X;//POSITIVE_X;
  913. *oRt = POSITIVE_Y;
  914. *oFwd = POSITIVE_Z;
  915. }
  916. break;
  917. case CLASS_WAMPA:
  918. if ( Q_stricmp( "pelvis", boneName ) == 0 )
  919. {
  920. *oUp = NEGATIVE_X;
  921. *oRt = POSITIVE_Y;
  922. *oFwd = NEGATIVE_Z;
  923. }
  924. else
  925. {
  926. //*oUp = POSITIVE_X;
  927. //*oRt = POSITIVE_Y;
  928. //*oFwd = POSITIVE_Z;
  929. //kinda worked
  930. *oUp = NEGATIVE_X;
  931. *oRt = POSITIVE_Y;
  932. *oFwd = POSITIVE_Z;
  933. }
  934. break;
  935. case CLASS_ASSASSIN_DROID:
  936. if ( Q_stricmp( "pelvis", boneName ) == 0
  937. || Q_stricmp( "lower_lumbar", boneName ) == 0
  938. || Q_stricmp( "upper_lumbar", boneName ) == 0 )
  939. {//only these 3 bones on them are wrong
  940. //*oUp = POSITIVE_X;
  941. //*oRt = POSITIVE_Y;
  942. //*oFwd = POSITIVE_Z;
  943. *oUp = NEGATIVE_X;
  944. *oRt = POSITIVE_Y;
  945. *oFwd = POSITIVE_Z;
  946. }
  947. break;
  948. }
  949. }
  950. extern void G_LoadAnimFileSet( gentity_t *ent, const char *modelName );
  951. qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn )
  952. {
  953. if ( ent->playerModel != -1 )
  954. {// we found the model ok
  955. vec3_t angles = {0,0,0};
  956. const char *token;
  957. const char *p;
  958. //Now turn on/off any surfaces
  959. if ( surfOff && surfOff[0] )
  960. {
  961. p = surfOff;
  962. while ( 1 )
  963. {
  964. token = COM_ParseExt( &p, qtrue );
  965. if ( !token[0] )
  966. {//reached end of list
  967. break;
  968. }
  969. //turn off this surf
  970. gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], token, 0x00000002/*G2SURFACEFLAG_OFF*/ );
  971. }
  972. }
  973. if ( surfOn && surfOn[0] )
  974. {
  975. p = surfOn;
  976. while ( 1 )
  977. {
  978. token = COM_ParseExt( &p, qtrue );
  979. if ( !token[0] )
  980. {//reached end of list
  981. break;
  982. }
  983. //turn on this surf
  984. gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], token, 0 );
  985. }
  986. }
  987. if ( ent->client->NPC_class == CLASS_IMPERIAL && ent->message )
  988. {//carrying a key, turn on the key sleeve surface (assuming we have one)
  989. gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "l_arm_key", 0 );
  990. }
  991. G_LoadAnimFileSet( ent, modelName );
  992. ent->headBolt = ent->cervicalBolt = ent->torsoBolt = ent->gutBolt = ent->chestBolt =
  993. ent->crotchBolt = ent->elbowLBolt = ent->elbowRBolt = ent->handLBolt =
  994. ent->handRBolt = ent->kneeLBolt = ent->kneeRBolt = ent->footLBolt =
  995. ent->footRBolt = -1;
  996. // now turn on the bolt in the hand - this one would be best always turned on.
  997. if ( G_StandardHumanoid( ent ) )
  998. {//only _humanoid skeleton is expected to have these
  999. ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
  1000. ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" );
  1001. if ( !Q_stricmp("protocol", modelName ) )
  1002. {//*sigh*, no thoracic bone
  1003. ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
  1004. ent->chestBolt = ent->gutBolt;
  1005. }
  1006. else
  1007. {
  1008. ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic");
  1009. ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
  1010. }
  1011. ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar");
  1012. ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis");
  1013. ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_arm_elbow");
  1014. ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_arm_elbow");
  1015. ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
  1016. ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
  1017. ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_l_knee");
  1018. ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_r_knee");
  1019. ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot");
  1020. ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot");
  1021. if ( ent->client->NPC_class == CLASS_BOBAFETT
  1022. || ent->client->NPC_class == CLASS_ROCKETTROOPER )
  1023. {//get jet bolts
  1024. ent->genericBolt1 = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*jet1" );
  1025. ent->genericBolt2 = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*jet2" );
  1026. }
  1027. if ( ent->client->NPC_class == CLASS_BOBAFETT )
  1028. {//get the flamethrower bolt
  1029. ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flamethrower");
  1030. }
  1031. }
  1032. else
  1033. {
  1034. if ( ent->client->NPC_class == CLASS_VEHICLE )
  1035. {//do vehicles tags
  1036. // Setup the driver tag (where the driver is mounted to).
  1037. ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*driver");
  1038. // Setup the droid unit (or other misc tag we're using this for).
  1039. ent->m_pVehicle->m_iDroidUnitTag = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*droidunit");
  1040. char strTemp[128];
  1041. // Setup the Exhausts.
  1042. for ( int i = 0; i < MAX_VEHICLE_EXHAUSTS; i++ )
  1043. {
  1044. _snprintf( strTemp, 128, "*exhaust%d", i + 1 );
  1045. ent->m_pVehicle->m_iExhaustTag[i] = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], strTemp );
  1046. }
  1047. // Setup the Muzzles.
  1048. for ( int i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
  1049. {
  1050. _snprintf( strTemp, 128, "*muzzle%d", i + 1 );
  1051. ent->m_pVehicle->m_iMuzzleTag[i] = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], strTemp );
  1052. }
  1053. }
  1054. else if ( ent->client->NPC_class == CLASS_HOWLER )
  1055. {
  1056. ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Tongue01" );// tongue base
  1057. ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Tongue08" );// tongue tip
  1058. }
  1059. else if ( !Q_stricmp( "gonk", modelName ) || !Q_stricmp( "seeker", modelName ) || !Q_stricmp( "remote", modelName )
  1060. || !Q_stricmpn( "r2d2", modelName, 4 ) || !Q_stricmpn( "r5d2", modelName, 4 ) )
  1061. {//TEMP HACK: not a non-humanoid droid
  1062. ent->headBolt = -1;
  1063. }
  1064. else if (!Q_stricmp( "interrogator",modelName))
  1065. {
  1066. ent->headBolt = -1;
  1067. }
  1068. else if (!Q_stricmpn( "probe",modelName, 5))
  1069. {
  1070. ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cranium"); // head pivot point
  1071. ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash"); // Gun 1
  1072. }
  1073. else if (!Q_stricmp( "sentry",modelName))
  1074. {
  1075. ent->headBolt = -1;
  1076. ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Gun 1
  1077. ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2"); // Gun 2
  1078. ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash03"); // Gun 3
  1079. }
  1080. else if (!Q_stricmp( "mark1",modelName))
  1081. {
  1082. ent->headBolt = -1;
  1083. ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Blaster Gun 1
  1084. ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2"); // Blaster Gun 2
  1085. ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3"); // Blaster Gun 3
  1086. ent->genericBolt4 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4"); // Blaster Gun 4
  1087. ent->genericBolt5 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash5"); // Missile Gun 1
  1088. }
  1089. else if (!Q_stricmp( "mark2",modelName))
  1090. {
  1091. ent->headBolt = -1;
  1092. ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash"); // Blaster Gun 1
  1093. }
  1094. else if (!Q_stricmp( "atst",modelName) )//&& (ent->client->playerTeam != TEAM_PLAYER))
  1095. {
  1096. ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head");
  1097. ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Front guns
  1098. ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2");
  1099. ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3"); // Left side gun
  1100. ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4"); // Right side missle launcher
  1101. ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_foot");
  1102. ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_foot");
  1103. }
  1104. else if ( !Q_stricmp( "minemonster", modelName ))
  1105. {
  1106. ent->handRBolt = ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_f1");
  1107. }
  1108. else if ( !Q_stricmp( "rancor", modelName )
  1109. || !Q_stricmp( "mutant_rancor", modelName ))
  1110. {
  1111. ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
  1112. ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
  1113. ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
  1114. ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_mouth");
  1115. }
  1116. else if ( !Q_stricmp( "sand_creature", modelName ))
  1117. {
  1118. ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*mouth");
  1119. ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*ground");
  1120. }
  1121. else if ( !Q_stricmp( "wampa", modelName ))
  1122. {
  1123. ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
  1124. ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "neck_bone" );
  1125. ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_spine");
  1126. ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "mid_spine");
  1127. ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_spine");
  1128. ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "rear_bone");
  1129. ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_arm_elbow");
  1130. ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_arm_elbow");
  1131. ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
  1132. ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
  1133. ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_l_knee");
  1134. ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_r_knee");
  1135. ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot");
  1136. ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot");
  1137. }
  1138. else
  1139. {//TEMP HACK: not a non-humanoid droid
  1140. ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*weapon");//should be r_hand
  1141. if ( Q_stricmp( "atst", modelName ) )
  1142. {//not an ATST
  1143. ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*headg");
  1144. ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" );
  1145. ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar");
  1146. ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
  1147. ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic");
  1148. ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis");
  1149. ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*bicep_lg");
  1150. ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*bicep_rg");
  1151. ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hand_l");
  1152. ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*thigh_lg");
  1153. ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*thigh_rg");
  1154. ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*foot_lg");
  1155. ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*foot_rg");
  1156. }
  1157. }
  1158. }
  1159. ent->faceBone = BONE_INDEX_INVALID;
  1160. ent->craniumBone = BONE_INDEX_INVALID;
  1161. ent->cervicalBone = BONE_INDEX_INVALID;
  1162. ent->thoracicBone = BONE_INDEX_INVALID;
  1163. ent->upperLumbarBone = BONE_INDEX_INVALID;
  1164. ent->lowerLumbarBone = BONE_INDEX_INVALID;
  1165. ent->motionBone = BONE_INDEX_INVALID;
  1166. ent->hipsBone = BONE_INDEX_INVALID;
  1167. ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
  1168. #ifndef FINAL_BUILD
  1169. if ( g_developer->integer && ent->rootBone == -1 )
  1170. {
  1171. Com_Error(ERR_DROP,"ERROR: model %s has no model_root bone (and hence cannot animate)!!!\n", modelName );
  1172. }
  1173. #endif
  1174. ent->footLBone = BONE_INDEX_INVALID;
  1175. ent->footRBone = BONE_INDEX_INVALID;
  1176. ent->humerusRBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "rhumerus", qtrue );
  1177. // now add overrides on specific joints so the client can set angle overrides on the legs, torso and head
  1178. if ( ent->client->NPC_class == CLASS_VEHICLE )
  1179. {//do vehicles tags
  1180. //vehicleInfo_t *vehicle = ent->m_pVehicle->m_pVehicleInfo;
  1181. }
  1182. else if ( ent->client->NPC_class == CLASS_HOWLER )
  1183. {
  1184. }
  1185. else if ( !Q_stricmp( "gonk", modelName ) || !Q_stricmp( "seeker", modelName ) || !Q_stricmp( "remote", modelName ) )
  1186. {//
  1187. }
  1188. else if (!Q_stricmp( "sentry",modelName))
  1189. {
  1190. }
  1191. else if (!Q_stricmpn( "probe", modelName, 5 ))
  1192. {
  1193. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1194. if (ent->craniumBone>=0)
  1195. {
  1196. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1197. }
  1198. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
  1199. if (ent->thoracicBone>=0)
  1200. {
  1201. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1202. }
  1203. }
  1204. else if (!Q_stricmp( "interrogator", modelName ))
  1205. {
  1206. ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "left_arm", qtrue );
  1207. if (ent->genericBone1>=0)
  1208. {
  1209. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL );
  1210. }
  1211. ent->genericBone2 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "right_arm", qtrue );
  1212. if (ent->genericBone2>=0)
  1213. {
  1214. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone2, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL );
  1215. }
  1216. ent->genericBone3 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "claw", qtrue );
  1217. if (ent->genericBone3>=0)
  1218. {
  1219. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone3, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL );
  1220. }
  1221. }
  1222. else if (!Q_stricmpn( "r2d2", modelName, 4 ))
  1223. {
  1224. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1225. if (ent->craniumBone>=0)
  1226. {
  1227. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1228. }
  1229. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue );
  1230. if (ent->thoracicBone>=0)
  1231. {
  1232. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1233. }
  1234. ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "f_eye", qtrue );
  1235. if (ent->genericBone1>=0)
  1236. {
  1237. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL );
  1238. }
  1239. }
  1240. else if (!Q_stricmpn( "r5d2", modelName, 4 ))
  1241. {
  1242. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1243. if (ent->craniumBone>=0)
  1244. {
  1245. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1246. }
  1247. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue );
  1248. if (ent->thoracicBone>=0)
  1249. {
  1250. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1251. }
  1252. }
  1253. else if ( !Q_stricmp( "atst", modelName ))
  1254. {
  1255. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1256. if (ent->craniumBone>=0)
  1257. {
  1258. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1259. }
  1260. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
  1261. if (ent->thoracicBone>=0)
  1262. {
  1263. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1264. }
  1265. ent->footLBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "l_tarsal", qtrue );
  1266. if (ent->footLBone>=0)
  1267. {
  1268. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footLBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL );
  1269. }
  1270. ent->footRBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "r_tarsal", qtrue );
  1271. if (ent->footRBone>=0)
  1272. {
  1273. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footRBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL );
  1274. }
  1275. }
  1276. else if ( !Q_stricmp( "mark1", modelName ))
  1277. {
  1278. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1279. if (ent->craniumBone>=0)
  1280. {
  1281. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1282. }
  1283. ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1284. if (ent->upperLumbarBone>=0)
  1285. {
  1286. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1287. }
  1288. }
  1289. else if ( !Q_stricmp( "mark2", modelName ))
  1290. {
  1291. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1292. if (ent->craniumBone>=0)
  1293. {
  1294. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1295. }
  1296. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
  1297. if (ent->thoracicBone>=0)
  1298. {
  1299. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1300. }
  1301. }
  1302. else if ( !Q_stricmp( "minemonster", modelName ))
  1303. {
  1304. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic1", qtrue );
  1305. if (ent->thoracicBone>=0)
  1306. {
  1307. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1308. }
  1309. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1310. if (ent->craniumBone>=0)
  1311. {
  1312. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1313. }
  1314. }
  1315. else if ( ent->client->NPC_class == CLASS_RANCOR )
  1316. /*!Q_stricmp( "rancor", modelName ) || !Q_stricmp( "mutant_rancor", modelName ) )*/
  1317. {
  1318. Eorientations oUp, oRt, oFwd;
  1319. //regular bones we need
  1320. ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_spine", qtrue );
  1321. if (ent->lowerLumbarBone>=0)
  1322. {
  1323. G_BoneOrientationsForClass( ent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
  1324. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1325. }
  1326. ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "mid_spine", qtrue );
  1327. if (ent->upperLumbarBone>=0)
  1328. {
  1329. G_BoneOrientationsForClass( ent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
  1330. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1331. }
  1332. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_spine", qtrue );
  1333. if (ent->thoracicBone>=0)
  1334. {
  1335. G_BoneOrientationsForClass( ent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
  1336. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1337. }
  1338. }
  1339. else if ( !Q_stricmp( "sand_creature", modelName ))
  1340. {
  1341. }
  1342. else if ( !Q_stricmp( "wampa", modelName ) )
  1343. {
  1344. //Eorientations oUp, oRt, oFwd;
  1345. //bone needed for turning anims
  1346. /*
  1347. //SIGH... fucks him up BAD
  1348. ent->hipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
  1349. if (ent->hipsBone>=0)
  1350. {
  1351. G_BoneOrientationsForClass( ent->client->NPC_class, "pelvis", &oUp, &oRt, &oFwd );
  1352. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->hipsBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1353. }
  1354. */
  1355. /*
  1356. //SIGH... no anim split
  1357. ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
  1358. if (ent->lowerLumbarBone>=0)
  1359. {
  1360. G_BoneOrientationsForClass( ent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
  1361. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1362. }
  1363. */
  1364. /*
  1365. //SIGH... spine wiggles fuck all this shit
  1366. ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
  1367. if (ent->upperLumbarBone>=0)
  1368. {
  1369. G_BoneOrientationsForClass( ent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
  1370. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1371. }
  1372. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
  1373. if (ent->thoracicBone>=0)
  1374. {
  1375. G_BoneOrientationsForClass( ent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
  1376. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1377. }
  1378. ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
  1379. if (ent->cervicalBone>=0)
  1380. {
  1381. G_BoneOrientationsForClass( ent->client->NPC_class, "cervical", &oUp, &oRt, &oFwd );
  1382. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1383. }
  1384. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1385. if (ent->craniumBone>=0)
  1386. {
  1387. G_BoneOrientationsForClass( ent->client->NPC_class, "cranium", &oUp, &oRt, &oFwd );
  1388. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1389. }
  1390. */
  1391. }
  1392. else if ( !Q_stricmp( "rockettrooper", modelName )
  1393. || !Q_stricmp( "hazardtrooper", modelName )
  1394. || !Q_stricmp( "saber_droid", modelName )
  1395. || !Q_stricmp( "assassin_droid", modelName ) )
  1396. {
  1397. Eorientations oUp, oRt, oFwd;
  1398. if ( Q_stricmp( "saber_droid", modelName ) )
  1399. {//saber droid doesn't use these lower bones
  1400. //regular bones we need
  1401. ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
  1402. if (ent->upperLumbarBone>=0)
  1403. {
  1404. G_BoneOrientationsForClass( ent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
  1405. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1406. }
  1407. ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
  1408. if (ent->lowerLumbarBone>=0)
  1409. {
  1410. G_BoneOrientationsForClass( ent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
  1411. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1412. }
  1413. }
  1414. if ( Q_stricmp( "hazardtrooper", modelName ) )
  1415. {//hazard trooper doesn't have these upper bones
  1416. if ( Q_stricmp( "saber_droid", modelName ) )
  1417. {//saber droid doesn't use thoracic bone
  1418. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
  1419. if (ent->thoracicBone>=0)
  1420. {
  1421. G_BoneOrientationsForClass( ent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
  1422. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1423. }
  1424. }
  1425. ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
  1426. if (ent->cervicalBone>=0)
  1427. {
  1428. G_BoneOrientationsForClass( ent->client->NPC_class, "cervical", &oUp, &oRt, &oFwd );
  1429. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1430. }
  1431. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1432. if (ent->craniumBone>=0)
  1433. {
  1434. G_BoneOrientationsForClass( ent->client->NPC_class, "cranium", &oUp, &oRt, &oFwd );
  1435. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
  1436. }
  1437. }
  1438. }
  1439. else
  1440. {
  1441. //special case motion bone - to match up split anims
  1442. ent->motionBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "Motion", qtrue );
  1443. if (ent->motionBone>=0)
  1444. {
  1445. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->motionBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL );
  1446. }
  1447. ent->motionBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Motion");
  1448. //bone needed for turning anims
  1449. ent->hipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
  1450. if (ent->hipsBone>=0)
  1451. {
  1452. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->hipsBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1453. }
  1454. //regular bones we need
  1455. ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
  1456. if (ent->upperLumbarBone>=0)
  1457. {
  1458. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1459. }
  1460. ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
  1461. if (ent->lowerLumbarBone>=0)
  1462. {
  1463. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1464. }
  1465. ent->faceBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "face", qtrue );
  1466. if (ent->faceBone>=0)
  1467. {
  1468. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->faceBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1469. }
  1470. ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
  1471. if (ent->craniumBone>=0)
  1472. {
  1473. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1474. }
  1475. ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
  1476. if (ent->cervicalBone>=0)
  1477. {
  1478. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1479. }
  1480. ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
  1481. if (ent->thoracicBone>=0)
  1482. {
  1483. gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
  1484. }
  1485. }
  1486. ent->client->clientInfo.infoValid = qtrue;
  1487. }
  1488. if ( ent->client->NPC_class == CLASS_SAND_CREATURE )
  1489. {
  1490. ent->s.radius = 256;
  1491. }
  1492. else if ( ent->client->NPC_class == CLASS_RANCOR )
  1493. {
  1494. if ( (ent->spawnflags&1) )
  1495. {//mutant
  1496. ent->s.radius = 300;
  1497. }
  1498. else
  1499. {
  1500. ent->s.radius = 150;
  1501. }
  1502. }
  1503. else if ( ent->s.radius <= 0 )//radius cannot be negative or zero
  1504. {//set the radius to be the largest axial distance on the entity
  1505. float max;
  1506. max = ent->mins[0];//NOTE: mins is always negative
  1507. if ( max > ent->mins[1] )
  1508. {
  1509. max = ent->mins[1];
  1510. }
  1511. if ( max > ent->mins[2] )
  1512. {
  1513. max = ent->mins[2];
  1514. }
  1515. max = fabs(max);//convert to positive to compare with maxs
  1516. if ( max < ent->maxs[0] )
  1517. {
  1518. max = ent->maxs[0];
  1519. }
  1520. if ( max < ent->maxs[1] )
  1521. {
  1522. max = ent->maxs[1];
  1523. }
  1524. if ( max < ent->maxs[2] )
  1525. {
  1526. max = ent->maxs[2];
  1527. }
  1528. ent->s.radius = (int)max;
  1529. if (!ent->s.radius) // Still no radius?
  1530. {
  1531. ent->s.radius = 60;
  1532. }
  1533. }
  1534. // set the weaponmodel to -1 so we don't try to remove it in Pmove before we have it built
  1535. ent->weaponModel[0] = -1;
  1536. if ( ent->playerModel == -1 )
  1537. {
  1538. return qfalse;
  1539. }
  1540. return qtrue;
  1541. }
  1542. void G_SetG2PlayerModel( gentity_t * const ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn )
  1543. {
  1544. char skinName[MAX_QPATH];
  1545. //ok, lets register the skin name, and then pass that name to the config strings so the client can get it too.
  1546. if ( !customSkin )
  1547. {//use the default
  1548. Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", modelName );
  1549. }
  1550. else
  1551. {
  1552. if (strchr(customSkin, '|'))
  1553. {//three part skin
  1554. Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s", modelName, customSkin );
  1555. }
  1556. else
  1557. {
  1558. Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", modelName, customSkin );
  1559. }
  1560. }
  1561. int skin = gi.RE_RegisterSkin( skinName );
  1562. assert(skin);
  1563. //now generate the ghoul2 model this client should be.
  1564. if ( ent->client->NPC_class == CLASS_VEHICLE )
  1565. {//vehicles actually grab their model from the appropriate vehicle data entry
  1566. // This will register the model and other assets.
  1567. Vehicle_t *pVeh = ent->m_pVehicle;
  1568. pVeh->m_pVehicleInfo->RegisterAssets( pVeh );
  1569. ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName), pVeh->m_pVehicleInfo->modelIndex, G_SkinIndex( skinName ) );
  1570. }
  1571. else
  1572. {
  1573. //NOTE: it still loads the default skin's tga's because they're referenced in the .glm.
  1574. ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName), G_ModelIndex( va("models/players/%s/model.glm", modelName) ), G_SkinIndex( skinName ) );
  1575. }
  1576. if (ent->playerModel == -1)
  1577. {//try the stormtrooper as a default
  1578. gi.Printf( S_COLOR_RED"G_SetG2PlayerModel: cannot load model %s\n", modelName );
  1579. modelName = "stormtrooper";
  1580. Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", modelName );
  1581. skin = gi.RE_RegisterSkin( skinName );
  1582. assert(skin);
  1583. ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName), G_ModelIndex( va("models/players/%s/model.glm", modelName) ) );
  1584. }
  1585. if (ent->playerModel == -1)
  1586. {//very bad thing here!
  1587. Com_Error(ERR_DROP, "Cannot fall back to default model %s!", modelName);
  1588. }
  1589. gi.G2API_SetSkin( &ent->ghoul2[ent->playerModel], G_SkinIndex( skinName ), skin );//this is going to set the surfs on/off matching the skin file
  1590. // did we find a ghoul2 model? if so, load the animation.cfg file
  1591. if ( !G_SetG2PlayerModelInfo( ent, modelName, customSkin, surfOff, surfOn ) )
  1592. {//couldn't set g2 info, fall back to a mouse md3
  1593. NPC_ParseParms( "mouse", ent );
  1594. Com_Printf( S_COLOR_RED"couldn't load playerModel %s!\n", va("models/players/%s/model.glm", modelName) );
  1595. }
  1596. }
  1597. /*
  1598. Ghoul2 Insert End
  1599. */
  1600. void G_RemovePlayerModel( gentity_t *ent )
  1601. {
  1602. if ( ent->playerModel >= 0 && ent->ghoul2.size() )
  1603. {
  1604. gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->playerModel );
  1605. ent->playerModel = -1;
  1606. }
  1607. }
  1608. void G_RemoveWeaponModels( gentity_t *ent )
  1609. {
  1610. if ( ent->ghoul2.size() )
  1611. {
  1612. if ( ent->weaponModel[0] > 0 )
  1613. {
  1614. gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel[0] );
  1615. ent->weaponModel[0] = -1;
  1616. }
  1617. if ( ent->weaponModel[1] > 0 )
  1618. {
  1619. gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel[1] );
  1620. ent->weaponModel[1] = -1;
  1621. }
  1622. }
  1623. }
  1624. void G_AddWeaponModels( gentity_t *ent )
  1625. {
  1626. if ( !ent || !ent->client )
  1627. {
  1628. return;
  1629. }
  1630. if ( ent->weaponModel[0] == -1 )
  1631. {
  1632. if ( ent->client->ps.weapon == WP_SABER )
  1633. {
  1634. WP_SaberAddG2SaberModels( ent );
  1635. }
  1636. else if ( ent->client->ps.weapon != WP_NONE )
  1637. {
  1638. G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
  1639. }
  1640. }
  1641. }
  1642. extern saber_colors_t TranslateSaberColor( const char *name );
  1643. extern void WP_RemoveSaber( gentity_t *ent, int saberNum );
  1644. void G_ChangePlayerModel( gentity_t *ent, const char *newModel );
  1645. void G_SetSabersFromCVars( gentity_t *ent )
  1646. {
  1647. if ( g_saber->string
  1648. && g_saber->string[0]
  1649. && Q_stricmp( "none", g_saber->string )
  1650. && Q_stricmp( "NULL", g_saber->string ) )
  1651. {//FIXME: how to specify second saber?
  1652. WP_SaberParseParms( g_saber->string, &ent->client->ps.saber[0] );
  1653. if ( ent->client->ps.saber[0].style )
  1654. {
  1655. ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saber[0].style);
  1656. }
  1657. }
  1658. if ( player
  1659. && player->client
  1660. && player->client->sess.mission_objectives[LIGHTSIDE_OBJ].status == 2
  1661. && g_saberDarkSideSaberColor->integer )
  1662. {//dark side!
  1663. //always use red
  1664. for ( int n = 0; n < MAX_BLADES; n++ )
  1665. {
  1666. ent->client->ps.saber[0].blade[n].color = SABER_RED;
  1667. }
  1668. }
  1669. else if ( g_saber_color->string )
  1670. {//FIXME: how to specify color for each blade and/or color for second saber?
  1671. saber_colors_t color = TranslateSaberColor( g_saber_color->string );
  1672. for ( int n = 0; n < MAX_BLADES; n++ )
  1673. {
  1674. ent->client->ps.saber[0].blade[n].color = color;
  1675. }
  1676. }
  1677. if ( g_saber2->string
  1678. && g_saber2->string[0]
  1679. && Q_stricmp( "none", g_saber2->string )
  1680. && Q_stricmp( "NULL", g_saber2->string ) )
  1681. {
  1682. if ( !ent->client->ps.saber[0].twoHanded )
  1683. {//can't use a second saber if first one is a two-handed saber...?
  1684. WP_SaberParseParms( g_saber2->string, &ent->client->ps.saber[1] );
  1685. if ( ent->client->ps.saber[1].style )
  1686. {
  1687. ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saber[1].style);
  1688. }
  1689. if ( ent->client->ps.saber[1].twoHanded )
  1690. {//tsk tsk, can't use a twoHanded saber as second saber
  1691. WP_RemoveSaber( ent, 1 );
  1692. }
  1693. else
  1694. {
  1695. ent->client->ps.dualSabers = qtrue;
  1696. if ( player
  1697. && player->client
  1698. && player->client->sess.mission_objectives[LIGHTSIDE_OBJ].status == 2
  1699. && g_saberDarkSideSaberColor->integer )
  1700. {//dark side!
  1701. //always use red
  1702. for ( int n = 0; n < MAX_BLADES; n++ )
  1703. {
  1704. ent->client->ps.saber[1].blade[n].color = SABER_RED;
  1705. }
  1706. }
  1707. else if ( g_saber2_color->string )
  1708. {//FIXME: how to specify color for each blade and/or color for second saber?
  1709. saber_colors_t color = TranslateSaberColor( g_saber2_color->string );
  1710. for ( int n = 0; n < MAX_BLADES; n++ )
  1711. {
  1712. ent->client->ps.saber[1].blade[n].color = color;
  1713. }
  1714. }
  1715. }
  1716. }
  1717. }
  1718. }
  1719. void G_InitPlayerFromCvars( gentity_t *ent )
  1720. {
  1721. //set model based on cvars
  1722. G_ChangePlayerModel( ent, va("%s|%s|%s|%s", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string) );
  1723. //FIXME: parse these 2 from some cvar or require playermodel to be in a *.npc?
  1724. if( ent->NPC_type && gi.bIsFromZone(ent->NPC_type, TAG_G_ALLOC) ) {
  1725. gi.Free(ent->NPC_type);
  1726. }
  1727. ent->NPC_type = "player";//default for now
  1728. if( ent->client->clientInfo.customBasicSoundDir && gi.bIsFromZone(ent->client->clientInfo.customBasicSoundDir, TAG_G_ALLOC) ) {
  1729. gi.Free(ent->client->clientInfo.customBasicSoundDir);
  1730. }
  1731. char snd[512];
  1732. gi.Cvar_VariableStringBuffer( "snd", snd, sizeof(snd) );
  1733. ent->client->clientInfo.customBasicSoundDir = G_NewString(snd); //copy current cvar
  1734. //set the lightsaber
  1735. G_RemoveWeaponModels( ent );
  1736. G_SetSabersFromCVars( ent );
  1737. //set up weapon models, etc.
  1738. G_AddWeaponModels( ent );
  1739. NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
  1740. NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
  1741. if ( !ent->s.number )
  1742. {//the actual player, not an NPC pretending to be a player
  1743. ClientUserinfoChanged( ent->s.number );
  1744. }
  1745. //color tinting
  1746. //FIXME: the customRGBA shouldn't be set if the shader this guys .skin is using doesn't have the tinting on it
  1747. if ( g_char_color_red->integer
  1748. || g_char_color_green->integer
  1749. || g_char_color_blue->integer )
  1750. {
  1751. ent->client->renderInfo.customRGBA[0] = g_char_color_red->integer;
  1752. ent->client->renderInfo.customRGBA[1] = g_char_color_green->integer;
  1753. ent->client->renderInfo.customRGBA[2] = g_char_color_blue->integer;
  1754. ent->client->renderInfo.customRGBA[3] = 255;
  1755. }
  1756. }
  1757. void G_ChangePlayerModel( gentity_t *ent, const char *newModel )
  1758. {
  1759. if ( !ent || !ent->client || !newModel )
  1760. {
  1761. return;
  1762. }
  1763. G_RemovePlayerModel( ent );
  1764. if ( Q_stricmp( "player", newModel ) == 0 )
  1765. {
  1766. G_InitPlayerFromCvars( ent );
  1767. return;
  1768. }
  1769. //attempt to free the string (currently can't since it's always "player" )
  1770. if( ent->NPC_type && gi.bIsFromZone(ent->NPC_type, TAG_G_ALLOC) ) {
  1771. gi.Free(ent->NPC_type);
  1772. }
  1773. ent->NPC_type = G_NewString( newModel );
  1774. G_RemoveWeaponModels( ent );
  1775. if ( strchr(newModel,'|') )
  1776. {
  1777. char name[MAX_QPATH];
  1778. strcpy(name, newModel);
  1779. char *p = strchr(name, '|');
  1780. *p=0;
  1781. p++;
  1782. G_SetG2PlayerModel( ent, name, p, NULL, NULL );
  1783. }
  1784. else
  1785. {
  1786. //FIXME: everything but force powers gets reset, those should, too...
  1787. // currently leaves them as is except where otherwise noted in the NPCs.cfg?
  1788. //FIXME: remove all weapons?
  1789. if ( NPC_ParseParms( ent->NPC_type, ent ) )
  1790. {
  1791. G_AddWeaponModels( ent );
  1792. NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
  1793. NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
  1794. ClientUserinfoChanged( ent->s.number );
  1795. //Ugh, kind of a hack for now:
  1796. if ( ent->client->NPC_class == CLASS_BOBAFETT
  1797. || ent->client->NPC_class == CLASS_ROCKETTROOPER )
  1798. {
  1799. //FIXME: remove saber, too?
  1800. Boba_Precache(); // player as boba?
  1801. }
  1802. }
  1803. else
  1804. {
  1805. gi.Printf( S_COLOR_RED"G_ChangePlayerModel: cannot find NPC %s\n", newModel );
  1806. G_ChangePlayerModel( ent, "stormtrooper" ); //need a better fallback?
  1807. }
  1808. }
  1809. }
  1810. void G_ReloadSaberData( gentity_t *ent )
  1811. {
  1812. //dualSabers should already be set
  1813. if ( ent->client->ps.saber[0].name != NULL )
  1814. {
  1815. WP_SaberParseParms( ent->client->ps.saber[0].name, &ent->client->ps.saber[0], qfalse );
  1816. if ( ent->client->ps.saber[0].style )
  1817. {
  1818. ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saber[0].style);
  1819. }
  1820. }
  1821. if ( ent->client->ps.saber[1].name != NULL )
  1822. {
  1823. WP_SaberParseParms( ent->client->ps.saber[1].name, &ent->client->ps.saber[1], qfalse );
  1824. if ( ent->client->ps.saber[1].style )
  1825. {
  1826. ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saber[1].style);
  1827. }
  1828. }
  1829. }
  1830. qboolean G_PlayerSpawned( void )
  1831. {
  1832. if ( !player
  1833. || !player->client
  1834. || player->client->pers.teamState.state != TEAM_ACTIVE
  1835. || level.time - player->client->pers.enterTime < 100 )
  1836. {//player hasn't spawned yet
  1837. return qfalse;
  1838. }
  1839. return qtrue;
  1840. }
  1841. /*
  1842. ===========
  1843. ClientSpawn
  1844. Called every time a client is placed fresh in the world:
  1845. after the first ClientBegin, and after each respawn
  1846. Initializes all non-persistant parts of playerState
  1847. ============
  1848. */
  1849. qboolean G_CheckPlayerDarkSide( void )
  1850. {
  1851. if ( player && player->client && player->client->sess.mission_objectives[LIGHTSIDE_OBJ].status == 2 )
  1852. {//dark side player!
  1853. player->client->playerTeam = TEAM_FREE;
  1854. player->client->enemyTeam = TEAM_FREE;
  1855. if ( g_saberDarkSideSaberColor->integer )
  1856. {//dark side!
  1857. //always use red
  1858. for ( int n = 0; n < MAX_BLADES; n++ )
  1859. {
  1860. player->client->ps.saber[0].blade[n].color = player->client->ps.saber[1].blade[n].color = SABER_RED;
  1861. }
  1862. }
  1863. G_SoundIndex( "sound/chars/jedi2/28je2008.wav" );
  1864. G_SoundIndex( "sound/chars/jedi2/28je2009.wav" );
  1865. G_SoundIndex( "sound/chars/jedi2/28je2012.wav" );
  1866. return qtrue;
  1867. }
  1868. return qfalse;
  1869. }
  1870. qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded )
  1871. {
  1872. int index;
  1873. vec3_t spawn_origin, spawn_angles;
  1874. gclient_t *client;
  1875. int i;
  1876. clientPersistant_t saved;
  1877. clientSession_t savedSess;
  1878. clientInfo_t savedCi;
  1879. int persistant[MAX_PERSISTANT];
  1880. usercmd_t ucmd;
  1881. gentity_t *spawnPoint;
  1882. qboolean beamInEffect = qfalse;
  1883. extern qboolean g_qbLoadTransition;
  1884. index = ent - g_entities;
  1885. client = ent->client;
  1886. if ( eSavedGameJustLoaded == eFULL && g_qbLoadTransition == qfalse )//qbFromSavedGame)
  1887. {//loading up a full save game
  1888. ent->client->pers.teamState.state = TEAM_ACTIVE;
  1889. // increment the spawncount so the client will detect the respawn
  1890. client->ps.persistant[PERS_SPAWN_COUNT]++;
  1891. client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
  1892. client->airOutTime = level.time + 12000;
  1893. for (i=0; i<3; i++)
  1894. {
  1895. ent->client->pers.cmd_angles[i] = 0.0f;
  1896. }
  1897. SetClientViewAngle( ent, ent->client->ps.viewangles);//spawn_angles );
  1898. gi.linkentity (ent);
  1899. // run the presend to set anything else
  1900. ClientEndFrame( ent );
  1901. // clear entity state values
  1902. PlayerStateToEntityState( &client->ps, &ent->s );
  1903. //FIXME: make sure ent->NPC_type is saved out
  1904. G_LoadAnimFileSet( ent, ent->NPC_type );
  1905. G_SetSkin( ent );
  1906. //setup sabers
  1907. G_ReloadSaberData( ent );
  1908. //force power levels should already be set
  1909. }
  1910. else
  1911. {
  1912. // find a spawn point
  1913. // do it before setting health back up, so farthest
  1914. // ranging doesn't count this client
  1915. // don't spawn near existing origin if possible
  1916. spawnPoint = SelectSpawnPoint ( ent->client->ps.origin,
  1917. (team_t) ent->client->ps.persistant[PERS_TEAM], spawn_origin, spawn_angles);
  1918. ent->client->pers.teamState.state = TEAM_ACTIVE;
  1919. // clear everything but the persistant data
  1920. saved = client->pers;
  1921. savedSess = client->sess;
  1922. for ( i = 0 ; i < MAX_PERSISTANT ; i++ )
  1923. {
  1924. persistant[i] = client->ps.persistant[i];
  1925. }
  1926. //Preserve clientInfo
  1927. memcpy (&savedCi, &client->clientInfo, sizeof(clientInfo_t));
  1928. memset (client, 0, sizeof(*client));
  1929. memcpy (&client->clientInfo, &savedCi, sizeof(clientInfo_t));
  1930. client->pers = saved;
  1931. client->sess = savedSess;
  1932. for ( i = 0 ; i < MAX_PERSISTANT ; i++ )
  1933. {
  1934. client->ps.persistant[i] = persistant[i];
  1935. }
  1936. // increment the spawncount so the client will detect the respawn
  1937. client->ps.persistant[PERS_SPAWN_COUNT]++;
  1938. client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
  1939. client->airOutTime = level.time + 12000;
  1940. // clear entity values
  1941. client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
  1942. ent->s.groundEntityNum = ENTITYNUM_NONE;
  1943. ent->client = &level.clients[index];
  1944. ent->mass = 10;
  1945. ent->takedamage = qtrue;
  1946. ent->inuse = qtrue;
  1947. SetInUse(ent);
  1948. ent->m_iIcarusID = IIcarusInterface::ICARUS_INVALID;
  1949. if ( !ent->NPC_type )
  1950. {
  1951. ent->NPC_type = "player";
  1952. }
  1953. ent->classname = "player";
  1954. ent->targetname = ent->script_targetname = "player";
  1955. if ( ent->client->NPC_class == CLASS_NONE )
  1956. {
  1957. ent->client->NPC_class = CLASS_PLAYER;
  1958. }
  1959. client->playerTeam = TEAM_PLAYER;
  1960. client->enemyTeam = TEAM_ENEMY;
  1961. ent->contents = CONTENTS_BODY;
  1962. ent->clipmask = MASK_PLAYERSOLID;
  1963. ent->e_DieFunc = dieF_player_die;
  1964. ent->waterlevel = 0;
  1965. ent->watertype = 0;
  1966. client->ps.friction = 6;
  1967. client->ps.gravity = g_gravity->value;
  1968. ent->flags &= ~FL_NO_KNOCKBACK;
  1969. client->renderInfo.lookTarget = ENTITYNUM_NONE;
  1970. client->renderInfo.lookTargetClearTime = 0;
  1971. client->renderInfo.lookMode = LM_ENT;
  1972. VectorCopy (playerMins, ent->mins);
  1973. VectorCopy (playerMaxs, ent->maxs);
  1974. client->crouchheight = CROUCH_MAXS_2;
  1975. client->standheight = DEFAULT_MAXS_2;
  1976. client->ps.clientNum = index;
  1977. // give default weapons
  1978. //these are precached in g_items, ClearRegisteredItems()
  1979. client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
  1980. //client->ps.inventory[INV_ELECTROBINOCULARS] = 1;
  1981. //ent->client->ps.inventory[INV_BACTA_CANISTER] = 1;
  1982. // give EITHER the saber or the stun baton..never both
  1983. if ( spawnPoint->spawnflags & 32 ) // STUN_BATON
  1984. {
  1985. client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_STUN_BATON );
  1986. client->ps.weapon = WP_STUN_BATON;
  1987. }
  1988. else
  1989. { // give the saber because most test maps will not have the STUN BATON flag set
  1990. client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //this is precached in SP_info_player_deathmatch
  1991. client->ps.weapon = WP_SABER;
  1992. }
  1993. // force the base weapon up
  1994. client->ps.weaponstate = WEAPON_READY;
  1995. for ( i = FIRST_WEAPON; i < MAX_PLAYER_WEAPONS; i++ ) // don't give ammo for explosives
  1996. {
  1997. if ( (client->ps.stats[STAT_WEAPONS]&(1<<i)) )
  1998. {//if starting with this weapon, gimme max ammo for it
  1999. client->ps.ammo[weaponData[i].ammoIndex] = ammoData[weaponData[i].ammoIndex].max;
  2000. }
  2001. }
  2002. if ( eSavedGameJustLoaded == eNO )
  2003. {
  2004. //FIXME: get player's info from NPCs.cfg
  2005. client->ps.dualSabers = qfalse;
  2006. WP_SaberParseParms( g_saber->string, &client->ps.saber[0] );//get saber info
  2007. client->ps.saberStylesKnown |= (1<<gi.Cvar_VariableIntegerValue("g_fighting_style"));
  2008. // if ( client->ps.saber[0].style )
  2009. // {
  2010. // client->ps.saberStylesKnown |= (1<<client->ps.saber[0].style);
  2011. // }
  2012. WP_InitForcePowers( ent );//Initialize force powers
  2013. }
  2014. else
  2015. {//autoload, will be taken care of below
  2016. }
  2017. //
  2018. ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];
  2019. ent->client->dismemberProbHead = 0;
  2020. ent->client->dismemberProbArms = 5;
  2021. ent->client->dismemberProbHands = 20;
  2022. ent->client->dismemberProbWaist = 0;
  2023. ent->client->dismemberProbLegs = 0;
  2024. ent->client->ps.batteryCharge = 2500;
  2025. VectorCopy( spawn_origin, client->ps.origin );
  2026. VectorCopy( spawn_origin, ent->currentOrigin );
  2027. // the respawned flag will be cleared after the attack and jump keys come up
  2028. client->ps.pm_flags |= PMF_RESPAWNED;
  2029. SetClientViewAngle( ent, spawn_angles );
  2030. G_KillBox( ent );
  2031. gi.linkentity (ent);
  2032. // don't allow full run speed for a bit
  2033. client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  2034. client->ps.pm_time = 100;
  2035. client->respawnTime = level.time;
  2036. client->latched_buttons = 0;
  2037. // set default animations
  2038. client->ps.torsoAnim = BOTH_STAND2;
  2039. client->ps.legsAnim = BOTH_STAND2;
  2040. //clear IK grabbing stuff
  2041. client->ps.heldClient = client->ps.heldByClient = ENTITYNUM_NONE;
  2042. client->ps.saberLockEnemy = ENTITYNUM_NONE; //duh, don't think i'm locking with myself
  2043. // restore some player data
  2044. //
  2045. Player_RestoreFromPrevLevel(ent);
  2046. //FIXME: put this BEFORE the Player_RestoreFromPrevLevel check above?
  2047. if (eSavedGameJustLoaded == eNO)
  2048. {//fresh start
  2049. if (!(spawnPoint->spawnflags&1)) // not KEEP_PREV
  2050. {//then restore health and armor
  2051. ent->health = client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];
  2052. ent->client->ps.forcePower = ent->client->ps.forcePowerMax;
  2053. }
  2054. G_InitPlayerFromCvars( ent );
  2055. }
  2056. else
  2057. {//autoload
  2058. G_LoadAnimFileSet( ent, ent->NPC_type );
  2059. G_SetSkin( ent );
  2060. G_ReloadSaberData( ent );
  2061. //force power levels should already be set
  2062. }
  2063. //NEVER start a map with either of your sabers or blades on...
  2064. ent->client->ps.SaberDeactivate();
  2065. // run a client frame to drop exactly to the floor,
  2066. // initialize animations and other things
  2067. client->ps.commandTime = level.time - 100;
  2068. ucmd = client->pers.lastCommand;
  2069. ucmd.serverTime = level.time;
  2070. _VectorCopy( client->pers.cmd_angles, ucmd.angles );
  2071. ucmd.weapon = client->ps.weapon; // client think calls Pmove which sets the client->ps.weapon to ucmd.weapon, so ...
  2072. ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
  2073. ClientThink( ent-g_entities, &ucmd );
  2074. // run the presend to set anything else
  2075. ClientEndFrame( ent );
  2076. // clear entity state values
  2077. PlayerStateToEntityState( &client->ps, &ent->s );
  2078. //ICARUS include
  2079. Quake3Game()->FreeEntity( ent );
  2080. Quake3Game()->InitEntity( ent );
  2081. // Make sure no Sequencer exists then Get a new one.
  2082. IIcarusInterface::GetIcarus()->DeleteIcarusID( ent->m_iIcarusID );
  2083. ent->m_iIcarusID = IIcarusInterface::GetIcarus()->GetIcarusID( ent->s.number );
  2084. if ( spawnPoint->spawnflags & 64 ) //NOWEAPON
  2085. {//player starts with absolutely no weapons
  2086. ent->client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
  2087. ent->client->ps.ammo[weaponData[WP_NONE].ammoIndex] = 32000;
  2088. ent->client->ps.weapon = WP_NONE;
  2089. ent->client->ps.weaponstate = WEAPON_READY;
  2090. }
  2091. if ( ent->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_SABER ) )
  2092. {//set up so has lightsaber
  2093. WP_SaberInitBladeData( ent );
  2094. if ( (ent->weaponModel[0] <= 0 || (ent->weaponModel[1]<=0&&ent->client->ps.dualSabers)) //one or both of the saber models is not initialized
  2095. && ent->client->ps.weapon == WP_SABER )//current weapon is saber
  2096. {//add the proper models
  2097. WP_SaberAddG2SaberModels( ent );
  2098. }
  2099. }
  2100. if ( ent->weaponModel[0] == -1 && ent->client->ps.weapon != WP_NONE )
  2101. {
  2102. G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
  2103. }
  2104. {
  2105. // fire the targets of the spawn point
  2106. G_UseTargets( spawnPoint, ent );
  2107. //Designers needed them to fire off target2's as well... this is kind of messy
  2108. G_UseTargets2( spawnPoint, ent, spawnPoint->target2 );
  2109. /*
  2110. // select the highest weapon number available, after any
  2111. // spawn given items have fired
  2112. client->ps.weapon = 1;
  2113. for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
  2114. if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
  2115. client->ps.weapon = i;
  2116. break;
  2117. }
  2118. }*/
  2119. }
  2120. }
  2121. client->pers.enterTime = level.time;//needed mainly to stop the weapon switch to WP_NONE that happens on loads
  2122. ent->max_health = client->ps.stats[STAT_MAX_HEALTH];
  2123. if ( eSavedGameJustLoaded == eNO )
  2124. {//on map transitions, Ghoul2 frame gets reset to zero, restart our anim
  2125. NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
  2126. NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
  2127. }
  2128. if ( ent->s.number == 0 )
  2129. {//player
  2130. G_CheckPlayerDarkSide();
  2131. }
  2132. if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<<WP_SABER))
  2133. && !ent->client->ps.saberStylesKnown )
  2134. {//um, if you have a saber, you need at least 1 style to use it with...
  2135. ent->client->ps.saberStylesKnown |= (1<<SS_MEDIUM);
  2136. }
  2137. return beamInEffect;
  2138. }
  2139. /*
  2140. ===========
  2141. ClientDisconnect
  2142. Called when a player drops from the server.
  2143. Will not be called between levels.
  2144. ============
  2145. */
  2146. void ClientDisconnect( int clientNum ) {
  2147. gentity_t *ent;
  2148. ent = g_entities + clientNum;
  2149. if ( !ent->client ) {
  2150. return;
  2151. }
  2152. // send effect if they were completely connected
  2153. /* if ( ent->client->pers.connected == CON_CONNECTED ) {
  2154. // They don't get to take powerups with them!
  2155. // Especially important for stuff like CTF flags
  2156. TossClientItems ( ent );
  2157. }
  2158. */
  2159. gi.unlinkentity (ent);
  2160. ent->s.modelindex = 0;
  2161. ent->inuse = qfalse;
  2162. ClearInUse(ent);
  2163. ent->classname = "disconnected";
  2164. ent->client->pers.connected = CON_DISCONNECTED;
  2165. ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE;
  2166. gi.SetConfigstring( CS_PLAYERS + clientNum, "");
  2167. IIcarusInterface::GetIcarus()->DeleteIcarusID(ent->m_iIcarusID);
  2168. }