1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7
8 This file is part of the OpenJK source code.
9
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23
24 #include "g_headers.h"
25
26 #include "g_local.h"
27 #include "g_functions.h"
28 #include "anims.h"
29 #include "g_icarus.h"
30 #include "wp_saber.h"
31
32 extern void Q3_DebugPrint( int level, const char *format, ... );
33 extern void WP_SaberInitBladeData( gentity_t *ent );
34 extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel );
35 extern qboolean CheatsOk( gentity_t *ent );
36 extern vmCvar_t cg_thirdPersonAlpha;
37
38 // g_client.c -- client functions that don't happen every frame
39
40 float DEFAULT_MINS_0 = -16;
41 float DEFAULT_MINS_1 = -16;
42 float DEFAULT_MAXS_0 = 16;
43 float DEFAULT_MAXS_1 = 16;
44 float DEFAULT_PLAYER_RADIUS = sqrt((DEFAULT_MAXS_0*DEFAULT_MAXS_0) + (DEFAULT_MAXS_1*DEFAULT_MAXS_1));
45 vec3_t playerMins = {DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2};
46 vec3_t playerMaxs = {DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2};
47
48 void SP_misc_teleporter_dest (gentity_t *ent);
49
50 /*QUAK-ED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) - - NODRAW
51 potential spawning position for deathmatch games.
52 Targets will be fired when someone spawns in on them.
53 */
SP_info_player_deathmatch(gentity_t * ent)54 void SP_info_player_deathmatch(gentity_t *ent) {
55 SP_misc_teleporter_dest (ent);
56
57 if ( ent->spawnflags & 32 ) // STUN_BATON
58 {
59 RegisterItem( FindItemForWeapon( WP_STUN_BATON ));
60 }
61 else
62 {
63 RegisterItem( FindItemForWeapon( WP_SABER ) ); //these are given in ClientSpawn(), but we register them now before cgame starts
64 G_SkinIndex("models/players/kyle/model_fpls2.skin"); //preache the skin used in cg_players.cpp
65 }
66 }
67
68 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) KEEP_PREV DROPTOFLOOR x x x STUN_BATON NOWEAPON x
69 KEEP_PREV - keep previous health/ammo/etc
70 DROPTOFLOOR - Player will start on the first solid structure under it
71 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.
72
73 Targets will be fired when someone spawns in on them.
74 equivalant to info_player_deathmatch
75 */
SP_info_player_start(gentity_t * ent)76 void SP_info_player_start(gentity_t *ent) {
77 ent->classname = "info_player_deathmatch";
78
79 ent->spawnflags |= 1; // James suggests force-ORing the KEEP_PREV flag in for now
80
81 SP_info_player_deathmatch( ent );
82 }
83
84
85
86 /*
87 =======================================================================
88
89 SelectSpawnPoint
90
91 =======================================================================
92 */
93
94 /*
95 ================
96 SpotWouldTelefrag
97
98 ================
99 */
SpotWouldTelefrag(gentity_t * spot,team_t checkteam)100 qboolean SpotWouldTelefrag( gentity_t *spot, team_t checkteam )
101 {
102 int i, num;
103 gentity_t *touch[MAX_GENTITIES], *hit;
104 vec3_t mins, maxs;
105
106 // If we have a mins, use that instead of the hardcoded bounding box
107 if ( !VectorCompare(spot->mins, vec3_origin) && VectorLength( spot->mins ) )
108 VectorAdd( spot->s.origin, spot->mins, mins );
109 else
110 VectorAdd( spot->s.origin, playerMins, mins );
111
112 // If we have a maxs, use that instead of the hardcoded bounding box
113 if ( !VectorCompare(spot->maxs, vec3_origin) && VectorLength( spot->maxs ) )
114 VectorAdd( spot->s.origin, spot->maxs, maxs );
115 else
116 VectorAdd( spot->s.origin, playerMaxs, maxs );
117
118 num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
119
120 for (i=0 ; i<num ; i++)
121 {
122 hit = touch[i];
123 if ( hit != spot && hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 )
124 {
125 if ( hit->contents & CONTENTS_BODY )
126 {
127 if( checkteam == TEAM_FREE || hit->client->playerTeam == checkteam )
128 {//checking against teammates only...?
129 return qtrue;
130 }
131 }
132 }
133 }
134
135 return qfalse;
136 }
137
SpotWouldTelefrag2(gentity_t * mover,vec3_t dest)138 qboolean SpotWouldTelefrag2( gentity_t *mover, vec3_t dest )
139 {
140 int i, num;
141 gentity_t *touch[MAX_GENTITIES], *hit;
142 vec3_t mins, maxs;
143
144 VectorAdd( dest, mover->mins, mins );
145 VectorAdd( dest, mover->maxs, maxs );
146 num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
147
148 for (i=0 ; i<num ; i++)
149 {
150 hit = touch[i];
151 if ( hit == mover )
152 {
153 continue;
154 }
155
156 if ( hit->contents & mover->contents )
157 {
158 return qtrue;
159 }
160 }
161
162 return qfalse;
163 }
164 /*
165 ================
166 SelectNearestDeathmatchSpawnPoint
167
168 Find the spot that we DON'T want to use
169 ================
170 */
171 #define MAX_SPAWN_POINTS 128
SelectNearestDeathmatchSpawnPoint(vec3_t from,team_t team)172 gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from, team_t team ) {
173 gentity_t *spot;
174 float dist, nearestDist;
175 gentity_t *nearestSpot;
176
177 nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
178 nearestSpot = NULL;
179 spot = NULL;
180
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
189 if ( spot->targetname != NULL ) {
190 //this search routine should never find a spot that is targetted
191 continue;
192 }
193 dist = DistanceSquared( spot->s.origin, from );
194 if ( dist < nearestDist ) {
195 nearestDist = dist;
196 nearestSpot = spot;
197 }
198 }
199
200 return nearestSpot;
201 }
202
203
204 /*
205 ================
206 SelectRandomDeathmatchSpawnPoint
207
208 go to a random point that doesn't telefrag
209 ================
210 */
211 #define MAX_SPAWN_POINTS 128
SelectRandomDeathmatchSpawnPoint(team_t team)212 gentity_t *SelectRandomDeathmatchSpawnPoint( team_t team ) {
213 gentity_t *spot;
214 int count;
215 int selection;
216 gentity_t *spots[MAX_SPAWN_POINTS];
217
218 count = 0;
219 spot = NULL;
220
221 while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
222 /*if ( team == TEAM_RED && ( spot->spawnflags & 2 ) ) {
223 continue;
224 }
225 if ( team == TEAM_BLUE && ( spot->spawnflags & 1 ) ) {
226 continue;
227 }*/
228
229 if ( spot->targetname != NULL ) {
230 //this search routine should never find a spot that is targetted
231 continue;
232 }
233 if ( SpotWouldTelefrag( spot, TEAM_FREE ) ) {
234 continue;
235 }
236 spots[ count ] = spot;
237 count++;
238 }
239
240 if ( !count ) { // no spots that won't telefrag
241 spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch");
242 if ( !spot )
243 {
244 return NULL;
245 }
246 if ( spot->targetname != NULL )
247 {
248 //this search routine should never find a spot that is targetted
249 return NULL;
250 }
251 else
252 {
253 return spot;
254 }
255 }
256
257 selection = rand() % count;
258 return spots[ selection ];
259 }
260
261
262 /*
263 ===========
264 SelectSpawnPoint
265
266 Chooses a player start, deathmatch start, etc
267 ============
268 */
SelectSpawnPoint(vec3_t avoidPoint,team_t team,vec3_t origin,vec3_t angles)269 gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, team_t team, vec3_t origin, vec3_t angles ) {
270 gentity_t *spot;
271 gentity_t *nearestSpot;
272
273 if ( level.spawntarget[0] )
274 {//we have a spawnpoint specified, try to find it
275 if ( (nearestSpot = spot = G_Find( NULL, FOFS(targetname), level.spawntarget )) == NULL )
276 {//you HAVE to be able to find the desired spot
277 G_Error( "Couldn't find spawntarget %s", level.spawntarget );
278 return NULL;
279 }
280 }
281 else
282 {//not looking for a special startspot
283 nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint, team );
284
285 spot = SelectRandomDeathmatchSpawnPoint ( team );
286 if ( spot == nearestSpot ) {
287 // roll again if it would be real close to point of death
288 spot = SelectRandomDeathmatchSpawnPoint ( team );
289 }
290 }
291
292 // find a single player start spot
293 if (!spot) {
294 G_Error( "Couldn't find a spawn point" );
295 }
296
297
298 VectorCopy( spot->s.origin, origin );
299 if ( spot->spawnflags & 2 )
300 {
301 trace_t tr;
302
303 origin[2] = MIN_WORLD_COORD;
304 gi.trace(&tr, spot->s.origin, playerMins, playerMaxs, origin, ENTITYNUM_NONE, MASK_PLAYERSOLID, G2_NOCOLLIDE, 0 );
305 if ( tr.fraction < 1.0 && !tr.allsolid && !tr.startsolid )
306 {//found a floor
307 VectorCopy(tr.endpos, origin );
308 }
309 else
310 {//In solid or too far
311 VectorCopy( spot->s.origin, origin );
312 }
313 }
314
315 origin[2] += 9;
316 VectorCopy (spot->s.angles, angles);
317
318 return spot;
319 }
320
321
322 //======================================================================
323
324
325 /*
326 ==================
327 SetClientViewAngle
328
329 ==================
330 */
SetClientViewAngle(gentity_t * ent,vec3_t angle)331 void SetClientViewAngle( gentity_t *ent, vec3_t angle ) {
332 int i;
333
334 // set the delta angle
335 for (i=0 ; i<3 ; i++)
336 {
337 ent->client->ps.delta_angles[i] = (ANGLE2SHORT(angle[i]) - ent->client->pers.cmd_angles[i])&0xffff;
338 }
339 VectorCopy( angle, ent->s.angles );
340 VectorCopy (ent->s.angles, ent->client->ps.viewangles);
341 }
342
343 /*
344 ================
345 respawn
346 ================
347 */
respawn(gentity_t * ent)348 void respawn( gentity_t *ent ) {
349
350 gi.SendConsoleCommand("load *respawn\n"); // special case
351 }
352
353
354 /*
355 ================
356 PickTeam
357
358 ================
359 */
PickTeam(int ignoreClientNum)360 team_t PickTeam( int ignoreClientNum ) {
361 int i;
362 int counts[TEAM_NUM_TEAMS];
363
364 memset( counts, 0, sizeof( counts ) );
365
366 for ( i = 0 ; i < level.maxclients ; i++ ) {
367 if ( i == ignoreClientNum ) {
368 continue;
369 }
370 if ( level.clients[i].pers.connected == CON_DISCONNECTED ) {
371 continue;
372 }
373 }
374
375 return TEAM_FREE;
376 }
377
378 /*
379 ===========
380 ForceClientSkin
381
382 Forces a client's skin (for teamplay)
383 ===========
384 */
ForceClientSkin(gclient_t * client,char * model,const char * skin)385 void ForceClientSkin( gclient_t *client, char *model, const char *skin ) {
386 char *p;
387
388 if ((p = strchr(model, '/')) != NULL) {
389 *p = 0;
390 }
391
392 Q_strcat(model, MAX_QPATH, "/");
393 Q_strcat(model, MAX_QPATH, skin);
394 }
395
396 /*
397 ===========
398 ClientCheckName
399 ============
400 */
ClientCleanName(const char * in,char * out,int outSize)401 static void ClientCleanName( const char *in, char *out, int outSize )
402 {
403 int outpos = 0, colorlessLen = 0, spaces = 0;
404
405 // discard leading spaces
406 for ( ; *in == ' '; in++);
407
408 // discard leading asterisk's (fail raven for using * as a skipnotify)
409 // apparently .* causes the issue too so... derp
410 //for(; *in == '*'; in++);
411
412 for(; *in && outpos < outSize - 1; in++)
413 {
414 out[outpos] = *in;
415
416 if ( *in == ' ' )
417 {// don't allow too many consecutive spaces
418 if ( spaces > 2 )
419 continue;
420
421 spaces++;
422 }
423 else if ( outpos > 0 && out[outpos-1] == Q_COLOR_ESCAPE )
424 {
425 if ( Q_IsColorStringExt( &out[outpos-1] ) )
426 {
427 colorlessLen--;
428
429 #if 0
430 if ( ColorIndex( *in ) == 0 )
431 {// Disallow color black in names to prevent players from getting advantage playing in front of black backgrounds
432 outpos--;
433 continue;
434 }
435 #endif
436 }
437 else
438 {
439 spaces = 0;
440 colorlessLen++;
441 }
442 }
443 else
444 {
445 spaces = 0;
446 colorlessLen++;
447 }
448
449 outpos++;
450 }
451
452 out[outpos] = '\0';
453
454 // don't allow empty names
455 if ( *out == '\0' || colorlessLen == 0 )
456 Q_strncpyz( out, "Padawan", outSize );
457 }
458
459 /*
460 ===========
461 ClientUserInfoChanged
462
463 Called from ClientConnect when the player first connects and
464 directly by the server system when the player updates a userinfo variable.
465
466 The game can override any of the settings and call gi.SetUserinfo
467 if desired.
468 ============
469 */
ClientUserinfoChanged(int clientNum)470 void ClientUserinfoChanged( int clientNum ) {
471 gentity_t *ent = g_entities + clientNum;
472 gclient_t *client = ent->client;
473 int health=100, maxHealth=100;
474 const char *s=NULL, *sex=NULL;
475 char userinfo[MAX_INFO_STRING]={0}, buf[MAX_INFO_STRING]={0},
476 oldname[34]={0};
477
478 gi.GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
479
480 // set name
481 Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) );
482 s = Info_ValueForKey (userinfo, "name");
483 ClientCleanName( s, client->pers.netname, sizeof( client->pers.netname ) );
484
485 // set max health
486 maxHealth = 100;
487 health = Com_Clampi( 1, 100, atoi( Info_ValueForKey( userinfo, "handicap" ) ) );
488 client->pers.maxHealth = health;
489 if ( client->pers.maxHealth < 1 || client->pers.maxHealth > maxHealth )
490 client->pers.maxHealth = 100;
491 client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
492
493 // sex
494 sex = Info_ValueForKey( userinfo, "sex" );
495 if ( !sex[0] ) {
496 sex = "m";
497 }
498
499 // send over a subset of the userinfo keys so other clients can
500 // print scoreboards, display models, and play custom sounds
501 buf[0] = '\0';
502 Q_strcat( buf, sizeof( buf ), va( "n\\%s\\", client->pers.netname ) );
503 Q_strcat( buf, sizeof( buf ), va( "t\\%i\\", client->sess.sessionTeam ) );
504 Q_strcat( buf, sizeof( buf ), "headModel\\\\" );
505 Q_strcat( buf, sizeof( buf ), "torsoModel\\\\" );
506 Q_strcat( buf, sizeof( buf ), "legsModel\\\\" );
507 Q_strcat( buf, sizeof( buf ), va( "sex\\%s\\", sex ) );
508 Q_strcat( buf, sizeof( buf ), va( "hc\\%i\\", client->pers.maxHealth ) );
509
510 gi.SetConfigstring( CS_PLAYERS+clientNum, buf );
511 }
512
513
514 /*
515 ===========
516 ClientConnect
517
518 Called when a player begins connecting to the server.
519 Called again for every map change or tournement restart.
520
521 The session information will be valid after exit.
522
523 Return NULL if the client should be allowed, otherwise return
524 a string with the reason for denial.
525
526 Otherwise, the client will be sent the current gamestate
527 and will eventually get to ClientBegin.
528
529 firstTime will be qtrue the very first time a client connects
530 to the server machine, but qfalse on map changes and tournement
531 restarts.
532 ============
533 */
ClientConnect(int clientNum,qboolean firstTime,SavedGameJustLoaded_e eSavedGameJustLoaded)534 char *ClientConnect( int clientNum, qboolean firstTime, SavedGameJustLoaded_e eSavedGameJustLoaded )
535 {
536 gentity_t *ent = &g_entities[ clientNum ];
537 char userinfo[MAX_INFO_STRING] = {0};
538
539 gi.GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
540
541 // they can connect
542 ent->client = level.clients + clientNum;
543 gclient_t *client = ent->client;
544
545 // if (!qbFromSavedGame)
546 if (eSavedGameJustLoaded != eFULL)
547 {
548 clientSession_t savedSess = client->sess; //
549 memset( client, 0, sizeof(*client) );
550 client->sess = savedSess;
551 }
552
553 client->pers.connected = CON_CONNECTING;
554
555 if (eSavedGameJustLoaded == eFULL)//qbFromSavedGame)
556 {
557 // G_WriteClientSessionData( client ); // forget it, this is DM stuff anyway
558 // get and distribute relevent paramters
559 ClientUserinfoChanged( clientNum );
560 }
561 else
562 {
563 // read or initialize the session data
564 if ( firstTime ) {
565 G_InitSessionData( client, userinfo );
566 }
567 G_ReadSessionData( client );
568
569 // get and distribute relevent paramters
570 ClientUserinfoChanged( clientNum );
571
572 // don't do the "xxx connected" messages if they were caried over from previous level
573 if ( firstTime ) {
574 gi.SendServerCommand( -1, "print \"%s connected\n\"", client->pers.netname);
575 }
576 }
577
578 return NULL;
579 }
580
581 /*
582 ===========
583 ClientBegin
584
585 called when a client has finished connecting, and is ready
586 to be placed into the level. This will happen every level load,
587 and on transition between teams, but doesn't happen on respawns
588 ============
589 */
ClientBegin(int clientNum,usercmd_t * cmd,SavedGameJustLoaded_e eSavedGameJustLoaded)590 void ClientBegin( int clientNum, usercmd_t *cmd, SavedGameJustLoaded_e eSavedGameJustLoaded)
591 // qboolean qbFromSavedGame
592 {
593 gentity_t *ent;
594 gclient_t *client;
595
596 ent = g_entities + clientNum;
597 client = level.clients + clientNum;
598
599 if (eSavedGameJustLoaded == eFULL)//qbFromSavedGame)
600 {
601 client->pers.connected = CON_CONNECTED;
602 ent->client = client;
603 ClientSpawn( ent, eSavedGameJustLoaded );
604 }
605 else
606 {
607 if ( ent->linked ) {
608 gi.unlinkentity( ent );
609 }
610 G_InitGentity( ent );
611 ent->e_TouchFunc = touchF_NULL;
612 ent->e_PainFunc = painF_PlayerPain;//painF_NULL;
613 ent->client = client;
614
615 client->pers.connected = CON_CONNECTED;
616 client->pers.teamState.state = TEAM_BEGIN;
617 VectorCopyM( cmd->angles, client->pers.cmd_angles );
618
619 memset( &client->ps, 0, sizeof( client->ps ) );
620 memset( &client->sess.missionStats, 0, sizeof( client->sess.missionStats ) );
621 client->sess.missionStats.totalSecrets = gi.Cvar_VariableIntegerValue("newTotalSecrets");
622
623 // locate ent at a spawn point
624 if ( ClientSpawn( ent, eSavedGameJustLoaded) ) // SavedGameJustLoaded_e
625 {
626 // send teleport event
627 }
628 client->ps.inventory[INV_GOODIE_KEY] = 0;
629 client->ps.inventory[INV_SECURITY_KEY] = 0;
630 }
631 }
632
633
634
635 /*
636 ============
637 Player_CacheFromPrevLevel
638 Description : just need to grab the weapon items we're going to have when we spawn so they'll be cached
639 Return type : void
640 Argument : void
641 ============
642 */
Player_CacheFromPrevLevel(void)643 void Player_CacheFromPrevLevel(void)
644 {
645 char s[MAX_STRING_CHARS];
646 int i;
647
648 gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
649
650 if (strlen(s)) // actually this would be safe anyway because of the way sscanf() works, but this is clearer
651 {
652 int iDummy, bits, ibits;
653
654 sscanf( s, "%i %i %i %i",
655 &iDummy,//client->ps.stats[STAT_HEALTH],
656 &iDummy,//client->ps.stats[STAT_ARMOR],
657 &bits, //client->ps.stats[STAT_WEAPONS]
658 &ibits //client->ps.stats[STAT_ITEMS]
659 );
660
661 for ( i = 1 ; i < 16 ; i++ )
662 {
663 if ( bits & ( 1 << i ) )
664 {
665 RegisterItem( FindItemForWeapon( (weapon_t)i ) );
666 }
667 }
668
669 extern gitem_t *FindItemForInventory( int inv );
670
671 for ( i = 1 ; i < 16 ; i++ )
672 {
673 if ( ibits & ( 1 << i ) )
674 {
675 RegisterItem( FindItemForInventory( i-1 ));
676 }
677 }
678 }
679 }
680
681 /*
682 ============
683 Player_RestoreFromPrevLevel
684 Description : retrieve maptransition data recorded by server when exiting previous level (to carry over weapons/ammo/health/etc)
685 Return type : void
686 Argument : gentity_t *ent
687 ============
688 */
Player_RestoreFromPrevLevel(gentity_t * ent)689 void Player_RestoreFromPrevLevel(gentity_t *ent)
690 {
691 gclient_t *client = ent->client;
692 int i;
693
694 assert(client);
695 if (client) // though I can't see it not being true...
696 {
697 char s[MAX_STRING_CHARS];
698 const char *var;
699 int saberActive;
700
701 gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
702
703 if (strlen(s)) // actually this would be safe anyway because of the way sscanf() works, but this is clearer
704 {
705 sscanf( s, "%i %i %i %i %i %i %i %f %f %f %i %i %i %i %i %i",
706 &client->ps.stats[STAT_HEALTH],
707 &client->ps.stats[STAT_ARMOR],
708 &client->ps.stats[STAT_WEAPONS],
709 &client->ps.stats[STAT_ITEMS],
710 &client->ps.weapon,
711 &client->ps.weaponstate,
712 &client->ps.batteryCharge,
713 &client->ps.viewangles[0],
714 &client->ps.viewangles[1],
715 &client->ps.viewangles[2],
716 &client->ps.forcePowersKnown,
717 &client->ps.forcePower,
718 &saberActive,
719 &client->ps.saberAnimLevel,
720 &client->ps.saberLockEnemy,
721 &client->ps.saberLockTime
722 );
723 client->ps.saberActive = (saberActive ? qtrue : qfalse);
724 ent->health = client->ps.stats[STAT_HEALTH];
725
726 // slight issue with ths for the moment in that although it'll correctly restore angles it doesn't take into account
727 // the overall map orientation, so (eg) exiting east to enter south will be out by 90 degrees, best keep spawn angles for now
728 //
729 // VectorClear (ent->client->pers.cmd_angles);
730 //
731 // SetClientViewAngle( ent, ent->client->ps.viewangles);
732
733 //ammo
734 gi.Cvar_VariableStringBuffer( "playerammo", s, sizeof(s) );
735 i=0;
736 var = strtok( s, " " );
737 while( var != NULL )
738 {
739 /* While there are tokens in "s" */
740 client->ps.ammo[i++] = atoi(var);
741 /* Get next token: */
742 var = strtok( NULL, " " );
743 }
744 assert (i==AMMO_MAX);
745
746 //inventory
747 gi.Cvar_VariableStringBuffer( "playerinv", s, sizeof(s) );
748 i=0;
749 var = strtok( s, " " );
750 while( var != NULL )
751 {
752 /* While there are tokens in "s" */
753 client->ps.inventory[i++] = atoi(var);
754 /* Get next token: */
755 var = strtok( NULL, " " );
756 }
757 assert (i==INV_MAX);
758
759
760 // the new JK2 stuff - force powers, etc...
761 //
762 gi.Cvar_VariableStringBuffer( "playerfplvl", s, sizeof(s) );
763 i=0;
764 var = strtok( s, " " );
765 while( var != NULL )
766 {
767 /* While there are tokens in "s" */
768 client->ps.forcePowerLevel[i++] = atoi(var);
769 /* Get next token: */
770 var = strtok( NULL, " " );
771 }
772 assert (i==NUM_FORCE_POWERS);
773
774 client->ps.forcePowerMax = FORCE_POWER_MAX;
775 client->ps.forceGripEntityNum = ENTITYNUM_NONE;
776 }
777 }
778 }
779
780 /*
781 Ghoul2 Insert Start
782 */
G_SetSkin(gentity_t * ent,const char * modelName,const char * customSkin)783 void G_SetSkin( gentity_t *ent, const char *modelName, const char *customSkin )
784 {
785 char skinName[MAX_QPATH];
786 //ok, lets register the skin name, and then pass that name to the config strings so the client can get it too.
787 //FIXME: is have an alternate skin (in modelName, after '/'), replace "default" with that skin name
788 if ( !customSkin )
789 {
790 Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", modelName );
791 }
792 else
793 {
794 Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", modelName, customSkin );
795 }
796 // lets see if it's out there
797 int skin = gi.RE_RegisterSkin( skinName );
798 if ( skin )
799 {
800 // put it in the config strings
801 // and set the ghoul2 model to use it
802 gi.G2API_SetSkin( &ent->ghoul2[ent->playerModel], G_SkinIndex( skinName ), skin );
803 }
804 }
805
G_StandardHumanoid(const char * modelName)806 qboolean G_StandardHumanoid( const char *modelName )
807 {
808 if ( !modelName )
809 {
810 return qfalse;
811 }
812 if ( !Q_stricmp( "kyle", modelName ) ||
813 !Q_strncmp( "st", modelName, 2 ) ||
814 !Q_strncmp( "imp", modelName, 3 ) ||
815 !Q_strncmp( "gran", modelName, 4 ) ||
816 !Q_strncmp( "rodian", modelName, 6 ) ||
817 !Q_strncmp( "weequay", modelName, 7 ) ||
818 !Q_strncmp( "reborn", modelName, 6 ) ||
819 !Q_strncmp( "shadowtrooper", modelName, 13 ) ||
820 !Q_strncmp( "swamptrooper", modelName, 12 ) ||
821 !Q_stricmp( "rockettrooper", modelName ) ||
822 !Q_stricmp( "bespin_cop", modelName ) ||
823 !Q_strncmp( "bespincop", modelName, 9 ) ||
824 !Q_strncmp( "rebel", modelName, 5 ) ||
825 !Q_strncmp( "ugnaught", modelName, 8 ) ||
826 !Q_strncmp( "morgan", modelName,6 ) ||
827 !Q_strncmp( "protocol", modelName, 8 ) ||
828 !Q_strncmp( "jedi", modelName, 4 ) ||
829 !Q_strncmp( "prisoner", modelName, 8 ) ||
830 !Q_stricmp( "tavion", modelName ) ||
831 !Q_stricmp( "desann", modelName ) ||
832 !Q_stricmp( "trandoshan", modelName ) ||
833 !Q_stricmp( "jan", modelName ) ||
834 !Q_stricmp( "luke", modelName ) ||
835 !Q_stricmp( "lando", modelName ) ||
836 !Q_stricmp( "reelo", modelName ) ||
837 !Q_stricmp( "bartender", modelName ) ||
838 !Q_stricmp( "monmothma", modelName ) ||
839 !Q_stricmp( "chiss", modelName ) ||
840 !Q_stricmp( "galak", modelName ) )
841 {
842 return qtrue;
843 }
844 return qfalse;
845 }
846 extern void G_LoadAnimFileSet( gentity_t *ent, const char *modelName );
G_SetG2PlayerModelInfo(gentity_t * ent,const char * modelName,const char * customSkin,const char * surfOff,const char * surfOn)847 qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn )
848 {
849 if ( ent->playerModel != -1 )
850 {// we found the model ok
851 vec3_t angles = {0,0,0};
852 const char *token;
853 const char *p;
854
855 //Now turn on/off any surfaces
856 if ( surfOff && surfOff[0] )
857 {
858 p = surfOff;
859 COM_BeginParseSession();
860 while ( 1 )
861 {
862 token = COM_ParseExt( &p, qtrue );
863 if ( !token[0] )
864 {//reached end of list
865 break;
866 }
867 //turn off this surf
868 gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], token, 0x00000002/*G2SURFACEFLAG_OFF*/ );
869 }
870 COM_EndParseSession();
871 }
872 if ( surfOn && surfOn[0] )
873 {
874 p = surfOn;
875 COM_BeginParseSession();
876 while ( 1 )
877 {
878 token = COM_ParseExt( &p, qtrue );
879 if ( !token[0] )
880 {//reached end of list
881 break;
882 }
883 //turn on this surf
884 gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], token, 0 );
885 }
886 COM_EndParseSession();
887 }
888 if ( ent->client->NPC_class == CLASS_IMPERIAL && ent->message )
889 {//carrying a key, turn on the key sleeve surface (assuming we have one)
890 gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "l_arm_key", 0 );
891 }
892
893 G_LoadAnimFileSet( ent, modelName );
894 //we shouldn't actually have to do this anymore
895 //G_SetSkin( ent, modelName, customSkin );
896
897 ent->headBolt = ent->cervicalBolt = ent->torsoBolt = ent->gutBolt = ent->chestBolt =
898 ent->crotchBolt = ent->elbowLBolt = ent->elbowRBolt = ent->handLBolt =
899 ent->handRBolt = ent->kneeLBolt = ent->kneeRBolt = ent->footLBolt =
900 ent->footRBolt = -1;
901 // now turn on the bolt in the hand - this one would be best always turned on.
902 if ( G_StandardHumanoid( modelName ) )
903 {//temp hack because only the gran are set up right so far
904 ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
905 ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" );
906 if ( !Q_stricmp("protocol", modelName ) )
907 {//*sigh*, no thoracic bone
908 ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
909 ent->chestBolt = ent->gutBolt;
910 }
911 else
912 {
913 ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic");
914 ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
915 }
916 ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar");
917 ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis");
918 ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_arm_elbow");
919 ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_arm_elbow");
920 ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
921 ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
922 ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_l_knee");
923 ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_r_knee");
924 ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot");
925 ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot");
926 }
927 else
928 {
929 if ( !Q_stricmp( "gonk", modelName ) || !Q_stricmp( "seeker", modelName ) || !Q_stricmp( "remote", modelName )
930 || !Q_strncmp( "r2d2", modelName, 4 ) || !Q_strncmp( "r5d2", modelName, 4 ) )
931 {//TEMP HACK: not a non-humanoid droid
932 ent->headBolt = -1;
933 }
934 else if (!Q_stricmp( "interrogator",modelName))
935 {
936 ent->headBolt = -1;
937 }
938 else if (!Q_strncmp( "probe",modelName, 5))
939 {
940 ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cranium"); // head pivot point
941 ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash"); // Gun 1
942 }
943 /*
944 else if (!Q_strncmp( "protocol",modelName, 8))
945 {
946 ent->headBolt = -1;
947 }
948 */
949 else if (!Q_stricmp( "sentry",modelName))
950 {
951 ent->headBolt = -1;
952 ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Gun 1
953 ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2"); // Gun 2
954 ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash03"); // Gun 3
955 }
956 else if (!Q_stricmp( "mark1",modelName))
957 {
958 ent->headBolt = -1;
959 ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Blaster Gun 1
960 ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2"); // Blaster Gun 2
961 ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3"); // Blaster Gun 3
962 ent->genericBolt4 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4"); // Blaster Gun 4
963 ent->genericBolt5 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash5"); // Missile Gun 1
964 }
965 else if (!Q_stricmp( "mark2",modelName))
966 {
967 ent->headBolt = -1;
968 ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash"); // Blaster Gun 1
969 }
970 else if (!Q_stricmp( "atst",modelName) )//&& (ent->client->playerTeam != TEAM_PLAYER))
971 {
972 ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head");
973
974 ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Front guns
975 ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2");
976
977 ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3"); // Left side gun
978 ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4"); // Right side missle launcher
979
980 ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_foot");
981 ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_foot");
982 }
983 else if ( !Q_stricmp( "minemonster", modelName ))
984 {
985 ent->handRBolt = ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_f1");
986 }
987 else if ( !Q_stricmp( "howler", modelName ))
988 {
989 ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cranium"); // FIXME!
990 }
991 else if ( !Q_stricmp( "galak_mech", modelName ))
992 {
993 ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*antenna_effect");
994 ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
995 ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "torso");
996 ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "hips");
997 ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flasha");
998 ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flashb");
999 ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flashc");
1000 }
1001 else
1002 {//TEMP HACK: not a non-humanoid droid
1003 ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*weapon");//should be r_hand
1004 if ( Q_stricmp( "atst", modelName ) )
1005 {//not an ATST
1006 ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*headg");
1007 ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" );
1008 ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar");
1009 ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
1010 ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic");
1011 ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis");
1012 ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*bicep_lg");
1013 ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*bicep_rg");
1014 ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hand_l");
1015 ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*thigh_lg");
1016 ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*thigh_rg");
1017 ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*foot_lg");
1018 ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*foot_rg");
1019 }
1020 }
1021 }
1022
1023 ent->faceBone = BONE_INDEX_INVALID;
1024 ent->craniumBone = BONE_INDEX_INVALID;
1025 ent->cervicalBone = BONE_INDEX_INVALID;
1026 ent->thoracicBone = BONE_INDEX_INVALID;
1027 ent->upperLumbarBone = BONE_INDEX_INVALID;
1028 ent->lowerLumbarBone = BONE_INDEX_INVALID;
1029 ent->motionBone = BONE_INDEX_INVALID;
1030 ent->hipsBone = BONE_INDEX_INVALID;
1031 ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
1032 ent->footLBone = BONE_INDEX_INVALID;
1033 ent->footRBone = BONE_INDEX_INVALID;
1034 // now add overrides on specific joints so the client can set angle overrides on the legs, torso and head
1035 if ( !Q_stricmp( "gonk", modelName ) || !Q_stricmp( "seeker", modelName ) || !Q_stricmp( "remote", modelName ) )
1036 {//
1037 }
1038 else if (!Q_stricmp( "sentry",modelName))
1039 {
1040 }
1041 else if (!Q_strncmp( "probe", modelName, 5 ))
1042 {
1043 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1044 if (ent->craniumBone>=0)
1045 {
1046 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1047 }
1048 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
1049 if (ent->thoracicBone>=0)
1050 {
1051 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1052 }
1053 }
1054 /*
1055 else if (!Q_strncmp( "protocol",modelName,8))
1056 {
1057 }
1058 */
1059 else if (!Q_stricmp( "interrogator", modelName ))
1060 {
1061 ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "left_arm", qtrue );
1062 if (ent->genericBone1>=0)
1063 {
1064 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1065 }
1066 ent->genericBone2 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "right_arm", qtrue );
1067 if (ent->genericBone2>=0)
1068 {
1069 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone2, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1070 }
1071 ent->genericBone3 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "claw", qtrue );
1072 if (ent->genericBone3>=0)
1073 {
1074 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone3, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1075 }
1076 }
1077 else if (!Q_strncmp( "r2d2", modelName, 4 ))
1078 {
1079 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1080 if (ent->craniumBone>=0)
1081 {
1082 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1083 }
1084 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue );
1085 if (ent->thoracicBone>=0)
1086 {
1087 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1088 }
1089 ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "f_eye", qtrue );
1090 if (ent->genericBone1>=0)
1091 {
1092 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1093 }
1094 }
1095 else if (!Q_strncmp( "r5d2", modelName, 4 ))
1096 {
1097 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1098 if (ent->craniumBone>=0)
1099 {
1100 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1101 }
1102 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue );
1103 if (ent->thoracicBone>=0)
1104 {
1105 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1106 }
1107 }
1108 else if ( !Q_stricmp( "atst", modelName ))
1109 {
1110 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1111 if (ent->craniumBone>=0)
1112 {
1113 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1114 }
1115 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1116 if (ent->thoracicBone>=0)
1117 {
1118 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1119 }
1120 ent->footLBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "l_tarsal", qtrue );
1121 if (ent->footLBone>=0)
1122 {
1123 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footLBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
1124 }
1125 ent->footRBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "r_tarsal", qtrue );
1126 if (ent->footRBone>=0)
1127 {
1128 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footRBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
1129 }
1130 }
1131 else if ( !Q_stricmp( "mark1", modelName ))
1132 {
1133 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1134 if (ent->craniumBone>=0)
1135 {
1136 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1137 }
1138 ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1139 if (ent->upperLumbarBone>=0)
1140 {
1141 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1142 }
1143 }
1144 else if ( !Q_stricmp( "mark2", modelName ))
1145 {
1146 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1147 if (ent->craniumBone>=0)
1148 {
1149 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1150 }
1151 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1152 if (ent->thoracicBone>=0)
1153 {
1154 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1155 }
1156 }
1157 else if ( !Q_stricmp( "minemonster", modelName ))
1158 {
1159 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic1", qtrue );
1160 if (ent->thoracicBone>=0)
1161 {
1162 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1163 }
1164 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1165 if (ent->craniumBone>=0)
1166 {
1167 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1168 }
1169 }
1170 else if ( !Q_stricmp( "howler", modelName ))
1171 {
1172 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1173 if (ent->thoracicBone>=0)
1174 {
1175 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1176 }
1177 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1178 if (ent->craniumBone>=0)
1179 {
1180 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1181 }
1182 }
1183 else
1184 {
1185 //special case motion bone - to match up split anims
1186 ent->motionBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "Motion", qtrue );
1187 if (ent->motionBone>=0)
1188 {
1189 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->motionBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0 );
1190 }
1191 ent->motionBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Motion");
1192 //bone needed for turning anims
1193 ent->hipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
1194 if (ent->hipsBone>=0)
1195 {
1196 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->hipsBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1197 }
1198 //regular bones we need
1199 ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
1200 if (ent->upperLumbarBone>=0)
1201 {
1202 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1203 }
1204 ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
1205 if (ent->lowerLumbarBone>=0)
1206 {
1207 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1208 }
1209
1210 ent->faceBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "face", qtrue );
1211 if (ent->faceBone>=0)
1212 {
1213 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->faceBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1214 }
1215 ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1216 if (ent->craniumBone>=0)
1217 {
1218 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1219 }
1220 ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
1221 if (ent->cervicalBone>=0)
1222 {
1223 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1224 }
1225 ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1226 if (ent->thoracicBone>=0)
1227 {
1228 gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1229 }
1230 }
1231 ent->client->clientInfo.infoValid = qtrue;
1232
1233 }
1234
1235 int max;
1236 if ( ent->s.radius <= 0 )//radius cannot be negative or zero
1237 {//set the radius to be the largest axial distance on the entity
1238 max = ent->mins[0];//NOTE: mins is always negative
1239 if ( max > ent->mins[1] )
1240 {
1241 max = ent->mins[1];
1242 }
1243
1244 if ( max > ent->mins[2] )
1245 {
1246 max = ent->mins[2];
1247 }
1248
1249 max = fabs((double)max);//convert to positive to compare with maxs
1250 if ( max < ent->maxs[0] )
1251 {
1252 max = ent->maxs[0];
1253 }
1254
1255 if ( max < ent->maxs[1] )
1256 {
1257 max = ent->maxs[1];
1258 }
1259
1260 if ( max < ent->maxs[2] )
1261 {
1262 max = ent->maxs[2];
1263 }
1264
1265 ent->s.radius = max;
1266
1267 if (!ent->s.radius) // Still no radius?
1268 {
1269 ent->s.radius = 60;
1270 }
1271 }
1272
1273 // set the weaponmodel to -1 so we don't try and remove it in Pmove before we have it built
1274 ent->weaponModel = -1;
1275
1276 if ( ent->playerModel == -1 )
1277 {
1278 return qfalse;
1279 }
1280 return qtrue;
1281 }
1282
G_SetG2PlayerModel(gentity_t * const ent,const char * modelName,const char * customSkin,const char * surfOff,const char * surfOn)1283 void G_SetG2PlayerModel( gentity_t * const ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn )
1284 {
1285 char skinName[MAX_QPATH];
1286
1287 //ok, lets register the skin name, and then pass that name to the config strings so the client can get it too.
1288 if ( !customSkin )
1289 {//use the default
1290 Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", modelName );
1291 }
1292 else
1293 {
1294 Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", modelName, customSkin );
1295 }
1296 gi.RE_RegisterSkin( skinName );
1297 //now generate the ghoul2 model this client should be.
1298 //NOTE: for some reason, it still loads the default skin's tga's? Because they're referenced in the .glm?
1299 ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName),
1300 G_ModelIndex( va("models/players/%s/model.glm", modelName) ), G_SkinIndex( skinName ), NULL_HANDLE, 0, 0 );
1301 if (ent->playerModel == -1)
1302 {//try the stormtrooper as a default
1303 modelName = "stormtrooper";
1304 ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName),
1305 G_ModelIndex( va("models/players/%s/model.glm", modelName) ), NULL_HANDLE, NULL_HANDLE, 0, 0 );
1306 }
1307
1308 if ( !Q_stricmp( "kyle", modelName ))
1309 {
1310 // Try to get the skin we'll use when we switch to the first person light saber.
1311 // We use a new skin to disable certain surfaces so they are not drawn but we can still collide against them
1312 int skin = gi.RE_RegisterSkin( "models/players/kyle/model_fpls.skin" );
1313 if ( skin )
1314 {
1315 // put it in the config strings
1316 G_SkinIndex( skinName );
1317 }
1318 }
1319
1320 // did we find a ghoul2 model? if so, load the animation.cfg file
1321 if ( !G_SetG2PlayerModelInfo( ent, modelName, customSkin, surfOff, surfOn ) )
1322 {//couldn't set g2 info, fall back to a mouse md3
1323 NPC_ParseParms( "mouse", ent );
1324 //Com_Error( ERR_DROP, "couldn't load playerModel %s!\n", va("models/players/%s/model.glm", modelName) );
1325 Com_Printf( S_COLOR_RED"couldn't load playerModel %s!\n", va("models/players/%s/model.glm", modelName) );
1326 }
1327
1328 }
1329 /*
1330 Ghoul2 Insert End
1331 */
1332
G_ActivatePersonalShield(gentity_t * ent)1333 void G_ActivatePersonalShield( gentity_t *ent )
1334 {
1335 ent->client->ps.stats[STAT_ARMOR] = 100;//FIXME: define?
1336 ent->client->ps.powerups[PW_BATTLESUIT] = Q3_INFINITE;//Doesn't go away until armor does
1337 }
1338
1339 //HACK FOR FLYING
1340 extern void CG_ChangeWeapon( int num );
G_PilotXWing(gentity_t * ent)1341 void G_PilotXWing( gentity_t *ent )
1342 {
1343 if ( !CheatsOk( ent ) )
1344 {
1345 return;
1346 }
1347 if ( ent->client->ps.vehicleModel != 0 )
1348 {
1349 CG_ChangeWeapon( WP_SABER );
1350 ent->client->ps.vehicleModel = 0;
1351 ent->svFlags &= ~SVF_CUSTOM_GRAVITY;
1352 ent->client->ps.stats[STAT_ARMOR] = 0;//HACK
1353 //ent->mass = 10;
1354 //gi.cvar_set( "m_pitchOverride", "0" );
1355 //gi.cvar_set( "m_yawOverride", "0" );
1356 if ( ent->client->ps.weapon != WP_SABER )
1357 {
1358 gi.cvar_set( "cg_thirdperson", "0" );
1359 }
1360 cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG;
1361 cg.overrides.thirdPersonRange = 240;
1362 cg.overrides.active &= ~CG_OVERRIDE_FOV;
1363 cg.overrides.fov = 0;
1364 }
1365 else
1366 {
1367 ent->client->ps.vehicleModel = G_ModelIndex( "models/map_objects/ships/x_wing.md3" );
1368
1369 ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ATST_SIDE );
1370 ent->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = ammoData[weaponData[WP_ATST_SIDE].ammoIndex].max;
1371 gitem_t *item = FindItemForWeapon( WP_ATST_SIDE );
1372 RegisterItem( item ); //make sure the weapon is cached in case this runs at startup
1373 G_AddEvent( ent, EV_ITEM_PICKUP, (item - bg_itemlist) );
1374 CG_ChangeWeapon( WP_ATST_SIDE );
1375
1376 ent->client->ps.gravity = 0;
1377 ent->svFlags |= SVF_CUSTOM_GRAVITY;
1378 ent->client->ps.stats[STAT_ARMOR] = 200;//FIXME: define?
1379 //ent->mass = 300;
1380 ent->client->ps.speed = 0;
1381 //gi.cvar_set( "m_pitchOverride", "0.01" );//ignore inverse mouse look
1382 //gi.cvar_set( "m_yawOverride", "0.0075" );
1383 gi.cvar_set( "cg_thirdperson", "1" );
1384 cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_FOV);
1385 cg.overrides.thirdPersonRange = 240;
1386 cg.overrides.fov = 100;
1387 }
1388 }
1389 //HACK FOR FLYING
1390
1391 //HACK FOR ATST
G_DrivableATSTDie(gentity_t * self)1392 void G_DrivableATSTDie( gentity_t *self )
1393 {
1394 }
1395
G_DriveATST(gentity_t * ent,gentity_t * atst)1396 void G_DriveATST( gentity_t *ent, gentity_t *atst )
1397 {
1398 if ( ent->NPC_type && ent->client && (ent->client->NPC_class == CLASS_ATST) )
1399 {//already an atst, switch back
1400 //open hatch
1401 if ( ent->playerModel >= 0 )
1402 {
1403 gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->playerModel );
1404 }
1405 ent->NPC_type = "kyle";
1406 ent->client->NPC_class = CLASS_KYLE;
1407 ent->flags &= ~FL_SHIELDED;
1408 ent->client->ps.eFlags &= ~EF_IN_ATST;
1409 //size
1410 VectorCopy( playerMins, ent->mins );
1411 VectorCopy( playerMaxs, ent->maxs );
1412 ent->client->crouchheight = CROUCH_MAXS_2;
1413 ent->client->standheight = DEFAULT_MAXS_2;
1414 G_SetG2PlayerModel( ent, "kyle", NULL, NULL, NULL );
1415 //FIXME: reset/initialize their weapon
1416 ent->client->ps.stats[STAT_WEAPONS] &= ~(( 1 << WP_ATST_MAIN )|( 1 << WP_ATST_SIDE ));
1417 ent->client->ps.ammo[weaponData[WP_ATST_MAIN].ammoIndex] = 0;
1418 ent->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = 0;
1419 CG_ChangeWeapon( WP_BRYAR_PISTOL );
1420 //camera
1421 //if ( ent->client->ps.weapon != WP_SABER )
1422 {
1423 gi.cvar_set( "cg_thirdperson", "0" );
1424 }
1425 cg.overrides.active &= ~(CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_VOF|CG_OVERRIDE_3RD_PERSON_POF|CG_OVERRIDE_3RD_PERSON_APH);
1426 cg.overrides.thirdPersonRange = cg.overrides.thirdPersonVertOffset = cg.overrides.thirdPersonPitchOffset = 0;
1427 cg.overrides.thirdPersonAlpha = cg_thirdPersonAlpha.value;
1428 ent->client->ps.viewheight = ent->maxs[2] + STANDARD_VIEWHEIGHT_OFFSET;
1429 //ent->mass = 10;
1430 }
1431 else
1432 {//become an atst
1433 ent->NPC_type = "atst";
1434 ent->client->NPC_class = CLASS_ATST;
1435 ent->client->ps.eFlags |= EF_IN_ATST;
1436 ent->flags |= FL_SHIELDED;
1437 //size
1438 VectorSet( ent->mins, ATST_MINS0, ATST_MINS1, ATST_MINS2 );
1439 VectorSet( ent->maxs, ATST_MAXS0, ATST_MAXS1, ATST_MAXS2 );
1440 ent->client->crouchheight = ATST_MAXS2;
1441 ent->client->standheight = ATST_MAXS2;
1442 if ( ent->playerModel >= 0 )
1443 {
1444 gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->playerModel );
1445 ent->playerModel = -1;
1446 }
1447 if ( ent->weaponModel >= 0 )
1448 {
1449 gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel );
1450 ent->weaponModel = -1;
1451 }
1452 if ( !atst )
1453 {//no ent to copy from
1454 G_SetG2PlayerModel( ent, "atst", NULL, NULL, NULL );
1455 NPC_SetAnim( ent, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE );
1456 }
1457 else
1458 {
1459 gi.G2API_CopyGhoul2Instance( atst->ghoul2, ent->ghoul2, -1 );
1460 ent->playerModel = 0;
1461 G_SetG2PlayerModelInfo( ent, "atst", NULL, NULL, NULL );
1462 //turn off hatch underside
1463 gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "head_hatchcover_off", 0x00000002/*G2SURFACEFLAG_OFF*/ );
1464 G_Sound( ent, G_SoundIndex( "sound/chars/atst/atst_hatch_close" ));
1465 }
1466 ent->s.radius = 320;
1467 //weapon
1468 gitem_t *item = FindItemForWeapon( WP_ATST_MAIN ); //precache the weapon
1469 CG_RegisterItemSounds( (item-bg_itemlist) );
1470 CG_RegisterItemVisuals( (item-bg_itemlist) );
1471 item = FindItemForWeapon( WP_ATST_SIDE ); //precache the weapon
1472 CG_RegisterItemSounds( (item-bg_itemlist) );
1473 CG_RegisterItemVisuals( (item-bg_itemlist) );
1474 ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ATST_MAIN )|( 1 << WP_ATST_SIDE );
1475 ent->client->ps.ammo[weaponData[WP_ATST_MAIN].ammoIndex] = ammoData[weaponData[WP_ATST_MAIN].ammoIndex].max;
1476 ent->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = ammoData[weaponData[WP_ATST_SIDE].ammoIndex].max;
1477 CG_ChangeWeapon( WP_ATST_MAIN );
1478 //HACKHACKHACKTEMP
1479 item = FindItemForWeapon( WP_EMPLACED_GUN );
1480 CG_RegisterItemSounds( (item-bg_itemlist) );
1481 CG_RegisterItemVisuals( (item-bg_itemlist) );
1482 item = FindItemForWeapon( WP_ROCKET_LAUNCHER );
1483 CG_RegisterItemSounds( (item-bg_itemlist) );
1484 CG_RegisterItemVisuals( (item-bg_itemlist) );
1485 item = FindItemForWeapon( WP_BOWCASTER );
1486 CG_RegisterItemSounds( (item-bg_itemlist) );
1487 CG_RegisterItemVisuals( (item-bg_itemlist) );
1488 //HACKHACKHACKTEMP
1489 //FIXME: these get lost in load/save! Must use variables that are set every frame or saved/loaded
1490 //camera
1491 gi.cvar_set( "cg_thirdperson", "1" );
1492 cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG;
1493 cg.overrides.thirdPersonRange = 240;
1494 //cg.overrides.thirdPersonVertOffset = 100;
1495 //cg.overrides.thirdPersonPitchOffset = -30;
1496 //FIXME: this gets stomped in pmove?
1497 ent->client->ps.viewheight = 120;
1498 //FIXME: setting these broke things very badly...?
1499 //ent->client->standheight = 200;
1500 //ent->client->crouchheight = 200;
1501 //ent->mass = 300;
1502 //movement
1503 //ent->client->ps.speed = 0;//FIXME: override speed?
1504 //FIXME: slow turn turning/can't turn if not moving?
1505 }
1506 }
1507 //HACK FOR ATST
1508
1509 /*
1510 ===========
1511 ClientSpawn
1512
1513 Called every time a client is placed fresh in the world:
1514 after the first ClientBegin, and after each respawn
1515 Initializes all non-persistant parts of playerState
1516 ============
1517 */
1518
ClientSpawn(gentity_t * ent,SavedGameJustLoaded_e eSavedGameJustLoaded)1519 qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded )
1520 {
1521 int index;
1522 vec3_t spawn_origin, spawn_angles;
1523 gclient_t *client;
1524 int i;
1525 clientPersistant_t saved;
1526 clientSession_t savedSess;
1527 clientInfo_t savedCi;
1528 int persistant[MAX_PERSISTANT];
1529 usercmd_t ucmd;
1530 gentity_t *spawnPoint;
1531 qboolean beamInEffect = qfalse;
1532 extern qboolean g_qbLoadTransition;
1533
1534 index = ent - g_entities;
1535 client = ent->client;
1536
1537 if ( eSavedGameJustLoaded == eFULL && g_qbLoadTransition == qfalse )//qbFromSavedGame)
1538 {
1539 ent->client->pers.teamState.state = TEAM_ACTIVE;
1540
1541 // increment the spawncount so the client will detect the respawn
1542 client->ps.persistant[PERS_SPAWN_COUNT]++;
1543 client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
1544
1545 client->airOutTime = level.time + 12000;
1546
1547 for (i=0; i<3; i++)
1548 {
1549 ent->client->pers.cmd_angles[i] = 0.0f;
1550 }
1551
1552 SetClientViewAngle( ent, ent->client->ps.viewangles);//spawn_angles );
1553
1554 gi.linkentity (ent);
1555
1556 // run the presend to set anything else
1557 ClientEndFrame( ent );
1558
1559 // clear entity state values
1560 PlayerStateToEntityState( &client->ps, &ent->s );
1561
1562 if ( ent->client->NPC_class == CLASS_ATST )
1563 {
1564 G_LoadAnimFileSet( ent, "atst" );
1565 G_SetSkin( ent, "atst", NULL );
1566 }
1567 else
1568 {
1569 G_LoadAnimFileSet( ent, "kyle" );
1570 G_SetSkin( ent, "kyle", NULL );
1571 }
1572 }
1573 else
1574 {
1575 // find a spawn point
1576 // do it before setting health back up, so farthest
1577 // ranging doesn't count this client
1578 // don't spawn near existing origin if possible
1579 spawnPoint = SelectSpawnPoint ( ent->client->ps.origin,
1580 (team_t) ent->client->ps.persistant[PERS_TEAM], spawn_origin, spawn_angles);
1581
1582 ent->client->pers.teamState.state = TEAM_ACTIVE;
1583
1584 // clear everything but the persistant data
1585 saved = client->pers;
1586 savedSess = client->sess;
1587 for ( i = 0 ; i < MAX_PERSISTANT ; i++ )
1588 {
1589 persistant[i] = client->ps.persistant[i];
1590 }
1591 //Preserve clientInfo
1592 memcpy (&savedCi, &client->clientInfo, sizeof(clientInfo_t));
1593
1594 memset (client, 0, sizeof(*client));
1595
1596 memcpy (&client->clientInfo, &savedCi, sizeof(clientInfo_t));
1597
1598 client->pers = saved;
1599 client->sess = savedSess;
1600 for ( i = 0 ; i < MAX_PERSISTANT ; i++ )
1601 {
1602 client->ps.persistant[i] = persistant[i];
1603 }
1604
1605 // increment the spawncount so the client will detect the respawn
1606 client->ps.persistant[PERS_SPAWN_COUNT]++;
1607 client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
1608
1609 client->airOutTime = level.time + 12000;
1610
1611 // clear entity values
1612 client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
1613 ent->s.groundEntityNum = ENTITYNUM_NONE;
1614 ent->client = &level.clients[index];
1615 ent->mass = 10;
1616 ent->takedamage = qtrue;
1617 ent->inuse = qtrue;
1618 SetInUse(ent);
1619 ent->classname = "player";
1620 client->squadname = ent->targetname = ent->script_targetname = ent->NPC_type = "kyle";
1621 if ( ent->client->NPC_class == CLASS_NONE )
1622 {
1623 ent->client->NPC_class = CLASS_KYLE;
1624 }
1625 client->playerTeam = TEAM_PLAYER;
1626 client->enemyTeam = TEAM_ENEMY;
1627 ent->contents = CONTENTS_BODY;
1628 ent->clipmask = MASK_PLAYERSOLID;
1629 ent->e_DieFunc = dieF_player_die;
1630 ent->waterlevel = 0;
1631 ent->watertype = 0;
1632 client->ps.friction = 6;
1633 client->ps.gravity = g_gravity->value;
1634 ent->flags &= ~FL_NO_KNOCKBACK;
1635 client->renderInfo.lookTarget = ENTITYNUM_NONE;
1636 client->renderInfo.lookTargetClearTime = 0;
1637 client->renderInfo.lookMode = LM_ENT;
1638
1639 VectorCopy (playerMins, ent->mins);
1640 VectorCopy (playerMaxs, ent->maxs);
1641 client->crouchheight = CROUCH_MAXS_2;
1642 client->standheight = DEFAULT_MAXS_2;
1643
1644 client->ps.clientNum = index;
1645
1646 // give default weapons
1647 client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
1648 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL ); //these are precached in g_items, ClearRegisteredItems()
1649 client->ps.inventory[INV_ELECTROBINOCULARS] = 1;
1650
1651 // always give the bryar pistol, but we have to give EITHER the saber or the stun baton..never both
1652 if ( spawnPoint->spawnflags & 32 ) // STUN_BATON
1653 {
1654 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_STUN_BATON );
1655 }
1656 else
1657 { // give the saber AND the blaster because most test maps will not have the STUN BATON flag set
1658 client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //this is precached in SP_info_player_deathmatch
1659 }
1660
1661 for ( i = 0; i < AMMO_THERMAL; i++ ) // don't give ammo for explosives
1662 {
1663 client->ps.ammo[i] = ammoData[i].max;
1664 }
1665
1666 client->ps.saberColor = SABER_BLUE;
1667 client->ps.saberActive = qfalse;
1668 client->ps.saberLength = 0;
1669 //Initialize force powers
1670 WP_InitForcePowers( ent );
1671 //
1672
1673 ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];
1674 ent->client->dismemberProbHead = 0;
1675 ent->client->dismemberProbArms = 5;
1676 ent->client->dismemberProbHands = 20;
1677 ent->client->dismemberProbWaist = 0;
1678 ent->client->dismemberProbLegs = 0;
1679
1680 ent->client->ps.batteryCharge = 2500;
1681
1682 VectorCopy( spawn_origin, client->ps.origin );
1683 VectorCopy( spawn_origin, ent->currentOrigin );
1684
1685 // the respawned flag will be cleared after the attack and jump keys come up
1686 client->ps.pm_flags |= PMF_RESPAWNED;
1687
1688 SetClientViewAngle( ent, spawn_angles );
1689
1690 {
1691 G_KillBox( ent );
1692 gi.linkentity (ent);
1693 // force the base weapon up
1694 client->ps.weapon = WP_BRYAR_PISTOL;
1695 client->ps.weaponstate = WEAPON_READY;
1696 }
1697
1698 // don't allow full run speed for a bit
1699 client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
1700 client->ps.pm_time = 100;
1701
1702 client->respawnTime = level.time;
1703 client->inactivityTime = level.time + g_inactivity->integer * 1000;
1704 client->latched_buttons = 0;
1705
1706 // set default animations
1707 client->ps.torsoAnim = BOTH_STAND2;
1708 client->ps.legsAnim = BOTH_STAND2;
1709
1710 // restore some player data if this is a spawn point with KEEP_REV (spawnflags&1) set...
1711 //
1712 if ( eSavedGameJustLoaded == eAUTO ||
1713 (spawnPoint->spawnflags&1) || // KEEP_PREV
1714 g_qbLoadTransition == qtrue )
1715 {
1716 Player_RestoreFromPrevLevel(ent);
1717 }
1718
1719
1720 /*
1721 Ghoul2 Insert Start
1722 */
1723
1724 if (eSavedGameJustLoaded == eNO)
1725 {
1726 ent->weaponModel = -1;
1727 G_SetG2PlayerModel( ent, "kyle", NULL, NULL, NULL );
1728 }
1729 else
1730 {
1731 if ( ent->client->NPC_class == CLASS_ATST )
1732 {
1733 G_LoadAnimFileSet( ent, "atst" );
1734 G_SetSkin( ent, "atst", NULL );
1735 }
1736 else
1737 {
1738 G_LoadAnimFileSet( ent, "kyle" );
1739 G_SetSkin( ent, "kyle", NULL );
1740 }
1741 }
1742 /*
1743 Ghoul2 Insert End
1744 */
1745
1746 // run a client frame to drop exactly to the floor,
1747 // initialize animations and other things
1748 client->ps.commandTime = level.time - 100;
1749 ucmd = client->pers.lastCommand;
1750 ucmd.serverTime = level.time;
1751 VectorCopyM( client->pers.cmd_angles, ucmd.angles );
1752 ucmd.weapon = client->ps.weapon; // client think calls Pmove which sets the client->ps.weapon to ucmd.weapon, so ...
1753 ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
1754 ClientThink( ent-g_entities, &ucmd );
1755
1756 // run the presend to set anything else
1757 ClientEndFrame( ent );
1758
1759 // clear entity state values
1760 PlayerStateToEntityState( &client->ps, &ent->s );
1761
1762 //ICARUS include
1763 ICARUS_FreeEnt( ent ); //FIXME: This shouldn't need to be done...?
1764 ICARUS_InitEnt( ent );
1765
1766 if ( spawnPoint->spawnflags & 64 )
1767 {//player starts with absolutely no weapons
1768 ent->client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
1769 ent->client->ps.ammo[weaponData[WP_NONE].ammoIndex] = 32000; // checkme
1770 ent->client->ps.weapon = WP_NONE;
1771 ent->client->ps.weaponstate = WEAPON_READY;
1772 }
1773
1774 if ( ent->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_SABER ) )
1775 {//set up so has lightsaber
1776 WP_SaberInitBladeData( ent );
1777 if ( ent->weaponModel == -1 && ent->client->ps.weapon == WP_SABER )
1778 {
1779 G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saberModel );
1780 }
1781 }
1782 if ( ent->weaponModel == -1 && ent->client->ps.weapon != WP_NONE )
1783 {
1784 G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl );
1785 }
1786
1787 {
1788 // fire the targets of the spawn point
1789 G_UseTargets( spawnPoint, ent );
1790 //Designers needed them to fire off target2's as well... this is kind of messy
1791 G_UseTargets2( spawnPoint, ent, spawnPoint->target2 );
1792
1793 /*
1794 // select the highest weapon number available, after any
1795 // spawn given items have fired
1796 client->ps.weapon = 1;
1797 for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
1798 if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
1799 client->ps.weapon = i;
1800 break;
1801 }
1802 }*/
1803 }
1804 }
1805
1806 client->pers.enterTime = level.time;//needed mainly to stop the weapon switch to WP_NONE that happens on loads
1807 ent->max_health = client->ps.stats[STAT_MAX_HEALTH];
1808
1809 if ( eSavedGameJustLoaded == eNO )
1810 {//on map transitions, Ghoul2 frame gets reset to zero, restart our anim
1811 NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
1812 NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
1813 }
1814 return beamInEffect;
1815 }
1816
1817
1818 /*
1819 ===========
1820 ClientDisconnect
1821
1822 Called when a player drops from the server.
1823 Will not be called between levels.
1824 ============
1825 */
ClientDisconnect(int clientNum)1826 void ClientDisconnect( int clientNum ) {
1827 gentity_t *ent;
1828
1829 ent = g_entities + clientNum;
1830 if ( !ent->client ) {
1831 return;
1832 }
1833
1834 // send effect if they were completely connected
1835 /* if ( ent->client->pers.connected == CON_CONNECTED ) {
1836 // They don't get to take powerups with them!
1837 // Especially important for stuff like CTF flags
1838 TossClientItems ( ent );
1839 }
1840 */
1841 gi.unlinkentity (ent);
1842 ent->s.modelindex = 0;
1843 ent->inuse = qfalse;
1844 ClearInUse(ent);
1845 ent->classname = "disconnected";
1846 ent->client->pers.connected = CON_DISCONNECTED;
1847 ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE;
1848
1849 gi.SetConfigstring( CS_PLAYERS + clientNum, "");
1850
1851 }
1852
1853
1854